Monday, 17 April 2017 10:56

Raspberry Pi - Auto WiFi Hotspot Switch

Written by 
  • Author Type: Individual
  • Country: uk

A script to switch between a WiFi Network or WiFi HotSpot without a reboot. If the Wifi network is lost then the RPi will generate a Wifi hotspot, then reconnect to the Network when it is available again.

I often use my Raspberry Pi's in the garden or away from home. I use a script that generates a wifi hotspot if my home network is not found so I can ssh to the Raspberry Pi via my Tablet, Phone or Laptop. When I am at home it will connect to my Home network as normal. This only happens at reboot but works fine for what it is designed for. If I have a Raspberry Pi in my garden taking photos or using sensors it will be connected to my home network but occasionally looses connection to my router and not reconnect probably due to inteferance. This means I have to go outside and reboot the pi to get the wifi back.

I have now updated my original script so that it can switch the RPI between an access point hotspot or network without a reboot. Using this script along with a timer, if the RPi looses connection with the router it will generate a WiFi Hotspot when the timer is triggered so I can still gain SSH or VNC access without going outside.

This can also be used so the Pi will genarally switch between a hotspot or network as you come home from a trip out without rebooting. Though if you have made a ssh or vnc connection at the point it switches you will loose your connection.

This setup is designed to gain direct access to the pi when it is not in range of a network, as a result it will not be able to connect to the Internet via Ethernet in hotspot mode but is fully functional in Network mode. 

If it is in Hotspot mode and you connect it to a router via an ethernet cable you can ssh from that routers network and use the internet but you cannot get internet if you have made a wifi connection to the hotspot using the ip 10.0.0.5.

Aim: 

  • On bootup it will connect to a known network or if not available generate a hotspot
  • Access available via SSH and VNC in both network and hotspot mode
  • using a timer to run the script at set intervals:
    • If the Raspberry Pi looses network connection it will generate a wifi hotspot
    • When the network is available again RPi reconnects to the router

Can be used for:

  • Running a program in the background when you are home, then when you go out of network range you can still access the Rpi to check results or make changes.
  • Raspberry Pi In car entertainment setups. Upload new content at home from the network then access the pi via a hotspot on the move. Could be altered to allow a button to activate Hotspot or Network manually.
  • Linking a Raspberry Pi with a Laptop and Telescope for photography or positioning.
  • If you use the RPi in your garden and place it in a wifi dead zone, it is still accessible via ssh or vnc in hotspot mode.

 

Requirments

Connection settings already created for any SSID you are using and listed in /etc/wpa_supplicant/wpa_supplicant.conf

The "Raspberry Pi 3 Auto WiFi Spot if no Internet" article has been followed except the /usr/bin/autohotspot script.

Initial Setup

This article makes changes to the main script of Raspberry PI 3 Auto WiFi Hotspot if no Internet

For the full setup follow the instructions in the link above but change the script saved as /usr/bin/autohotspot to the one in this article.

 

Hotspot and Network Switch script

 Once you have setup:

  • Hostapd
  • DnsMasq
  • Made changes to the Interfaces file
  • Set up the systemd autohotspot.service and activated it

Save following script to /usr/bin/autohotspot

(Also available to download from here)


