特定APの起動をトリガーに、ラズパイの無線接続先を自動切替

特定APの起動をトリガーに、ラズパイの無線接続先を自動切替
Page content

近いAPへ自動接続してほしい

自宅の中でRaspberry Pi Zero WH (以下ラズパイZero) の稼働を開始している。無線接続は次のような状態にしていて、

  1. 自宅LANの中で2つの無線アクセスポイントAP-MainとAP-Subを運用していて、どちらのAPも共通のSSID MY_HOUSE_AP で運用している
  2. 24時間起動のラズパイZeroを、SSID MY_HOUSE_AP に無線接続している
  3. AP-SubはラズパイZeroに近接しているが、省エネのために、部屋内の人間が必要なときだけAP-Subの電源をON/OFFしている
  4. AP-Subが起動していない時は、多少の通信ロスは許容するので、ラズパイZeroは遠くのAP-Mainに自動的に接続していてほしい
  5. AP-Subが起動している時は、通信ロスを極力抑えるため、ラズパイZeroはすばやく自動的に、無線接続をAP-Mainから近接のAP-Subへ切り替えてほしい

箇条書きした各要素を、AP別に表にまとめるとこうなる。

AP-MainAP-Sub
SSIDMY_HOUSE_APMY_HOUSE_AP
稼働時間24時間連続不定期に電源ON/OFF
ラズパイZeroとの距離遠い近接
接続優先度の希望LowHigh

上記の状態のうち、4はAP-Subへ接続できないことからフォールバック的に自動的に行われるのだが、5は、ラズパイZeroがAP-Mainに接続を維持できている間は起こらないように思う。そこで5を実現するため、AP-Subの起動を検出したらラズパイZeroの無線を再接続させる仕掛けを作ってみた。

ラズパイZeroなどのIoTデバイスを無線接続で利用する上で、圏内の無線アクセスポイントを全台かならずしも常時電源ONにはしていない、という場合のひと工夫として、ご紹介です。

無線を再接続させるスクリプト

内容・ロジック

仕掛けは、ラズパイZeroで実行するbashスクリプトの形で、reconnect_wlan.shと名付けた。ソースコードは最後に掲載。Raspberry Pi OS (Raspbian GNU/Linux 10 (buster)) で動作確認を行っている。

スクリプトのロジックは次のとおり。無線の再接続の後は自動的に、電波強度の強い (= 近い) ほうの無線アクセスポイントにラズパイZeroが繋がることを暗に期待している。

  • AP-Mainには接続しているはずのラズパイZeroから、AP-Sub (固定IPアドレスを想定) へのping疎通を確認できたら、AP-Subが起動していると見なす
  • さらに現時点の接続先APがAP-Subではない場合に、wpa_cliのreconfigureを実行して、無線の再接続を行わせる

使い方

スクリプトreconnect_wlan.shchmod +xした後、root権限で定期的にcron実行しておく。

*/1 * * * * timeout 55s /home/foo/reconnect_wlan.sh >/dev/null 2>&1

スクリプトが想定通りに動作し、AP-Subの起動を検知して無線の再接続が行われる際には、/var/log/syslogに次のようなログが記録される。ここでは、「D8:07:B6:XX:XX:XX」はAP-Subの、「6C:E4:DA:YY:YY:YY」はAP-MainのMACアドレスである。

Mar  3 23:19:01 raspi0 reconnect_wlan.sh[32492]: wlan1: target = D8:07:B6:XX:XX:XX, now = 6C:E4:DA:YY:YY:YY
Mar  3 23:19:01 raspi0 reconnect_wlan.sh[32492]: wlan1: trying to reconnect...
Mar  3 23:19:32 raspi0 reconnect_wlan.sh[32492]: wlan1: target = D8:07:B6:XX:XX:XX, now = D8:07:B6:XX:XX:XX

P.S. iwlist wlanN scanning

今回の、pingを用いるreconnect_wlan.shスクリプトを書き終えて満足した後に、近接のAP-Subの電波が飛んでいるかどうかを、定期的に $ sudo iwlist wlanN scanning で調べてみるという方法もアリと気づいた。ああ、このほうが電波強度も参考情報として取り入れられるしスマートな気がする。orz

TP-Link WiFi 無線LAN 子機 AC600 433Mbps + 200Mbps Windows/Mac OS 対応 ナノ設計 デュアルバンド 3年保証 Archer T2U Nano

TP-Link WiFi 無線LAN 子機 AC600 433Mbps + 200Mbps Windows/Mac OS 対応 ナノ設計 デュアルバンド 3年保証 Archer T2U Nano

TP-Link

Raspberry Pi Zero W - ヘッダー ハンダ付け済み - ラズベリー・パイ ゼロ W ワイヤレス

Raspberry Pi Zero W - ヘッダー ハンダ付け済み - ラズベリー・パイ ゼロ W ワイヤレス

Raspberry Pi

TOOGOO アクリルケース4 in 1キット Raspberry Pi Zero WおよびPi Zero用 ヒートシンク付き

TOOGOO アクリルケース4 in 1キット Raspberry Pi Zero WおよびPi Zero用 ヒートシンク付き

ソースコード

reconnect_wlan.sh

#!/bin/bash

# reconnect_wlan.sh (ver.20210303) for root@raspi0

TARGET_AP='MY_HOUSE_AP' # Name of Access Points
TARGET_IPADDRESS='192.168.1.NNN' # Nearest AP's IP address
TARGET_MAC='D8:07:B6:XX:XX:XX' # Nearest AP's MAC address

TARGET_NIC=`basename /run/wpa_supplicant/*`
CMD_LOGGER="logger -p user.info -t `basename $0`[$$]"
CMD_ACTION="sudo /usr/sbin/wpa_cli -i ${TARGET_NIC} reconfigure"

function get_mac() {
	local TMP_MAC=`/usr/sbin/iwconfig ${TARGET_NIC} 2>/dev/null | grep -A1 "${TARGET_AP}" | grep "Access Point:" | awk '{print $6}'`
	echo ${TMP_MAC}
}
function make_log_msg() {
	local TMP_MSG="${TARGET_NIC}: target = ${TARGET_MAC}, now = ${NOW_MAC}"
	echo ${TMP_MSG}
}

ping ${TARGET_IPADDRESS} -c 1 >/dev/null
if [ $? == 0 ]; then
	# Nearest AP is online
	NOW_MAC=`get_mac`
	if [ ${NOW_MAC} != ${TARGET_MAC} ]; then
		# reconnect
		LOG_MSG=`make_log_msg`; ${CMD_LOGGER} "${LOG_MSG}"
		LOG_MSG="${TARGET_NIC}: trying to reconnect..."; ${CMD_LOGGER} "${LOG_MSG}"
		sleep 10
		${CMD_ACTION}
		sleep 20
		NOW_MAC=`get_mac`
		LOG_MSG=`make_log_msg`; ${CMD_LOGGER} "${LOG_MSG}"
	fi
fi