2019年9月30日月曜日

ラズパイ(IOT・2)

増税前のラズパイ用機器の爆買い、第2弾。

これらが、組み合わされば
面白そうな感じがしませんか?
リモートでコミュニケーションできる
何かを作ります!

ロジクール ウェブカメラ C310 ブラック HD 720P ウェブカム ストリーミング
Raspberry Pi 4 ケース冷却ファン付きヒートシンクラズベリーパイ4用
GeeekPiラズベリーパイ冷却ファン30x30x7mm DC 5VブラシレスCUP冷却ファンとラズベリーパイヒートシンク
Cable Matters Micro HDMI HDMI 変換アダプター (マイクロ HDMI HDMI 変換アダプタ)
LABISTS Raspberry Pi NoIR カメラモジュール V2 ソニーIMX219PQ CMOS画像センサ 8メガピクセル
ミヨシ MCO カテゴリ-5e対応 ストレ-ト-クロス変換アダプタ CAT-C5E
Samsung microSDカード32GB EVOPlus Class10 UHS-I対応
BitTradeOne ラズベリーパイ専用学習リモコン基板
HiLetgo 3個セット BH1750FVI BH1750 GY-302 3V-5V デジタル 光量センサモジュール
goot 吸取線幅2.5mm長1.5m CP2515
HiLetgo TCA9548A I2C IIC マルチプレクサ ブレークアウト ボード 8チャンネル 拡張ボード
BU-Bauty 世界最小USBマイク PC Mac用USBマイク
ELEGOO Arduino用170タイポイント Miniブレッドボード6PCS
Kuman 3.5インチ Raspberry Pi用ディスプレイ タッチパネル 保護ケースセット
MPU-6050 使用 3軸ジャイロスコープ・3軸加速度センサー モジュール
セット ブレッドボード・ジャンパーワイヤー ジャンパー線/ケーブル オス-オス/オス-メス/メス-メス
[GPG] PCA9685 16Channel 12bit PWM サーボドライバー Arduino等の電子工作用 (Droidfun型)
タミヤ 楽しい工作シリーズ No.227 カムプログラムロボット 工作セット 70227
などなど

作るものが見えてきました~。うふふ

https://www.filetalk.info/index.html

2019年9月27日金曜日

ラズパイ(カメラ・8)

ラズパイ カメラの第五回で、v1.3のカメラモジュールを試してみました。
今回は、ラズパイが公式で販売しているカメラモジュールの
LABISTS Raspberry Pi NoIR カメラモジュール V2を購入したので、
比較してみます。
ソニーIMX219PQ CMOS画像センサ 8メガピクセルを利用しているので、
だいぶ綺麗とれるはず??



