Raspberry PIをWifiアクセスポイントにしてWifiネットワーク設定をできるようにした
タイトルが変なので何言ってるんだコイツ状態になるけど。
時々見かける、最初はデバイス自身がWifiのアクセスポイントになって、PCなどから接続して実際に使うネットワーク設定を行う...というやつをRaspberry PIでやってみるという話。
作ったものはこれ。
環境準備
まずはhostpadをインストールする。
$ sudo apt-get update $ sudo apt-get install -y hostapd
hostapd.serviceはmaskされているのでインストール時点では起動に失敗する。
設定ファイルをexampleからコピーしてきて/etc/hostapd/hostapd.conf
を編集する。
$ sudo cp /usr/share/doc/hostapd/examples/hostapd.conf /etc/hostapd/hostapd.conf
とりあえず、デフォルトから変更した項目は次の通り。
ssid=RasPi-AP ieee80211n=1 ht_capab=[HT40][SHORT-GI-20][DSSS_CCK-40] auth_algs=1 wpa=2 wpa_passphrase=raspberry wpa_key_mgmt=WPA-PSK rsn_pairwise=CCMP
続いて、dhcpサーバをインストールする。
$ sudo apt-get install -y isc-dhcp-server
こちらもインストール時点では起動に失敗する。
/etc/dhcp/dhcpd.conf
を編集して末尾に次の設定を追加する。
subnet 192.168.100.0 netmask 255.255.255.0 { range 192.168.100.100 192.168.100.105; option routers 192.168.100.1; option broadcast-address 192.168.100.255; default-lease-time 600; max-lease-time 7200; }
/etc/default/isc-dhcp-server
を編集してwlan0
をインターフェイスとして有効にする。
INTERFACESv4="wlan0"
isc-dhcp-serverは自動起動せずに使うので自動起動を無効化しておく。
$ sudo update-rc.d isc-dhcp-server disable
とりあえず動作確認のために簡易起動スクリプトをざっと書いて実行してみる。
#!/bin/sh sudo hostapd /etc/hostapd/hostapd.conf & sudo ip addr flush dev wlan0 sudo ip addr replace 192.168.100.1/24 dev wlan0 sudo /etc/init.d/isc-dhcp-server start
スクリプトを実行したら、wlan0
に192.168.100.1
が割り当てられるので、PCなどからアクセスポイントに接続してIPアドレスを貰えればOK。
ブラウザからWifi設定するためのWebサーバを用意
アクセスポイントに接続してPC側がクライアントになることはできるようになったので、アクセスポイントのIPアドレスにブラウザでアクセスしてWifi設定を行えるようにWebサーバを作る。
とりあえず前に作ったtornado使ったソースを流用してざっくり作成。
import os import subprocess import tornado.ioloop import tornado.web class SystemControl(object): def read_wpa_supplicant_conf(self, config_path): with open(config_path) as f: return f.read() def write_wpa_supplicant_conf(self, config_path, data): subprocess.check_call('sudo sh -c "echo -n \'%s\' > %s"' % (data, config_path) , shell=True) def auto_start_off(self): subprocess.run('sudo sed -i --follow-symlinks s/AUTO_START=ON/AUTO_START=OFF/g /etc/default/pi-wifi-admin', shell=True) def reboot(self): subprocess.run('sudo reboot', shell=True) class MainHandler(tornado.web.RequestHandler): def initialize(self, sys_ctrl, wpa_supplicant_conf_path): self._sys_ctrl = sys_ctrl self._wpa_supplicant_conf_path = wpa_supplicant_conf_path def get(self): html = """<html> <head><title>Wifi Setting</title></head> <body> <form method='POST' action='/'> <textarea name='config' style='width:800px;height:600px;'>%s</textarea> <input type='submit' value='Save' /> </form> </body> </html> """ conf = self._sys_ctrl.read_wpa_supplicant_conf(self._wpa_supplicant_conf_path) self.write(html % conf) self.set_status(200) def post(self): conf = self.get_body_argument('config') self._sys_ctrl.write_wpa_supplicant_conf(self._wpa_supplicant_conf_path, conf.replace('\r', '').replace('"', '\\"')) self._sys_ctrl.auto_start_off() self._sys_ctrl.reboot() self.redirect('/', permanent=True) def make_app(sys_ctrl, wpa_supplicant_conf_path): return tornado.web.Application([ (r'/', MainHandler, { 'sys_ctrl': sys_ctrl, 'wpa_supplicant_conf_path': wpa_supplicant_conf_path }) ]) if __name__ == '__main__': try: port = int(os.getenv('PORT', '8080')) sys_ctrl = SystemControl() app = make_app(sys_ctrl, '/etc/wpa_supplicant/wpa_supplicant.conf') app.listen(port, '0.0.0.0') tornado.ioloop.IOLoop.instance().start() except KeyboardInterrupt: pass
やってることは/
にアクセスがあったらwpa_supplicant.conf
の中身を書きだしたtextareaのあるフォームを表示する。フォームのPOSTでwpasupplicant.conf
を上書きして設定アプリケーションの自動起動停止、OSを再起動するだけ。ちゃんとAPI作る気力が今回はなかったので超ざっくりに。
これを/usr/local/lib/pi-wifi-admin
ディレクトリを作って設置し、venvでtornadoを入れて使えるようにしておく。
アクセスポイント化とWebサーバの起動をまとめてサービス化
環境準備で用意したhostapdやisc-dhcp-serverの起動と設定用Webサーバを起動するスクリプトを次の通り用意して/usr/local/sbin/pi-wifi-admin
として設置する。
毎回起動されても困るので、環境変数のAUTO_START
を見て完全に起動を行うかを判定している。
#!/bin/bash AUTO_START=${AUTO_START:-OFF} WIFI_IFNAME=${WIFI_IFNAME:-wlan0} IP_ADDR=${IP_ADDR:-192.168.100.1/24} APP_HOME_DIR=/usr/local/lib/pi-wifi-admin if [ "$AUTO_START" == "OFF" ]; then echo "auto start disable" exit 0 fi systemctl stop wpa_supplicant systemctl stop dhcpcd hostapd -B -P /tmp/hostapd.pid /etc/hostapd/hostapd.conf ip addr flush dev $WIFI_IFNAME ip addr replace $IP_ADDR dev $WIFI_IFNAME systemctl start isc-dhcp-server $APP_HOME_DIR/venv/bin/python $APP_HOME_DIR/app.py &
使用する環境変数は/etc/default/pi-wifi-admin
に設定として置いておく。
AUTO_START=ON WIFI_IFNAME=wlan0 IP_ADDR=192.168.100.1/24
これらをsystemd用に次の/etc/systemd/wifi-pi-admin.service
として設定を用意してサービス化する。
[Unit] Description = PI Wifi Admin After=network.target [Service] ExecStart = /usr/local/sbin/pi-wifi-admin EnvironmentFile = /etc/default/pi-wifi-admin Type = oneshot RemainAfterExit = yes [Install] WantedBy = multi-user.target
あとはサービスを有効化して終わり。
$ sudo systemctl enable pi-wifi-admin.service
使い方
PCでRasPI-AP
にWifi接続して、ブラウザでhttp://192.168.100.1:8080を開く。
wpa_supplicant.conf
の設定フォーマットで設定してSaveを押す。
レスポンスは返る前にOSが再起動してしまうのでタイムアウトすると思う。
課題
スマホでWifi接続したら、外部に通信送れないせいか「このWifi駄目だ」と判断されて自動で切り替わってしまった。PCでももしかしたらそういうことあるかも。
ファイル直接触るのやめてちゃんとしたAPIで設定できるようにしたい。
設定保存した後にOSを再起動せずに変更を反映できるようにしたい。