#!/bin/bash
#Script to switch between a wifi network and a No Internet Hotspot without rebooting
#Other setup required find out more at 
#http://www.raspberryconnect.com/network/item/315-raspberry-pi-3-auto-wifi-hotspot-if-no-internet
#Note: Remove the # in in front of ssids=('mySSID1'.... to only use certain networks and put a # infront of  ssids=$(awk '.....
#To only check for certain WiFi networks enter one or more ssids that are already setup in the wifi settings
# separated by a space, ('mySSID1' 'mySSID2')
#ssids=('mySSID1' 'mySSID2' 'mySSID3')
# or to use any wifi network in your setting use
ssids=$(awk '/ssid="/{ print $0 }' /etc/wpa_supplicant/wpa_supplicant.conf | awk -F'ssid=' '{ print $2 }' ORS=' ' | sed 's/\"/''/g' | sed 's/ $//')
createAdHocNetwork()
{
    ip link set dev wlan0 down
    ip a add 10.0.0.5/24 dev wlan0
    ip link set dev wlan0 up
    systemctl start dnsmasq
    systemctl start hostapd
}
KillHotspot()
{
	echo "Shutting Down Hotspot"
	ip link set dev wlan0 down
	systemctl stop hostapd
	systemctl stop dnsmasq
	ip addr flush dev wlan0
	ip link set dev wlan0 up
}
#ssid in range check
ssidChk=('NoSSid')
for ssid in "${ssids[@]}"
do 
	if iw dev wlan0 scan ap-force | grep "$ssid" > /dev/null ;then
		ssidChk=$ssid
                break
	else
		ssidChk='NoSSid'
	fi
done 
if [ $ssidChk != "NoSSid" ] 
then
	if systemctl status hostapd | grep "(running)"
	then #hotspot running and ssid in range
		KillHotspot
		echo "Hotspot Deactivated, Bringing Wifi Up"
        	wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf > /dev/null 2>&1
	elif wpa_cli status | grep 'wlan0'
	then #If already connected stop script
		echo "Wifi already connected to network"
	else #ssid exists and no hotspot running connect to wifi network
		echo "Connecting to WiFi Network"
		wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf > /dev/null 2>&1
        	if wpa_cli status | grep 'wlan0' > /dev/null
        	then
			echo "Wifi connection ok"
        	else
			echo "Wifi failed to connect, Creating Hotspot instead"
            		wpa_cli terminate
			createAdHocNetwork
        	fi
	fi
else #ssid not in range
    	if systemctl status hostapd | grep "(running)" > /dev/null 
	then
		echo "Hostspot already active"
	elif wpa_cli status | grep 'wlan0' > /dev/null
	then
		echo "Cleaning wifi files and Activating Hotspot"
		wpa_cli terminate
		ip addr flush wlan0
		ip link set dev wlan0 down
		rm -r /var/run/wpa_supplicant > /dev/null
		createAdHocNetwork
	else #"No SSID, activating Hotspot"
		createAdHocNetwork
	fi
fi

This script works by either checking all wifi networks that you have set a connection to (found in wpa_supplicant.conf) or just checks the WiFi networks you enter a SSID for, and ignores any others you have configured in your wifi settings.

By Default it will just try to connect to any WiFi network you Raspberry Pi has been setup to use.

To only check for certain SSIDs put a # in front of the line starting ssids=$(awk '/s.....

and remove the # from the line starting #ssids=('mySSID1......

Change the entries in ssids=('mySSID1' 'mySSID2' 'mySSID3') to your routers SSID you can add or delete ssid entries but make sure there is a space between each entry.

Once saved make the script executable with

sudo chmod +x /usr/bin/autohotspot

Now the script will only work at bootup so we need to setup a timer to run the autohotspot script at set intervals.

For now I have had to do this using a Cron job as systemd timers are not working consistantly with this to be useful.

Using a Cron job, tasks can be set off automatically at certain times, dates or intervals. For my use running the script every 5 minutes is fine but this can be changed to your needs. To setup a cron task enter the command

crontab -e

At the bottom of the file enter the command

*/5 * * * * /usr/bin/autohotspot

There is a space after each entry and * except the first *.

 The first * position is for minutes. If you want it to check every minute just use * instead of */5

If you want to use hours, say every 2 hours enter it as

* */2 * * * /usr/bin/autohotspot

 Save the cron tab with ctrl & o and close it with ctrl & x

The cron job will automatically start straight away.

 

This process has been tested on a Raspberry Pi 3 and a Raspberry Pi Zero W.

Disable Cron Timer

