8

I have a Raspberry Pi 3B running Raspbian Lite Stretch (2018-03-13).

I have an IoT device that broadcasts an AP. I want my RPi to join this AP whenever it is available (i.e., the IoT device is on). But when the AP is not available, I want it to fall back to connecting to my home infrastructure network.

I've configured the networks in the wpa_supplicant config, and assigned a priority of 1000 to the IoT network; if I turn the Raspberry Pi on while the IoT network is broadcasting, it correctly connects to it. When the IoT device goes off, the Raspberry Pi switches to the home network.

However, when the IoT device turns on after the RPi, the RPi does not automatically leave the home network and associate to the IoT device's network.

I'm OK with having to kick off the switch through a cron job or on some other trigger.

Doesn't work: wpa_cli select_network

The first technique I've tried is simply using wpa_cli select_network to switch manually. After running it, list_networks output changes to have this [DISABLED]/[CURRENT] indicator. However, it does not actually change networks.

Selected interface 'p2p-dev-wlan0'
network id / ssid / bssid / flags
0   HomeNetwork any [DISABLED]
1   IoTDevice   any [CURRENT]

I've also tried using wpa_cli reconnect and wpa_cli reassociate but these don't do anything. wpa_cli disconnect also does nothing, and wpa_cli disassociate [BSSID] just reports failure.

Doesn't work: iwconfig

$ sudo iwconfig wlan0 essid "IoTDevice"
Error for wireless request "Set ESSID" (8B1A) :
    SET failed on device wlan0 ; Operation already in progress.

Doesn't work: iw

$ sudo iw wlan0 connect IoTDevice
command failed: Operation already in progress (-114)

Not supported anymore, presumably: wpa-roam

Many guides suggest putting the device into wpa-roam mode, but the information on how to do so is out of date; all the guides reference /etc/network/interfaces, which seems to be virtually blank since the switch to dhcpcd.

Does work, but sucks: Nuking dhcpcd

Other people also express frustration with difficulty restarting wpa_supplicant short of blowing away dhcpcd each time. But when I do this, it does switch to the higher priority network if available.

I've written an answer that periodically scans for the desired network and kills dhcpcd when it's found; this works, but it is a very heavy-handed solution.

Since I can change networks just fine when the associated AP goes down without killing any processes, I shouldn't need to do this.


I'm trying to avoid cargo cult answers that involve flipping interfaces on and off, killing processes and anointing the board with sacred oils. How is switching networks really supposed to work?

rgov
  • 223
  • 2
  • 8
  • If you want any helpful answers I suggest you provide some information, starting with which OS, what you actually tried, and avoid emotive phrases like " blowing away dhcpcd" and say what commend this may refer to. – Milliways Mar 28 '18 at 07:17
  • 1
    The OS, Raspbian Lite 4.9, is in the first sentence. The linked answer about dhcpcd includes the commands to kill dhcpcd but I have also added it to my post. – rgov Mar 28 '18 at 07:24
  • Post OS in a tag - don't expect people to follow links, and guess what you did. There is No such thing as "Raspbian Lite 4.9" – Milliways Mar 28 '18 at 07:28
  • What happens if you upgrade the OS to RaspiOS Lite (2022-04-04) and try your wpa_cli, iwconfig and wpa-roam stuff again? Uninstalling dhcpcd will have no effect other than breaking all networking on your RPi. – Dougie Apr 08 '22 at 20:35
  • Something else that may be relevant to some who are finding this question (but I don't know if it's applicable in Raspbian, so not making an answer out of it) is nmcli - It's what worked for me to manually switch between available networks on an Ubuntu system. (wpa_cli was only showing one in list_networks, even after a scan produced scan_results showing various others -- so I couldn't figure out how to switch with wpa_cli, but nmcli con up <networkname> worked for me.) – lindes Jan 19 '23 at 17:43

3 Answers3

8

If you simply call wpa_cli without specifying the Interface, by default your raspbian stretch uses the interface 'p2p-dev-wlan0', as I can see in the output you posted.