おや、画像が赤い。
そうです、赤外線対応のカメラを買ってしまいました。(笑
よく暗視に使うあれです。
どおりで赤いはずです。
このカメラは、今後監視カメラでつかう用にしたいと思います。
NoIRって書いてますね(笑

https://www.filetalk.info/index.html

2019年9月26日木曜日

ラズパイ(カメラ・7)

カメラ回りは色々な応用が利きます。
たとえば、OpenCVなどを利用して物体認識や顔認識。
ディープラーニングを使った、識別。
動画転送や、複数人へのライブストリーミングなど。
どれも、人とコミュニケーションを行う上で
必要な機能だと言えます。
これから新しく出てくる技術を含め、
一つ一つ検証して、最適に活用できるものを
探してみます。

https://www.filetalk.info/index.html

2019年9月25日水曜日

ラズパイ(カメラ・6)

前回のPythonに、パラメータの変更で動画も
撮れるように改良してみます。
第一パラメータに撮影時間を設定できるようにして、
ここの数字が0の場合は、静止画を撮影し、
1以上の場合は、動画を撮影する秒数と
なるようにしてみます。

パラメータの取得は、sys.argv から
もらうことができるので、
これが設定されているかを確認し取得します。

if (__name__ == "__main__"):
    if (len(sys.argv) == 2): # 一つ目はパスが入るので2つあるか確認
        run(rec_time=int(sys.argv[1])) # 0から始まって2つ目を取得
    else:
        run()

def run(rec_time=0):
    with picamera.PiCamera() as camera:
        if (mode == 0):
            # 写真

        else:
            # 動画
全体は、こちら
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
写真撮影
Usage:
    python3 cam.py [rec_time = video rec time]
    
    インストール
    sudo apt-get install python3-picamera 
    
    mkdir /home/pi/pic
    chmod +x cam.py
"""

import time
import picamera
from datetime import datetime



#--------------------------------------------------------------------------
# 撮影開始
#--------------------------------------------------------------------------
def run(rec_time=0):
    with picamera.PiCamera() as camera:
        if (mode == 0):
            camera.resolution = (2592, 1944)
            #camera.vflip = True
            #camera.hflip = True
            #camera.start_preview()
            #time.sleep(3)
            tmsmp = datetime.now().strftime("%Y%m%d-%H%M%S")
            camera.capture('pic/pic' + tmsmp + '.jpg')
            #camera.stop_preview()
        
        else:
            camera.resolution=(640, 480)
            #camera.sharpness = 0
            #camera.contrast = 0
            #camera.brightness = 50
            #camera.saturation = 0
            #camera.ISO = 0
            #camera.video_stabilization = False
            #camera.exposure_compensation = 0
            #camera.exposure_mode = 'auto'
            #camera.meter_mode = 'average'
            #camera.awb_mode = 'auto'
            #camera.image_effect = 'none'
            #camera.color_effects = None
            #camera.rotation = 0
            #camera.hflip = False
            #camera.vflip = False
            #camera.crop = (0.0, 0.0, 1.0, 1.0)
            camera.start_recording('pic/video.h264') # 保存先
            sleep(rec_time)    # 撮影時間分をスリーブする
            camera.stop_recording()

if (__name__ == "__main__"):
    if (len(sys.argv) == 2):
        run(rec_time=int(sys.argv[1]))
    else:
        run()

https://www.filetalk.info/index.html

2019年9月24日火曜日

ラズパイ(カメラ・5)

以下の環境で、撮影関係を確認してみました。
Raspberry Pi 3 Model B
OSに、Raspbian Buster Lite(2019-07-10)
でラズパイのカメラモジュールのv1.3で確認しました。

だいぶ前、アマゾンで買った3000円ほどのラズパイ用のカメラでしたが、
2592×1944ピクセルで撮影でき、画質もきれいなものでした。

ただ、コマンドのraspistillや、Pythonでpicameraから
同様に撮影は行うことは成功したのですが、
どちらも、上下反転や左右反転のコマンドを行っても
反映されませんでした。
ファームやソフトのアップデートをしてもダメでした。
相性でしょうかね~
v2.1のカメラモジュールだとうまくいくかもしれないです。

v1.3のデフォルト設定の場合、どちらかというと青白い感じ
今後、v2.1と比べる予定です。

https://www.filetalk.info/index.html

2019年9月21日土曜日

ラズパイ(カメラ・4)

今回は、Pythonから写真の撮影を試してみます。
Pythonから実行できれば、ほかのプログラムとも
組み合わせて、応用が色々できるようになります。
import time
import picamera
from datetime import datetime

with picamera.PiCamera() as camera:
 camera.resolution = (2592, 1944)
 camera.vflip = True   # 上下反転
 camera.hflip = True   # 左右反転
 camera.start_preview()  # プレビューを表示
 time.sleep(3)    # 撮影までの待ち時間
 tmsmp = datetime.now().strftime("%Y%m%d-%H%M%S")
 camera.capture('pic/pic' + tmsmp + '.jpg')

まず、写真の撮れ具合から確認してみます。
https://www.filetalk.info/index.html

2019年9月20日金曜日

ラズパイ(カメラ・3)

動画の撮影も、前回静止画を撮影したのと同様に
コマンドから制御することができます。
ビデオ撮影(-t=撮影時間(ms)、-w 640 -h 480(解像度の指定))
 raspivid -o video-1.h264 -t 5000

コマンドの一覧は、raspistill で表示できます。

よく使うパラメータ
-b:    ビットレート。(10MBits/secは、-b 10000000)
-w:    イメージの幅
-h:    イメージの高さ
-o:    出力先パス
-sh:    画像のシャープネス(-100~100)
-co:    画像コントラスト(-100~100)
-br:    画像の明るさ(0~100)
-sa:    画像の彩度(-100~100)
-ISO:    キャプチャISOを設定
-vs:    ビデオスタビライザーを使用する
-ex:    露出モード(off, auto, night, nightpreview, backlight, spotlight,
                sports, snow, beach, verylong, fixedfps, antishake, fireworks)
-rot:    回転(0~359)
-hf:    左右反転
-vf:    上下反転
-l:    ストリーミングのポート設定
            raspivid -l -o tcp://0.0.0.0:3333
            raspivid -l -o tcp://192.168.1.1:3333



デフォルトだと青白い感じのものになりました。
便利なのが、ストリーミング配信が標準で利用可能になっていることですね。
ただし、接続は、1対1のみで片方がクローズすると終了してしまうようです。
ビデオプレイヤーのVLC用のモジュールもあるので、
こちらを試してみるといいかもしれないですね。

https://www.filetalk.info/index.html

2019年9月19日木曜日

ラズパイ(カメラ・2)

前回、カメラモジュールを接続し、撮影できる準備が完了しました。
今回は、カメラを使ってコマンドから静止画の撮影を行います。

撮影は、一行でとっても簡単。-o を付けて保存先を指定するだけです。
 raspistill -o default.jpg

撮影方向やサイズ・フォーマットなど調整が可能です。
コマンドの一覧は、raspistill で表示できます。

よく使うパラメータ
-q:    jpegの品質を(0~100)
-e:    出力ファイルの種類(jpg、bmp、gif、png)
-tl:    タイムラプス(-t:msごとに撮影、%d:フレーム番号(%04d=4桁の連番))
    raspistill -o img-%04d.jpg -tl 1000 -t 3000 -w 640 -h 480        // 1秒おきに3秒撮影
-w:    イメージの幅
-h:    イメージの高さ
-o:    出力先パス
-sh:    画像のシャープネス(-100~100)
-co:    画像コントラスト(-100~100)
-br:    画像の明るさ(0~100)
-sa:    画像の彩度(-100~100)
-ISO:    キャプチャISOを設定
-vs:    ビデオスタビライザーを使用する
-ex:    露出モード(off, auto, night, nightpreview, backlight, spotlight,
                sports, snow, beach, verylong, fixedfps, antishake, fireworks)
-rot:    回転(0~359)
-hf:    左右反転
-vf:    上下反転

細かい調整も簡単ですね


https://www.filetalk.info/index.html

2019年9月18日水曜日

ラズパイ(カメラ・1)

まず、Raspberry Pi を利用した最もポピュラーな
外部とのつながりを行うものとして、カメラからの
情報のやり取りを行えるようにしてみたいと思います。

カメラの画像から物体や人、顔などの認識を
行うために基本部分から押さえていきます。

ラズパイとカメラの接続の場合、本体についている
専用ケーブルを用いる方法と、USBのWEBカメラを使う方法が
簡単ですが、せっかくなので場所を取らないシンプルな接続方法の
専用ケーブルを用いる方法を利用してみます。

専用ケーブルの方も、フラットケーブルを刺す場所と向きを
間違えなければ、そのまま使えると思います。

あとは、設定からカメラを有効にします。
 sudo raspi-config

インターフェース オプションを選択
カメラを選んで
有効にします

コマンドから以下を実行してカメラの状態を確認
両方1なら成功です
 vcgencmd get_camera

次回は、コマンドで写真を撮影~

https://www.filetalk.info/index.html

2019年9月17日火曜日

ラズパイ(IOT・1)

ラズパイ用のセンサーを増税前に爆買いしたので、
それぞれを組み合わせ面白いものを作っていきたいと思います。
第一段の商品が到着しました。
 ラズベリーパイの良いところである、さまざまなセンサーを
プログラミングして利用することが簡単に行えるので面白いですね。
目標としては、ライブ配信しつつ、AIしながら移動可能な
小型インターフェースロボット的な
ものを目指してみます。


https://www.filetalk.info/index.html

2019年9月14日土曜日

ラズパイ(Webサーバー・14)

前回は、設定画面をいつでも表示できるよう自動起動の為、
サービスへ登録を行いました。
今回は、管理画面を決まったアドレスにして、他のパソコン等から
固定にアクセスできるようにホスト名の設定を行います。

通常、LANやWIFIに接続すると、ルーターのDHCPの機能で
動的にIPアドレスが割り振られるかと思います。

192.168.0.xxx

普通ならばここから先は、クライアント側からのアクセスだけ
だとIPを知る必要はないと思いますが、
サーバーのような場合ですと、
先方にアクセスするために、アドレスを知っている必要が出ます。

192.168.0.10 のページを表示するなど

また、IPアドレスは、設置する環境により何番になるかは、
設置先の担当者でないと分からないので、
接続を設定する人が管理画面を表示しにくい状況となってしまいます。

そこで、固定的なアドレスとしてホスト名を設定することで
解決するようにします。

設定画面の表示
$ sudo raspi-config





ホスト名の設定

使用できる文字の説明

ホスト名を設定

 とすることで、testというアドレスでアクセスできるようになります。


https://www.filetalk.info/index.html

2019年9月13日金曜日

ラズパイ(Webサーバー・13)

前回までで、WIFI設定をWebサーバーから調整する仕組みができました。
今回は、できあがったWebページを自動起動させて、
いつでも設定が行えるように登録を行います。

ラズパイのラズビアンの環境において、
プログラムを自動起動する手段は、
いくつかあります。

■cron
  時間を決めてプログラムを毎回動かすような処理に向いています。

■rc.local
  ラズパイが起動時に、設定された処理を実行する。

■systemd
  プログラムをサービスとして登録し、起動・停止をできるようにする。

状況に応じて使い分ける必要があるのですが、
バックでずっと動かせる今回のようなものは、rc.local か systemd の
2択になります。
ただ、rc.local の場合ですと、別の同じようなpythonプログラムを
起動させて、さらにもう一つ動かす必要がある場合などは、
一つ目しか動かないので注意が必要です。
(※一つ目のプログラムで、専用してしまい。次のが動かないようです。)

ということで、systemd が今後も考えると最善となります。

サービスの設定は、
/etc/systemd/system/ に下記ファイル作成を作成して登録します。

サービスの名前のファイルを作成します。
wifi_menu.service というファイルを作成

            [Unit]
            Description = menu3 daemon
            
            [Service]
            ExecStart=/usr/bin/python3 /usr/local/bin/wifi_menu.py
            Restart = always
            Type = simple
            
            [Install]
            WantedBy = multi-user.target


あとは、自動起動するようにコマンドから登録を行います。

        sudo systemctl enable wifi_menu.service


■サービスの設定
  デーモン開始
    sudo systemctl enable wifi_menu.service
  デーモン停止
    sudo systemctl disable wifi_menu.service
  サービス開始
    sudo systemctl start wifi_menu.service
  サービス終了
    sudo systemctl stop wifi_menu.service
  状態の確認
    sudo systemctl status wifi_menu.service

https://www.filetalk.info/index.html

2019年9月12日木曜日

ラズパイ(Webサーバー・12)

PythonのWebサーバーを使った簡易WIFI設定。
これまでのまとめ

ソースの配置
 wifi_menu.py
 contents
  └ index.html
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
シンプル HTTP サーバー(WIFI設定用)Python3版
Usage:
    ./wifi_menu.py []
"""

import os
import sys
import http.server as s
import json
import logging
import configparser
import hashlib
import base64

PORT = 80
PAGE_ID = ''
PAGE_PASSWORD = 'xxxxxxxx'
PAGE_KEY = ''  # BASIC認証用
# WIFIの設定ファイルの場所
WIFI_CONF_PATH = "/etc/wpa_supplicant/wpa_supplicant.conf"
# WIFIの作業用ファイルの場所
WIFI_TMP_PATH = "../wpa.conf"
CONF_WIFI_SEC_NM = "wificonnect"



#--------------------------------------------------------------------------
# HTTPリクエスト対応処理
#--------------------------------------------------------------------------
class MyHandler(s.BaseHTTPRequestHandler):
    def do_AUTHHEAD(self):
        self.send_response(401)
        self.send_header("WWW-Authenticate", "Basic realm=\"Please enter your user information\"")
        self.send_header("Content-type", "text/html")
        self.end_headers()
        return
    
    def do_GET(self):
        global PAGE_KEY
        chk_pass = False
        if (self.headers.get("Authorization") == None):
            self.do_AUTHHEAD()
            self.wfile.write('no auth user received'.encode("utf-8"))
            pass
        
        elif self.headers.get("Authorization") == "Basic " + PAGE_KEY:
            chk_pass = True
        
        else:
            self.do_AUTHHEAD()
            self.wfile.write((self.headers.get("Authorization")).encode("utf-8"))
            self.wfile.write('not authenticated'.encode("utf-8"))
            pass
        
        if (self.path == "/"):
            self.path = "/index.html"
        
        if (chk_pass == True):
            try:
                send_reply = False
                if (self.path.endswith(".html")):
                    mimetype = "text/html"
                    send_reply = True
                if (self.path.endswith(".jpg")):
                    mimetype = "image/jpg"
                    send_reply = True
                if (self.path.endswith(".gif")):
                    mimetype = "image/gif"
                    send_reply = True
                if (self.path.endswith(".js")):
                    mimetype = "application/javascript"
                    send_reply = True
                if (self.path.endswith(".css")):
                    mimetype = "text/css"
                    send_reply = True
                
                if (send_reply == True):
                    cur_dir = os.getcwd()           # カレントフォルダ取得
                    logging.info(cur_dir)
                    tar_file = cur_dir + self.path
                    if (os.path.isfile(tar_file)):
                        f = open(tar_file, encoding='utf-8')
                        txt = f.read()
                        f.close()
                        self.send_response(200)
                        self.send_header("Content-type", mimetype)
                        self.end_headers()
                        logging.info("GET request,\nPath: %s\nHeaders:\n%s\n", str(self.path), str(self.headers))
                        #self.wfile.write("GET request for {}" . format(self.path).encode("utf-8"))
                        
                        self.wfile.write(txt.encode("utf-8"))
                return
            
            except IOError:
                self.send_error(404, 'File Not Found: %s' % self.path)
    
    def do_POST(self):
        global WIFI_CONF_PATH, WIFI_TMP_PATH, CONF_WIFI_SEC_NM
        # リクエスト取得
        content_len  = int(self.headers.get("content-length"))
        body = json.loads(self.rfile.read(content_len).decode("utf-8"))
        
        logging.info("post")
        
        # レスポンス処理
        act = body["act"]
        if (act == "write"):
            # wifi書き込み
            wifi_ssid = str(body["wifi_ssid"])
            wifi_psk = str(body["wifi_psk"])
            wifi_scan_ssid = str(body["wifi_scan_ssid"])
            wifi_key_mgmt = str(body["wifi_key_mgmt"])
            
            # 作業設定を読み込む
            config = configparser.ConfigParser()
            config.read(WIFI_TMP_PATH)
            
            r_wifi_ssid = ""
            if (config.has_option(CONF_WIFI_SEC_NM, "wifi_ssid")):
                r_wifi_ssid = config.get(CONF_WIFI_SEC_NM, "wifi_ssid")
            r_wifi_psk = ""
            if (config.has_option(CONF_WIFI_SEC_NM, "wifi_psk")):
                r_wifi_psk = config.get(CONF_WIFI_SEC_NM, "wifi_psk")
            r_wifi_scan_ssid = ""
            if (config.has_option(CONF_WIFI_SEC_NM, "wifi_scan_ssid")):
                r_wifi_scan_ssid = config.get(CONF_WIFI_SEC_NM, "wifi_scan_ssid")
            r_wifi_key_mgmt = ""
            if (config.has_option(CONF_WIFI_SEC_NM, "wifi_key_mgmt")):
                r_wifi_key_mgmt = config.get(CONF_WIFI_SEC_NM, "wifi_key_mgmt")
            
            bef_wifi_cnf = 'network={\n' + \
                '  ssid="' + r_wifi_ssid + '"\n' + \
                '  psk="' + r_wifi_psk + '"\n'
            if (r_wifi_scan_ssid != ''):
                bef_wifi_cnf += '  scan_ssid=1\n'
            if (r_wifi_scan_ssid != ''):
                bef_wifi_cnf += '  key_mgmt=' + r_wifi_key_mgmt + '\n'
            bef_wifi_cnf += '}\n'
            
            if (os.path.exists(WIFI_CONF_PATH)):
                f = open(WIFI_CONF_PATH, 'w+', encoding='utf-8')
                contents = f.read()
                
                logging.info(contents)
                
                contents = contents.replace(bef_wifi_cnf, "") # 前回の設定をクリアする
                f.write(contents)
                f.close()
            
            if (True):
                f = open(WIFI_CONF_PATH, 'a', encoding='utf-8')
                f.write('network={\n')
                f.write('  ssid="' + wifi_ssid + '"\n')
                f.write('  psk="' + wifi_psk + '"\n')
                # ステルス時、書き出す
                if (wifi_scan_ssid != ''):
                    f.write('  scan_ssid=1\n')
                # WPA時は、書き出さない
                if (wifi_key_mgmt != ''):
                    f.write('  key_mgmt=' + wifi_key_mgmt + '\n')
                f.write('}\n')
                f.close()
                
                # 作業設定の書き出し
                if (not config.has_section(CONF_WIFI_SEC_NM)):
                    config.add_section(CONF_WIFI_SEC_NM)
                
                config.set(CONF_WIFI_SEC_NM, "wifi_ssid", wifi_ssid)
                config.set(CONF_WIFI_SEC_NM, "wifi_psk", wifi_psk)
                config.set(CONF_WIFI_SEC_NM, "wifi_scan_ssid", wifi_scan_ssid)
                config.set(CONF_WIFI_SEC_NM, "wifi_key_mgmt", wifi_key_mgmt)
                
                f = open(WIFI_TMP_PATH, "w+", encoding='utf-8')
                config.write(f)
                f.close()
            
            ret = {
                "ret": "1"
            }
            
        else:
            # 読込
            config = configparser.ConfigParser()
            config.read(WIFI_TMP_PATH)
            
            r_wifi_ssid = ""
            if (config.has_option(CONF_WIFI_SEC_NM, "wifi_ssid")):
                r_wifi_ssid = config.get(CONF_WIFI_SEC_NM, "wifi_ssid")
            r_wifi_psk = ""
            if (config.has_option(CONF_WIFI_SEC_NM, "wifi_psk")):
                r_wifi_psk = config.get(CONF_WIFI_SEC_NM, "wifi_psk")
            r_wifi_scan_ssid = ""
            if (config.has_option(CONF_WIFI_SEC_NM, "wifi_scan_ssid")):
                r_wifi_scan_ssid = config.get(CONF_WIFI_SEC_NM, "wifi_scan_ssid")
            r_wifi_key_mgmt = ""
            if (config.has_option(CONF_WIFI_SEC_NM, "wifi_key_mgmt")):
                r_wifi_key_mgmt = config.get(CONF_WIFI_SEC_NM, "wifi_key_mgmt")
            
            ret = {
                "wifi_ssid": r_wifi_ssid,
                "wifi_psk": r_wifi_psk,
                "wifi_scan_ssid": r_wifi_scan_ssid,
                "wifi_key_mgmt": r_wifi_key_mgmt,
                
                "ret": "1"
            }
        
        self.send_response(200)
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Content-type", "application/json;charset=utf-8")
        self.end_headers()
        ret_json = json.dumps(ret, sort_keys=False, indent=4, ensure_ascii=False)
        self.wfile.write(ret_json.encode("utf-8"))

#--------------------------------------------------------------------------
# サーバ起動
#--------------------------------------------------------------------------
def run(server_class=s.HTTPServer, handler_class=MyHandler, port=PORT):
    global PAGE_ID, PAGE_PASSWORD, PAGE_KEY
    logging.basicConfig(level=logging.INFO)
    
    # BASIC認証用のキーを作成
    secret = '{}:{}'.format(PAGE_ID, PAGE_PASSWORD)
    encodedBytes = base64.b64encode(secret.encode("utf-8"))
    PAGE_KEY = str(encodedBytes, "utf-8")
    #logging.info('page_key: ' + PAGE_KEY)
    
    server_address = ("", port)     # IPを指定した場合、IP外からアクセスが不能
    httpd = server_class(server_address, handler_class)
    logging.info("Starting httpd...\n")
    try:
        print("Starting server, use  to stop")
        httpd.serve_forever()
    except KeyboardInterrupt:
        pass
    httpd.server_close()
    logging.info("Stopping httpd...\n")

if (__name__ == "__main__"):
    os.chdir(os.path.join(os.path.dirname(__file__), 'contents')) # カレントフォルダ設定
    if (len(sys.argv) == 2):
        run(port=int(sys.argv[1]))
    else:
        run()


wifi_menu.py

index.html
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Configuration</title>
<style type="text/css">
.tbl_main {
 margin: 20px; padding: 10px; background: #eee; border-radius: 10px;
}
.tbl_main, TD, TH {
 padding: 0px 5px 0px 5px;
}
.lrg_ttl {
 font-size: 22px; font-weight: bold;
}
.sml_ttl {
 font-size: 11px;
}
.sub_ttl {
 font-size: 17px; font-weight: bold;
}
.fld_ttl {
 padding: 0px 5px 0px 15px; font-size: 15px;
}
.input_l {
 width: 400px;
}
.set_button {
 padding: 5px; margin: 15px 0px 10px 0px; width: 100%; font-size: 20px; font-weight: bold;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
(window.onload = function() {
  axios.post('/', {
    "act": "get"
  })
  .then(function (response) {
    var result = document.getElementById('result1');
    document.getElementById('wifi_ssid').value = response.data['wifi_ssid'];
    document.getElementById('wifi_psk').value = response.data['wifi_psk'];
    var wifi_scan_ssid = response.data['wifi_scan_ssid'];
    if (wifi_scan_ssid == "1") document.getElementById('wifi_scan_ssid').checked = true;
    document.getElementById('wifi_key_mgmt').value = response.data['wifi_key_mgmt'];
    
    result.innerHTML = response.data['ret'];
  })
  .catch(function (error) {
    console.log(error)
  });
})();

function setWrite() {
  if (window.confirm("Are you sure you want to update it?")) {
    var wifi_scan_ssid = "";
    if (document.getElementById('wifi_scan_ssid').checked) wifi_scan_ssid = "1";
    axios.post('/', {
      "act": "write",
      "wifi_ssid": document.getElementById('wifi_ssid').value,
      "wifi_psk": document.getElementById('wifi_psk').value,
      "wifi_scan_ssid": wifi_scan_ssid,
      "wifi_key_mgmt": document.getElementById('wifi_key_mgmt').value
    })
    .then(function (response) {
      var result = document.getElementById('result2');
      result.innerHTML = response.data['ret'];
      alert("Finished");
    })
    .catch(function (error) {
      console.log(error)
    });
  }
}
</script>
</head>

<body>
  <center>
    <table class="tbl_main">
      <tr>
        <td colspan="2" align="center">
          <div style="padding: 10px 0px 0px 0px;"><span class="lrg_ttl">Configuration Menu</span></div>
          <div style="margin: -5px 0px 0px 0px;"><span class="sml_ttl">©2019 Sherpa CO., LTD. ver:1.1a</span></div>
        </td>
      </tr>
      
      <tr>
        <td colspan="2">
          <hr />
          <span class="sub_ttl">Wifi Connect Section</span>
        </td>
      </tr>
      <tr>
        <td class="fld_ttl">SSID</td>
        <td>
          <input type="text" class="input_l" name="wifi_ssid" id="wifi_ssid">
        </td>
      </tr>
      <tr>
        <td class="fld_ttl">Password</td>
        <td>
          <input type="text" class="input_l" name="wifi_psk" id="wifi_psk">
        </td>
      </tr>
      <tr>
        <td class="fld_ttl">Scan SSID</td>
        <td>
          <input type="checkbox" name="wifi_scan_ssid" id="wifi_scan_ssid" value="1">
        </td>
      </tr>
      <tr>
        <td class="fld_ttl">Key Mgmt</td>
        <td>
          <input type="text" class="input_l" name="wifi_key_mgmt" id="wifi_key_mgmt"><br />
          <span style="font-size: 11px;">WPA( set empty ) - WEP( WPA-PSK, WPA-EAP, NONE )</style>
        </td>
      </tr>
      
      <tr>
        <td colspan="2">
          <input type="button" class="set_button" value="Write" onclick="javascript:setWrite()">
        </td>
      </tr>
      
      <tr>
        <td colspan="2" align="center">
          <table>
            <tr>
              <td class="fld_ttl">Return Value</td>
              <td>
                <div id="result2"></div>
              </td>
            </tr>
          </table>
        </td>
      </tr>
    </table>
  </center>
</body>
</html>

設定をWrite後、再起動すれば適用されます。
https://www.filetalk.info/index.html

2019年9月11日水曜日

ラズパイ(Webサーバー・11)

前回は、ページの日本語対応の調整を行いました。
今回は、ここまで作成してきたサンプルをもとに
WIFIの設定をブラウザからできるようにしてみます。
CUIベースのラズベリーパイにおいて、WIFIの設定は
前回記録した情報の表示ができなかったり、
設定したWIFIは保存毎に追加されるので、
間違えた場合でも、設定がのこってどんどん増えていきます。
こんな感じで、お世辞にも簡単とは言えません。

WIFIの設定ファイルの場所ですが、
/etc/wpa_supplicant/wpa_supplicant.conf

に接続設定が書かれています。
そこで、このファイルを読み書きすれば、WIFIの設定ができそうです。
また、ほかのネットワーク設定も存在する場合、
ファイル内をクリアして上書きすると、既存の情報が
消えてしまうので、前回の変えた情報を控えておいて、
該当する箇所を書き換えるように作ります。

表示時(読み込み表示)
            # WIFIの設定ファイルの場所
            WIFI_CONF_PATH = "/etc/wpa_supplicant/wpa_supplicant.conf"
            # WIFIの作業用ファイルの場所
            WIFI_TMP_PATH = "../wpa.conf"
            
            config.read(WIFI_TMP_PATH)
            
            r_wifi_ssid = ""
            if (config.has_option(CONF_WIFI_SEC_NM, "wifi_ssid")):
                r_wifi_ssid = config.get(CONF_WIFI_SEC_NM, "wifi_ssid")
            r_wifi_psk = ""
            if (config.has_option(CONF_WIFI_SEC_NM, "wifi_psk")):
                r_wifi_psk = config.get(CONF_WIFI_SEC_NM, "wifi_psk")
            r_wifi_scan_ssid = ""
            if (config.has_option(CONF_WIFI_SEC_NM, "wifi_scan_ssid")):
                r_wifi_scan_ssid = config.get(CONF_WIFI_SEC_NM, "wifi_scan_ssid")
            r_wifi_key_mgmt = ""
            if (config.has_option(CONF_WIFI_SEC_NM, "wifi_key_mgmt")):
                r_wifi_key_mgmt = config.get(CONF_WIFI_SEC_NM, "wifi_key_mgmt")
            
            ret = {
                "url": r_url,
                "urlwav": r_urlwav,
                "urlpcap": r_urlpcap,
                "iot_id": r_iot_id,
                "voicerec_on": r_voicerec_on,
                
                "smbconnect_on": r_smbconnect_on,
                "smbusername": r_smbusername,
                "smbpassword": r_smbpassword,
                "smbhostname": r_smbhostname,
                "smbipaddress": r_smbipaddress,
                "smbremotedirectory": r_smbremotedirectory,
                "smbremotepath": r_smbremotepath,
                
                "wifi_ssid": r_wifi_ssid,
                "wifi_psk": r_wifi_psk,
                "wifi_scan_ssid": r_wifi_scan_ssid,
                "wifi_key_mgmt": r_wifi_key_mgmt,
                
                "ret": "1"
            }
        
        self.send_response(200)
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Content-type", "application/json;charset=utf-8")
        self.end_headers()
        ret_json = json.dumps(ret, sort_keys=False, indent=4, ensure_ascii=False)
        self.wfile.write(ret_json.encode("utf-8"))


書き込み時(保存時)
            wifi_ssid = str(body["wifi_ssid"])
            wifi_psk = str(body["wifi_psk"])
            wifi_scan_ssid = str(body["wifi_scan_ssid"])
            wifi_key_mgmt = str(body["wifi_key_mgmt"])
            
            # 作業設定を読み込む
            config = configparser.ConfigParser()
            config.read(WIFI_TMP_PATH)
            
            r_wifi_ssid = ""
            if (config.has_option(CONF_WIFI_SEC_NM, "wifi_ssid")):
                r_wifi_ssid = config.get(CONF_WIFI_SEC_NM, "wifi_ssid")
            r_wifi_psk = ""
            if (config.has_option(CONF_WIFI_SEC_NM, "wifi_psk")):
                r_wifi_psk = config.get(CONF_WIFI_SEC_NM, "wifi_psk")
            r_wifi_scan_ssid = ""
            if (config.has_option(CONF_WIFI_SEC_NM, "wifi_scan_ssid")):
                r_wifi_scan_ssid = config.get(CONF_WIFI_SEC_NM, "wifi_scan_ssid")
            r_wifi_key_mgmt = ""
            if (config.has_option(CONF_WIFI_SEC_NM, "wifi_key_mgmt")):
                r_wifi_key_mgmt = config.get(CONF_WIFI_SEC_NM, "wifi_key_mgmt")
            
            bef_wifi_cnf = 'network={\n' + \
                '  ssid="' + r_wifi_ssid + '"\n' + \
                '  psk="' + r_wifi_psk + '"\n'
            if (r_wifi_scan_ssid != ''):
                bef_wifi_cnf += '  scan_ssid=1\n'
            if (r_wifi_scan_ssid != ''):
                bef_wifi_cnf += '  key_mgmt=' + r_wifi_key_mgmt + '\n'
            bef_wifi_cnf += '}\n'
            
            if (os.path.exists(WIFI_CONF_PATH)):
                f = open(WIFI_CONF_PATH, 'w+', encoding='utf-8')
                contents = f.read()
                
                logging.info(contents)
                
                contents = contents.replace(bef_wifi_cnf, "") # 前回の設定をクリアする
                f.write(contents)
                f.close()
            
            if (True):
                f = open(WIFI_CONF_PATH, 'a', encoding='utf-8')
                f.write('network={\n')
                f.write('  ssid="' + wifi_ssid + '"\n')
                f.write('  psk="' + wifi_psk + '"\n')
                # ステルス時、書き出す
                if (wifi_scan_ssid != ''):
                    f.write('  scan_ssid=1\n')
                # WPA時は、書き出さない
                if (wifi_key_mgmt != ''):
                    f.write('  key_mgmt=' + wifi_key_mgmt + '\n')
                f.write('}\n')
                f.close()
                
                # 作業設定の書き出し
                if (not config.has_section(CONF_WIFI_SEC_NM)):
                    config.add_section(CONF_WIFI_SEC_NM)
                
                config.set(CONF_WIFI_SEC_NM, "wifi_ssid", wifi_ssid)
                config.set(CONF_WIFI_SEC_NM, "wifi_psk", wifi_psk)
                config.set(CONF_WIFI_SEC_NM, "wifi_scan_ssid", wifi_scan_ssid)
                config.set(CONF_WIFI_SEC_NM, "wifi_key_mgmt", wifi_key_mgmt)
                
                f = open(WIFI_TMP_PATH, "w+", encoding='utf-8')
                config.write(f)
                f.close()
            
            ret = {
                "ret": "1"
            }
https://www.filetalk.info/index.html
 

2019年9月10日火曜日

ラズパイ(Webサーバー・10)

前回は、パスワード設定を変更できるように調整しました。
今回は、ページの日本語化対応についてです。
Webページとして表示したい部分をPythonのファイルだけ利用して
作成する場合ですと、短いページなら問題ないのですが、
ページ切り替えや、多くの外部ファイルと連携しないと
いけない場合など、ソースのコードが増えて
管理が大変になってしまいます。
そこで、表示するHTML部分を別のファイルとして作成して
読み込むようにします。
cur_dir = os.getcwd() # カレントフォルダ取得
tar_file = cur_dir + self.path
if (os.path.isfile(tar_file)):
    f = open(tar_file)
    txt = f.read()
    f.close()
    self.send_response(200)
    self.send_header("Content-type", mimetype)
    self.end_headers()
    self.wfile.write(txt.encode("utf-8"))

通常は、これでOKなのですが、
日本語の文字列が混ざるとエラーが出て表示されなくなってしまう
場合があります。

そこで、読み込む際のファイルのエンコードを以下のように
調整することで、エラーが出なくなります。

    f = open(tar_file, encoding='utf-8')

エンコードを追加しないとエラーがでるという
Webページを配信時のファイル読み込みに
見落としがちな文字コードについてでした。

https://www.filetalk.info/index.html


2019年9月9日月曜日

ラズパイ(Webサーバー・9)

前回は、Webページを守るためBASIC認証の機能を取り入れました。
今回は、このBASIC認証で利用するパスワードを作成する
処理を作りこみます。

認証に使われるパスワードは、内容をみただけでは
分からないようにするため、パスワードからハッシュ関数で作られた
文字列が利用されます。
Pythonからですと、base64を使ったものを利用します。

import base64

id = ""
password = "xxxxxx"
secret = '{}:{}'.format(id, password)
encodedBytes = base64.b64encode(secret.encode("utf-8"))
PAGE_KEY = str(encodedBytes, "utf-8")
logging.info(PAGE_KEY)


IDは、なしでも登録可能です。
作成するとこのような文字列が生成されます。


Onh4eHh4eA==

これを、Authorization 部分に利用することで設定できます。



https://www.filetalk.info/index.html

2019年9月6日金曜日

ラズパイ(Webサーバー・8)

前回は、Python で設定ファイルの読み書きが簡単にできる
方法を行いました。
今回は、誰からでも設定を変更されないように、
認証機能をつけてみます。
まず仕様としては、機器の設定にローカルネットワークからの
接続のみを想定してますので、比較的に単純なBASIC認証を
設定してみます。

そこで、これまで server_class クラスのハンドラに認証機能を追加します。
PAGE_KEY = 'Onxxxxxxx=='

class MyHandler(s.BaseHTTPRequestHandler):
    def do_AUTHHEAD(self):
        self.send_response(401)
        self.send_header("WWW-Authenticate", "Basic realm=\"Please enter your user information\"")
        self.send_header("Content-type", "text/html")
        self.end_headers()
        return
        
    def do_GET(self):
        global PAGE_KEY
        chk_pass = False
        if (self.headers.get("Authorization") == None):
            self.do_AUTHHEAD()
            self.wfile.write('no auth user received'.encode("utf-8"))
            pass
        
        elif self.headers.get("Authorization") == "Basic " + PAGE_KEY:
            chk_pass = True
        
        else:
            self.do_AUTHHEAD()
            self.wfile.write((self.headers.get("Authorization")).encode("utf-8"))
            self.wfile.write('not authenticated'.encode("utf-8"))
            pass


パスワードを入力しないとページが表示されないようにすることができます。




次回は、パスワードの生成を考えてみます。
https://www.filetalk.info/index.html

2019年9月5日木曜日

ラズパイ(Webサーバー・7)

前回までは、http.server のサーバー部分・クライアント部分の処理を
作りやり取りができるようになりました。
今回は、設定ファイルの作成についてです。
設定ファイルの作成って、書き込めることはもちろんですが、
簡単に必要とする項目を、取り出せなければいけないですよね。
また、項目はカテゴリーごとに分けて人が見てもわかりやすい
ものでないと後から大変になります。
ファイルの途中から読んだり書いたりができることが重要ですね。

で、今回つかうのが、configparser です。
これを使うことで、問題が解決されます。
import configparser

CONF_PATH = "../sherpacti.conf"

# 読込み
config = configparser.ConfigParser()
config.read(CONF_PATH)

r_url = config.get(CONF_NRM_SEC_NM, "url")
r_urlwav = config.get(CONF_NRM_SEC_NM, "urlwav")

# 書込み
if (not config.has_section(CONF_WIFI_SEC_NM)):
    config.add_section(CONF_WIFI_SEC_NM)  # セクションがない場合作成

config.set(CONF_WIFI_SEC_NM, "wifi_ssid", wifi_ssid)
config.set(CONF_WIFI_SEC_NM, "wifi_psk", wifi_psk)

f = open(WIFI_TMP_PATH, "w+")
config.write(f)
f.close()


とすると次のようなファイルに書き込まれます
[sherpacti]
url = http://ws3.ipad-fan.info/send
urlwav = http://sherpa.5dx.org/tel2/upd_wave.php


https://www.filetalk.info/index.html
簡単に見やすい設定ファイルが作られますね
次回は、設定ファイルをだれからでもいじられないようにするために
パスワードで保護できるようにしたいと思います。

2019年9月4日水曜日

ラズパイ(Webサーバー・6)

前回までで、http.server のサーバー部分・クライアント部分の処理を
構築し、やり取りができるようになりました。
今回は、クライアントに表示するベースの部分となる
HTMLの内容を書いてみたいと思います。

管理画面として、設定ファイルを作成するページにしたいので、
項目と内容をいれるようなものにします。

<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>SherpaCTI Configuration</title>
<style type="text/css">
.tbl_main {
 margin: 20px; padding: 10px; background: #eee; border-radius: 10px;
}
.tbl_main, TD, TH {
 padding: 0px 5px 0px 5px;
}
.lrg_ttl {
 font-size: 22px; font-weight: bold;
}
.sml_ttl {
 font-size: 11px;
}
.sub_ttl {
 font-size: 17px; font-weight: bold;
}
.fld_ttl {
 padding: 0px 5px 0px 15px; font-size: 15px;
}
.input_l {
 width: 400px;
}
.set_button {
 padding: 5px; margin: 15px 0px 10px 0px; width: 100%; font-size: 20px; font-weight: bold;
}
</style>
<script src="axios.min.js"></script>
<script>
(window.onload = function() {
  axios.post('/', {
    "act": "get"
  })
  .then(function (response) {
    var result = document.getElementById('result1');
    document.getElementById('smbconnect_on').value = response.data['smbconnect_on'];
    document.getElementById('smb_username').value = response.data['smbusername'];
    document.getElementById('smb_password').value = response.data['smbpassword'];
    document.getElementById('smb_hostname').value = response.data['smbhostname'];
    document.getElementById('smb_ipaddress').value = response.data['smbipaddress'];
    document.getElementById('smb_remotedirectory').value = response.data['smbremotedirectory'];
    document.getElementById('smb_remotepath').value = response.data['smbremotepath'];
    
    result.innerHTML = response.data['ret'];
  })
  .catch(function (error) {
    console.log(error)
  });
})();

function setVal() {
  if (window.confirm("Are you sure you want to update it?")) {
    axios.post('/', {
      "act": "set",
      "smbconnect_on": document.getElementById('smbconnect_on').value,
      "smbusername": document.getElementById('smb_username').value,
      "smbpassword": document.getElementById('smb_password').value,
      "smbhostname": document.getElementById('smb_hostname').value,
      "smbipaddress": document.getElementById('smb_ipaddress').value,
      "smbremotedirectory": document.getElementById('smb_remotedirectory').value,
      "smbremotepath": document.getElementById('smb_remotepath').value
    })
    .then(function (response) {
      var result = document.getElementById('result1');
      result.innerHTML = response.data['ret'];
      alert("Finished");
    })
    .catch(function (error) {
      console.log(error)
    });
  }
}
</script>
</head>

<body>
  <center>
    <table class="tbl_main">
      <tr>
        <td colspan="2" align="center">
          <div style="padding: 10px 0px 0px 0px;"><span class="lrg_ttl">SherpaCTI Configuration Menu</span></div>
          <div style="margin: -5px 0px 0px 0px;"><span class="sml_ttl">©2019 Sherpa CO., LTD. ver:1.1a</span></div>
        </td>
      </tr>
      
      <tr>
        <td colspan="2">
          <hr />
          <span class="sub_ttl">SMB Connect Section</span>
        </td>
      </tr>
      <tr>
        <td class="fld_ttl">SMB Connect</td>
        <td>
          <input type="text" class="input_l" name="smbconnect_on" id="smbconnect_on">
        </td>
      </tr>
      <tr>
        <td class="fld_ttl">User Name</td>
        <td>
          <input type="text" class="input_l" name="smb_username" id="smb_username">
        </td>
      </tr>
      <tr>
        <td class="fld_ttl">Password</td>
        <td>
          <input type="text" class="input_l" name="smb_password" id="smb_password">
        </td>
      </tr>
      <tr>
        <td class="fld_ttl">Host Name</td>
        <td>
          <input type="text" class="input_l" name="smb_hostname" id="smb_hostname">
        </td>
      </tr>
      <tr>
        <td class="fld_ttl">IP Address</td>
        <td>
          <input type="text" class="input_l" name="smb_ipaddress" id="smb_ipaddress">
        </td>
      </tr>
      <tr>
        <td class="fld_ttl">Remote Directory</td>
        <td>
          <input type="text" class="input_l" name="smb_remotedirectory" id="smb_remotedirectory">
        </td>
      </tr>
      <tr>
        <td class="fld_ttl">Remote Path</td>
        <td>
          <input type="text" class="input_l" name="smb_remotepath" id="smb_remotepath">
        </td>
      </tr>
      
      <tr>
        <td colspan="2">
          <input type="button" class="set_button" value="Set Value" onclick="javascript:setVal()">
        </td>
      </tr>
      
      <tr>
        <td colspan="2" align="center">
          <table>
            <tr>
              <td class="fld_ttl">Return Value</td>
              <td>
                <div id="result1"></div>
              </td>
            </tr>
          </table>
        </td>
      </tr>
      
    </table>
  </center>
</body>
</html>


Python には、設定ファイルを簡単に読み書きできる configparser という
ライブラリがあるので、こちらを利用します。
次回は、このライブラリを使って、設定の読み書きを行います。

https://www.filetalk.info/index.html

2019年9月2日月曜日

ラズパイ(Webサーバー・5)

前回は、POSTの情報もとれるようになりました。
今回はこのJSONをつかって、やり取りできるようにAjax部分を
組み込んでいきます。
Ajaxを扱うものとしましては、jQuery が有名ですが、
Python の http.server と相性がいい axios を利用してみました。

サーバーへデータをPOSTする部分

  function setWrite() {
  if (window.confirm("Are you sure you want to update it?")) {
    var wifi_scan_ssid = "";
    if (document.getElementById('wifi_scan_ssid').checked) wifi_scan_ssid = "1";
    axios.post('/', {
      "act": "write",
      "wifi_ssid": document.getElementById('wifi_ssid').value,
      "wifi_psk": document.getElementById('wifi_psk').value,
      "wifi_scan_ssid": wifi_scan_ssid,
      "wifi_key_mgmt": document.getElementById('wifi_key_mgmt').value
    })
    .then(function (response) {
      var result = document.getElementById('result2');
      result.innerHTML = response.data['ret'];
      alert("Finished");
    })
    .catch(function (error) {
      console.log(error)
    });
  }
}


サーバーとのやり取りでのXHRの部分で、簡単に実装することができますね。
IE8以上から利用できたりと、古いブラウザのサポートもあったり、
リクエストのタイムアウトも指定できるので重宝しそうです。

https://www.filetalk.info/index.html