If you no longer need the timer running edit the cron with

crontab -e

and put a # infront so it is now

#*/5 * * * * /usr/bin/autohotspot

The script will now only work at boot up or if you manually run the autohotspot script with the command

sudo /usr/bin/autohotspot

Script Removal

To disable the complete autohotspot process;

Edit the /etc/network/interfaces file and remove the # from the line

# wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf

Then disable the startup script with the command

sudo systemctl disable autohotspot

After a reboot your RPi will connect to your router.

 

 

Last modified on Wednesday, 03 May 2017 21:05
Google
roboberry

RapberryConnect.com SuperUser.  Contactable via the site admin e-mail in the Contact Us link.

Website: www.raspberryconnect.com

Leave a comment

Comments for Guest and account owners. Account owners will need to login to use registered e-mail address.

8 comments

  • Comment Link Graeme Sunday, 28 May 2017 09:34 posted by Graeme

    Hi susan, sorry for slow response, i have been away. The service triggers once the system signals the network is available so it should connect but it may be possible that another program causes a long enough delay for the ssid not to be detectable. Try removing the # infront of the wpa_supplicant line in /etc/network/interfaces it isn't needed for this script. The pi will connect to wifi, if available, without the script. Then the script will asses the situation and switch if required. Let me know if you still have an issue.

    Report
  • Comment Link Susan Thursday, 25 May 2017 22:47 posted by Susan

    Some of the time, on bootup, the service will fail to detect the local wifi and it will start the hotspot. Once the cronjob triggers, it realizes there is a local wifi and will make the switch. Is it possible the service is starting before the network is ready or able to detect the local wifi?

    Report
  • Comment Link Graeme Tuesday, 02 May 2017 21:57 posted by Graeme

    Hi Tino, Thanks for the update. I won't be able to try this one out as I don't have hidden networks or email configured but I will be adding it to the article as an alternative script. Thanks for sharing this.

    Report
  • Comment Link Tino Tuesday, 02 May 2017 21:07 posted by Tino

    Hi Graeme, here is another update. I have some hidden networks in my configuration, so I also wanted to scan for some mac addresses. In addition, I want to have an e-mail with the current IP address if connection was established to a wifi network or personal hotspot of my phone or tablet.

    Please see the modified script below, Cheers.

    #!/bin/bash
    ##
    # Wifi config - if no prefered Wifi generate a hotspot
    ##

    ## enter required ssids: ssids=('ssid1' 'ssid2') ...

    #ssids=('mySSID1' 'mySSID2')

    ## ... or let the script parse your wpa_supplicant.conf

    ssids=$(awk '/ssid="/{ print $0 }' /etc/wpa_supplicant/wpa_supplicant.conf | awk -F'ssid=' '{ print $2 }' ORS=' ' | sed 's/\"/''/g' | sed 's/ $//')

    ## for hidden wifi networks, add mac adresses here:

    macAdresses=('11:22:33:44:55:66' '66:55:44:33:22:11')

    ## build network string
    networks=($ssids ${macAdresses[@]})

    createAdHocNetwork()
    {
    ip link set dev wlan0 down
    ip a add 10.0.0.5/24 dev wlan0
    ip link set dev wlan0 up
    systemctl start dnsmasq
    systemctl start hostapd
    }
    KillHotspot()
    {
    echo "Shutting Down Hotspot"
    ip link set dev wlan0 down
    systemctl stop hostapd
    systemctl stop dnsmasq
    ip addr flush dev wlan0
    ip link set dev wlan0 up
    }
    #check for entry in range
    ssidChk=('NoSSid')
    for entry in "${networks[@]}"
    do
    if iw dev wlan0 scan ap-force | grep "$entry" > /dev/null ;then
    ssidChk=$entry
    break
    else
    ssidChk='NoSSid'
    fi
    done

    if [ $ssidChk != "NoSSid" ]
    then
    if systemctl status hostapd | grep "(running)"
    then #hotspot running and network entry in range
    KillHotspot
    echo "Hotspot Deactivated, Bringing Wifi Up"
    wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf > /dev/null 2>&1
    # wait and check if wifi connection was established
    sleep 10
    if wpa_cli status | grep 'wlan0' > /dev/null
    then
    echo "Wifi connection ok"
    # send e-mail with ip address
    echo "Wifi connection established..." |mail -s "[WifiConnect] $(hostname) - IP: $(hostname -I|cut -d" " -f1) / `date +\%Y.\%m.\%d` - `date +\%H:\%M` "
    else
    echo "Wifi failed to connect, Creating Hotspot again"
    wpa_cli terminate
    createAdHocNetwork
    fi
    elif wpa_cli status | grep 'wlan0'
    then #If already connected stop script
    echo "Wifi already connected to network"
    else #network entry exists and no hotspot running connect to wifi network
    echo "Connecting to WiFi Network"
    wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf > /dev/null 2>&1
    if wpa_cli status | grep 'wlan0' > /dev/null
    then
    echo "Wifi connection ok"
    # send e-mail with ip address
    echo "Wifi connection established..." |mail -s "[WifiConnect] $(hostname) - IP: $(hostname -I|cut -d" " -f1) / `date +\%Y.\%m.\%d` - `date +\%H:\%M` "
    else
    echo "Wifi failed to connect, Creating Hotspot instead"
    wpa_cli terminate
    createAdHocNetwork
    fi
    fi
    else #no network entry in range
    if systemctl status hostapd | grep "(running)" > /dev/null
    then
    echo "Hostspot already active"
    elif wpa_cli status | grep 'wlan0' > /dev/null
    then
    echo "Cleaning wifi files and Activating Hotspot"
    wpa_cli terminate
    ip addr flush wlan0
    ip link set dev wlan0 down
    rm -r /var/run/wpa_supplicant > /dev/null
    createAdHocNetwork
    else #"No network entry in range, activating Hotspot"
    createAdHocNetwork
    fi
    fi

    Report
  • Comment Link Graeme Sunday, 30 April 2017 10:58 posted by Graeme

    Hi Tino, thanks for reposting the command. I will update my RPi's and add to the article in due course. Very helpful, Thanks.

    Report
  • Comment Link Tino Saturday, 29 April 2017 13:45 posted by Tino

    Hi Graeme, as mentioned before, reading the ssids directly from wpa_supplicant.conf would help to ensure that the script looks only for known networks/ssids.

    Thats why I have changed line 6:
    #ssids=('mySSID1' 'mySSID2' 'mySSID3')
    ssids=$(awk '/ssid="/{ print $0 }' /etc/wpa_supplicant/wpa_supplicant.conf | awk -F'ssid=' '{ print $2 }' ORS=' ' | sed 's/\"/''/g' | sed 's/ $//')

    Cheers,
    Tino

    Report
  • Comment Link Graeme Friday, 28 April 2017 21:48 posted by Graeme

    Hi Tino, thanks for pointing that out. It worked in testing so I must cleared it in the code clean up before posting as I did only use the 3rd slot for the final checks. Good point about using the wpa file for both scripts which would make them set-up and forget scripts.

    Report
  • Comment Link Tino Friday, 28 April 2017 14:43 posted by Tino

    Hi,

    thank you for this helpful update of your script!

    But short note about the loop:

    If you use multiple ssids ('mySSID1' 'mySSID2' 'mySSID3') then the result of your loop will end with ssidChk='NoSSid' even if mySSID1 or mySSID2 are in range but mySSID3 is not.

    So I think you have to insert a break into the loop right after "ssidChk=$ssid".

    Furthermore, you could parse your wpa_supplicant.conf and scan for the ssid= entries and write the results to the $ssids variable. Just an idea ...

    Cheers,
    Tino

    Report

Additional information