RaspberryPi4を無線LANのアクセスポイントにしてRaspberryPiPicoWを接続する

RasberryPi4を主体としてRaspberryPiPicoWに実装したセンサの値を読み取ったりモーターや電磁弁の制御をできるようにしたい。最終的にはおうちで育ててる花の自動水やりシステムとかにしたい。
![画像1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271465/3095f45b-41a4-5f7b-d4e2-92949d338317.png)
全体のイメージはこんな感じ(PicoWのUSBポートからWiFiが飛んでるような絵になってしまった)
アクセスポイントにするRaspberryPi4はGUI操作のままできるのでVNCも使えて初心者にはうれしい。
ネットワーク機器を自作するとセキュリティの問題までついてきて素人の手に余るのでアクセスポイントはブリッジせずに独立させた状態で使う。さらにPicoWのMACアドレスに固定IPを割り当てて登録済みのMAC以外は接続できない設定にすればとりあえずは大丈夫なんじゃないかなと思う。外部のネットワークと接するRaspberryPi4のセキュリティには気を付ける(とはいっても公式でサポートしてるVNCをそのまま使うだけなんだけど)。

まずはRaspberryPi4をアクセスポイント化する。
公式にチュートリアルがあるのでこれをそのまま適用した
https://www.raspberrypi.com/documentation/computers/configuration.html#setting-up-a-routed-wireless-access-point

必要なソフトウェアをインストール
```console
sudo apt install hostapd
sudo systemctl unmask hostapd
sudo systemctl enable hostapd
sudo apt install dnsmasq
sudo DEBIAN_FRONTEND=noninteractive apt install -y netfilter-persistent iptables-persistent
```
ネットワークルーターを設定
```console
sudo vim /etc/dhcpcd.conf
```
```vim
interface wlan0
    static ip_address=192.168.4.1/24
    nohook wpa_supplicant
```
"Enable Routing and IP Masquerading"の項目は省略する。


DHCPとDNSサーバーを設定
```console
sudo mv /etc/dnsmasq.conf /etc/dnsmasq.conf.orig
sudo nano /etc/dnsmasq.conf
```
```vim
interface=wlan0 # Listening interface
dhcp-range=192.168.4.2,192.168.4.20,255.255.255.0,24h
                # Pool of IP addresses served via DHCP
domain=wlan     # Local wireless DNS domain
address=/gw.wlan/192.168.4.1
                # Alias for this router
```
公式のチュートリアルでは192.168.4.2から192.168.4.20をプールする設定となっていたのでそのまま使う

WiFiの有効化
```console
sudo rfkill unblock wlan
```
APの設定
```console
sudo vim /etc/hostapd/hostapd.conf
```
```vim
country_code=JP
interface=wlan0
ssid=NameOfNetwork
hw_mode=g
channel=7
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_passphrase=AardvarkBadgerHedgehog
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP
```

セキュリティのためSSIDとパスフレーズはちゃんと設定しよう。
hw_modeの設定については
a = IEEE 802.11a (5 GHz) (Raspberry Pi 3B+ 以降)
b = IEEE 802.11b (2.4 GHz)
g = IEEE 802.11g (2.4 GHz)
となっているおりPicoWを接続するので```hw_mode=g```とした

今回はこのアクセスポイントにPicoWを接続して使うのでPicoWのMACアドレスに対して
静的IPを割り当てる設定と登録済みMAC以外の端末の接続を拒否する設定を追記
```console
sudo vim /etc/dnsmasq.conf
```
設定ファイルを開いて
```vim
### Set static IP ###
dhcp-host=AA:BB:CC:DD:EE:FF,192.168.4.20
### Ignore unknown MAC ###
dhcp-ignore=tag:!known
```
以上を追記する。

PicoWのMACアドレスについてはPicoWの公式ドキュメント"3.7. The MAC address"に記述があり
```python
import network
import ubinascii

wlan = network.WLAN(network.STA_IF)
wlan.active(True)
mac = ubinascii.hexlify(network.WLAN().config('mac'),':').decode()
print(mac)

# Other things you can query
print(wlan.config('channel'))
print(wlan.config('essid'))
print(wlan.config('txpower'))
```
というコードを実行すれば取得できる。
MACアドレスの書き換えができるのかどうかは分からなかった。
設定ができたら再起動する。アクセスポイントが起動しない場合ばraspi-configからWiFiの設定をJPになおすとたぶん動く。

ネットワークが上手く動いてるかどうかのテストは公式のPcoWのチュートリアルにある
https://projects.raspberrypi.org/en/projects/get-started-pico-w/
PicoWでwebwerverを起動するコードで試した
```python
import network
import socket
from time import sleep
from picozero import pico_temp_sensor, pico_led
import machine

ssid = 'NameOfNetwork'
password = 'AardvarkBadgerHedgehog'

def connect():
    #Connect to WLAN
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    wlan.connect(ssid, password)
    while wlan.isconnected() == False:
        print( "Waiting for connection" )
        sleep(5)
    ip = wlan.ifconfig()[0]
    print(f'Connected on {ip}')
    return ip

def open_socket(ip):
    # Open a socket
    address = (ip, 80)
    connection = socket.socket()
    connection.bind(address)
    connection.listen(1)
    return connection

def webpage(temperature, state):
    #Template HTML
    html = f"""
            <!DOCTYPE html>
            <html>
            <form action="./lighton">
            <input type="submit" value="Light on" />
            </form>
            <form action="./lightoff">
            <input type="submit" value="Light off" />
            </form>
            <p>LED is {state}</p>
            <p>Temperature is {temperature}</p>
            </body>
            </html>
            """
    return str(html)

def serve(connection):
    #Start a web server
    state = 'OFF'
    pico_led.off()
    temperature = 0
    while True:
        client = connection.accept()[0]
        request = client.recv(1024)
        request = str(request)
        try:
            request = request.split()[1]
        except IndexError:
            pass
        if request == '/lighton?':
            pico_led.on()
            state = 'ON'
        elif request =='/lightoff?':
            pico_led.off()
            state = 'OFF'
        temperature = pico_temp_sensor.temp
        html = webpage(temperature, state)
        client.send(html)
        client.close()


try:
    ip = connect()
    connection = open_socket(ip)
    serve(connection)
except KeyboardInterrupt:
    machine.reset()
```
アクセスポイントとしているRaspberryPi4本体かアクセスポイントに接続している端末から、PicoWがネットワークに接続したときにprintされるIPにアクセスするとPicoWに組み込みのLEDのオンオフとか温度センサーの値が表示されるのでそれができたら成功。