Try the manual switching with wpa_cli by specifying the Interface (assuming that wlan0 is your regular wireless Interface, you want to use to connect to the Access point):

wpa_cli -i wlan0 select_network

oh.dae.su
  • 934
  • 1
  • 5
  • 12
  • 1
    Thanks for pointing out that the default interface should be specified by -i! That's the key point to solve this problem. – Huan Aug 31 '20 at 11:17
2

This should do what you need using the single wlan0 interface built into your Pi 3. I've tested this on my RPi 3B+; it works for my 2 WiFi APs. I've found it switches quickly as it does not require re-starting any services. You may wish to do some reading (see REFERENCES below) before you begin. The procedure is outlined below in two steps:

Step 1: create/edit wpa_supplicant.conf:

Declare both networks in /etc/wpa_supplicant/wpa_supplicant.conf. You didn't show your wpa_supplicant.conf file, so I've created this one to serve as an example. Be sure to change the country=GB line and network parameters to reflect your situation:

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

country=GB

network={ ssid="MySSID1" psk="mypasswd1" }

network={ ssid="MySSID2" psk="mypasswd2" }

Step 2: switch networks with wpa_cli:

Use wpa_cli in its interactive mode below, but know that all of these commands may be written as stand-alone commands, suitable for scripting. Lines beginning with # are explanatory comments - not part of the wpa_cli dialogue, and > is the wpa_cli command prompt in interactive mode:

$ wpa_cli

... # preliminaries...

> list_networks network id / ssid / bssid / flags 0 MySSID1 any [CURRENT] 1 MySSID2 any

The above result shows the two SSIDs configured in wpa_supplicant.conf

Also shows MySSID1 is [CURRENT]ly connected

> get_network 1 priority 0 > get_network 0 priority 0

This shows the priority of the two SSID/network ids (0 & 1)

Also shows that initially, both are priority 0 (equal priority)

> set_network 1 priority 2 OK

Assign a higher priority to network id 1 (MySSID2)

> reassociate OK
... a series of CTRL_EVENTS are listed ...

reassociate causes the connection to switch to the highest priority

network id/SSID if available; that is now network id 1 (MySSID2)

> list_networks network id / ssid / bssid / flags 0 MySSID1 any 1 MySSID2 any [CURRENT]

confirmtion that network switch took place after reassociate

connection has moved to higher priority network/SSID [CURRENT]

to restore the original connection to MySSID1:

use the set_network priority commands above to prioritze MySSID1,

and follow that with another reassociate

Note also: when priorities of all networks are equal,

wpa_supplicant defaults to the one with the strongest signal

> quit

terminates the wpa_cli interactive session

$

And that's the switch wifi connections process. You should verify this works with your network configuration manually as I've shown above. Once verified, you may use the equivalent stand-alone commands to automate the process in a script - or experiment with wpa_cli running in daemon mode.

This procedure assumes your networks are properly configured. On RPi, part of properly configured means dhcpcd.conf is configured IAW best practices; e.g. do not use static ip unless you know what you are doing.


REFERENCES:

  1. RE: wlan0 vs. p2p-dev-wlan0
  2. ArchWiki wpa_supplicant documentation
  3. man wpa_cli on your RPi
Seamus
  • 21,900
  • 3
  • 33
  • 70
2

I'm not happy with this solution, but it does work.

This script will scan for the desired network, and if it is found, it will restart dhcpcd. This disconnects from the current network and then connects to the highest priority network (as configured in wpa_supplicant.conf).

#!/bin/sh
if iwlist wlan0 scan | grep -q "$1"; then
  logger Network "$1" is available, switching
  systemctl restart dhcpcd
fi

Note that iwlist lies about scanning if it is not invoked as root; it will just spit out the details for the current network. Because Linux networking isn't full of enough pitfalls, I guess.

We can save this file in /usr/bin/switch-network-if-available.sh and then set up a cronjob to execute it every minute:

* * * * * root /usr/bin/switch-network-if-available.sh IoTDevice
rgov
  • 223
  • 2
  • 8