はじめに
この記事では, Raspberry Pi
を使って, 簡単なアクセスポイントとWebサーバーを構築していきます.
hostapd
・dnsmasq
・dhcpcd
を使用してRaspberry Pi
をアクセスポイント化し, Flask
アプリケーションを自動起動する方法を解説します.
また, 今回はRaspberry Pi OS Liteを使用し, ディスプレイやキーボードを接続せず, すべての作業をSSHを使ってリモートで行います.
使用機材
- Raspberry Pi 4-B (OS: Lite 64bit)
- Mac Book Air
- LANケーブル
環境構築
環境構築をしていきます.
準備
今回は, Mac
を使ってRaspberry Pi
に有線接続してSSH
しているので, 不要な方はこの工程を省いて大丈夫です.
まず, MacとRaspberry Pi
を有線LANケーブルで接続します.
システム設定 -> ネットワークから該当するインターフェースを選び, 以下のように静的IPアドレスを指定します.
SSHをします.
ssh pi@raspberrypi.local
これでモニターとキーボードを使わずに, 作業することが出来ます.
必要なパッケージのインストール
基本的に/home/pi
上で作業します.
以下のコマンドを実行して必要なパッケージをインストールします.
sudo apt-get update
sudo apt-get -y install hostapd dnsmasq dhcpcd5
sudo apt-get -y install python3-pip
vimをインストールしますが, エディタはお好みのものをお使いください.
vimを使わない方は, これ以降vimをお使いのエディタに読み替えてください.
sudo apt-get -y install vim
Pythonの仮想環境構築とFlaskのインストール
仮想環境はvenvを使用していきます.
python -m venv .venv
以下で仮想環境有効化.
source /home/pi/.venv/bin/activate
Flaskをインストールします.
pip install Flask
NetworkManagerの無効化
アクセスポイント化したときに競合を避けるため, NetworkManager
を無効化します.
sudo systemctl disable NetworkManager
Pythonスクリプト
今回は, Flaskアプリケーションのコードはapp.py
に書きます.
また, アクセスポイントを立ち上げ, app.py
を呼び出すコードはmain.py
に書いていきます.
app.py (Flaskアプリケーション)
ここでは, Flaskアプリケーションを最小構成にします.
h1タグのHello, World!
を返します.
main.pyでこのファイルをimport
するので, app.run()
をstart
関数内に書きます.
host="0.0.0.0"
は, Flaskが任意のIPアドレスでリクエストを受け付けます. ポートは, Webアプリケーションのテストに使われる8080
を使用しました.
使用するポートの競合に注意してください.
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "<h1>Hello, World!</h1>"
def start():
app.run(host="0.0.0.0", port=8080)
main.py (アクセスポイント)
subprocess
というコマンドをimport
します.
また, 先ほど作ったapp.py
も同じくimport
します.
ssid
の値は, RaspberryPiAccessPoint
にしました. また, パスワードは1234ABCD
としました. これらは任意のもので大丈夫です.
ap_configでは, Wi-Fiインターフェースとしてwlan0
を指定しています.
アクセスポイント用に使用するLinuxのWi-Fiドライバnl80211
を指定, hw_mode=a
で5GHz帯をWi-Fiの動作モードとして指定, Wi-Fiのチャンネルは, 5GHzの36チャンネルを指定しています.
import subprocess
import app
def start_access_point():
ssid = "RaspberryPiAccessPoint"
password = "1234ABCD"
ap_config = f"""interface=wlan0
driver=nl80211
ssid={ssid}
hw_mode=a
channel=36
ieee80211n=1
ieee80211ac=1
wmm_enabled=0
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_passphrase={password}
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP
"""
先程の設定をhostapd.conf
に書き込んでいきます.
3つ目のwith open
の箇所では, hostapd
が使用する設定ファイルを指定しています.
def start_access_point():
(省略)
with open("/etc/hostapd/hostapd.conf", "w") as f:
f.write(ap_config)
hostapd_default = "/etc/default/hostapd"
with open(hostapd_default, "r") as f:
content = f.read()
with open(hostapd_default, "w") as f:
content = content.replace("#DAEMON_CONF=\"\"", f'DAEMON_CONF="/etc/hostapd/hostapd.conf"')
f.write(content)
dhcpcd(DHCP Client Daemon)は, ネットワークインターフェースにIPアドレスを割り当てる役割があります.
今回は, Mac
を使ってRaspberry Pi
に有線接続してSSH
しているので, 不要な方はeth0
のコードは省いて大丈夫です.
eth0
とwlan0
に静的IPアドレスを設定し, Raspberry Pi
をアクセスポイントとして動作させる準備を行っていきます.
eth0
にはMac側から手動でIPアドレスを192.168.1.1
に指定したのでそれに合わせています. サブネットマスクは/24
です. デフォルトゲートウェイとして192.168.1.1
を指定し, DNSサーバーを192.168.1.1
に指定しています.
wlan0
には, 192.168.0.50
を静的アドレスとして割り当ててます(サブネットマスクは/24
). nohook wpa_supplicant
は, WPA Supplicant
を無効化しています.
def start_access_point():
(省略)
eth0_config = """\ninterface eth0
static ip_address=192.168.1.1/24
static routers=192.168.1.1
static domain_name_servers=192.168.1.1
"""
wlan0_config = """\ninterface wlan0
static ip_address=192.168.0.50/24
nohook wpa_supplicant
"""
with open("/etc/dhcpcd.conf", "w") as f:
f.write(eth0_config)
f.write(wlan0_config)
dnsmasq
は, DNSサーバーの役割と自動的にIPアドレスを割り当てる役割があります.
192.168.0.50
をDNSリクエストのlisten-address
として指定します.
server=8.8.8.8
はGoogleのパブリックDNSを上流のDNSサーバーとして指定します.
dhcp-range=192.168.0.50,192.168.0.70,12h
では, DHCPで割り当てるIPアドレスの範囲を指定します.
192.168.0.50
から192.168.0.70
の間で, リース期間は12時間です.
def start_access_point():
(省略)
dnsmasq_config = """
interface=wlan0
listen-address=192.168.0.50
server=8.8.8.8
domain-needed
bogus-priv
dhcp-range=192.168.0.50,192.168.0.70,12h
"""
with open("/etc/dnsmasq.conf", "w") as f:
f.write(dnsmasq_config)
hostapd
, dnsmasq
, dhcpcd
を起動していきます.
最後に, main
でアクセスポイントの関数を呼び出した後, Flaskアプリケーションを起動しています.
def start_access_point():
(省略)
subprocess.call(["sudo", "systemctl", "unmask", "hostapd"])
subprocess.call(["sudo", "systemctl", "unmask", "dhcpcd"])
subprocess.call(["sudo", "systemctl", "enable", "hostapd"])
subprocess.call(["sudo", "systemctl", "enable", "dhcpcd"])
subprocess.call(["sudo", "systemctl", "start", "hostapd"])
subprocess.call(["sudo", "systemctl", "enable", "dnsmasq"])
subprocess.call(["sudo", "systemctl", "restart", "dhcpcd"])
subprocess.call(["sudo", "systemctl", "restart", "dnsmasq"])
subprocess.call(["sudo", "systemctl", "restart", "hostapd"])
if __name__ == "__main__":
start_access_point()
app.start()
これで, アクセスポイントを立てる -> Flaskアプリケーションを起動するというところまで完成しました.
全コードはGitHubにあげています.
自動起動設定(systemd)
systemdを使って, 自動起動させていきます.
以下のようにserviceファイルを作ります.
sudo vim /etc/systemd/system/web_server.service
以下の内容を記入していきます.
ポイントは.venvのPythonを起動しているところです.
[Unit]
Description=start access point and start web server
After=network.target
[Service]
ExecStart=/home/pi/.venv/bin/python /home/pi/main.py
WorkingDirectory=/home/pi
[Install]
WantedBy=multi-user.target
最後に, 自動起動を有効化します.
sudo systemctl enable web_server
動かしてみる
それでは動かしてみましょう.
自動起動するか確かめたいので, 再起動します.
sudo reboot
そして, http://192.168.0.50:8080 に入り, 以下のような画面になれば成功です!
おわりに
今回は, あるプロジェクトの中でこの記事の内容を実装する機会があったので, 復習と整理のためにこの記事を書いてみました.
Raspberry Piやネットワークについて理解が深まりました.
参考文献