Tuesday, 21 August 2018

Bash wifi network switcher script for Raspberry Pi

I had a situation where I had a Raspberry Pi 3B+ in a location where there were two usable wifi networks in range, but neither were particularly reliable in terms of Internet access. The wifi networks themselves would stay up, but the ADSL connections used by the  routers would sometimes drop and stay down for a while (in the case of one, until someone rebooted it).

So I wanted the Pi to switch from one network to the other in the event of losing Internet connectivity.

So I wrote a bash script that can be run periodically by cron.


There are a number of ways to achieve this,  including using additional packages like nmcli, but I wanted something that should just work on a plain version of Raspbian.

I also decided to test it on older Pis without built-in wifi and using a doingle instead. I found that a few tweaks were required to get the script to work reliably. 

I also encountered one situation where the Pi under test ended up not connected to any wifi network at all. Annoyingly I have not been able to reproduce that outcome to identify what went wrong.

The script is set to switch between two networks, SSID1 and SSID1 - obviously you'll need to change these names to reflect your own environment. 

To use the script you'll need two different copies of your wpa_supplicant file - which contains the network name and pre-shared key (password) required to authenticate - , one for each SSID.  Give them sensible names like  wpa_supplicant_1.conf and  wpa_supplicant_2.conf.

The script logs to a file wifi.log - this can be useful for debugging. It also echoes to the terminal too, to make it easier to see what's going on when testing. If you don't want any of this, just remove or comment out the 'echo' lines. 
#!/bin/bash
# Checks for Internet connectivity and changes to an alternative wifi network if
# it detects a problem.
# Needs to have a wpa_supplicant file for each network:
#  wpa_supplicant_1 for SSID! and wpa_supplicant_2 for SSID2
# Can easily be extended to more than 2 possible networks

# function to bring down wireless interface, stop wpa_supplicant service and
# then bring up wlan0 again
reset_wifi(){
    /sbin/ifconfig wlan0 down
    sleep 1
    killall wpa_supplicant
    sleep 1
    /sbin/ifconfig wlan0 up
    sleep 1
}
# Logging information saved to a file
# useful for testing and debugging
echo "START" >> /home/pi/wifi.log
echo "Date:" `date`  >> /home/pi/wifi.log
echo "WLAN:" `/sbin/iwgetid` >> /home/pi/wifi.log
if /sbin/iwgetid | grep SSID1 > /dev/null; then
  echo "SSID1" >> /home/pi/wifi.log
  SSID="SSID1"
elif /sbin/iwgetid | grep SSID2 > /dev/null; then
  echo "SSID2"
  echo "SSID2" >> /home/pi/wifi.log
  SSID="SSID2"
else
  echo "unknown SSID"
  echo "unknown SSID" >> /home/pi/wifi.log
  SSID="UN"
fi
echo "$SSID" >> /home/pi/wifi.log
if ping -q -c 1 -W 1 8.8.8.8 >/dev/null; then
  echo "Net is up"
  echo "Net is up" >> /home/pi/wifi.log
else
  if ping -q -c 1 -W 1 8.8.8.8 >/dev/null; then
    echo "Net is up (after retry)"
    echo "Net is up (after retry)" >> /home/pi/wifi.log
  else
    echo "Net is down (after retry)"
    echo "Net is down (after retry)" >> /home/pi/wifi.log
    if [ "$SSID" = "SSID1" ]; then
      echo "trying SSID2"
      echo "trying SSID2"  >> /home/pi/wifi.log
      reset_wifi
      cp /etc/wpa_supplicant/wpa_supplicant_2.conf /etc/wpa_supplicant/wpa_supplicant.conf
      /sbin/wpa_supplicant -B -c/etc/wpa_supplicant/wpa_supplicant.conf -iwlan0

  elif [ "$SSID" = "SSID2" ]; then
      echo "trying SSID1"
      echo "trying SSID1"  >> /home/pi/wifi.log
      reset_wifi
      cp /etc/wpa_supplicant/wpa_supplicant_1.conf /etc/wpa_supplicant/wpa_supplicant.conf
      /sbin/wpa_supplicant -B -c/etc/wpa_supplicant/wpa_supplicant.conf -iwlan0
    else
      echo "dunno"  >> /home/pi/wifi.log
      echo "dunno"
      if [ `date +%M` = 10 ] || [ `date +%M` = 30 ] || [ `date +%M` = 50 ] ; then
         echo "yep"
         echo "trying SSID1 anyway"
         echo "trying SSID1 anayway"  >> /home/pi/wifi.log
         reset_wifi
         cp /etc/wpa_supplicant/wpa_supplicant_1.conf /etc/wpa_supplicant/wpa_supplicant.conf
         /sbin/wpa_supplicant -B -c/etc/wpa_supplicant/wpa_supplicant.conf -iwlan0
      else
         echo "trying SSID2 anyway"
         echo "trying SSID2 anyway"  >> /home/pi/wifi.log
         reset_wifi
         cp /etc/wpa_supplicant/wpa_supplicant_2.conf /etc/wpa_supplicant/wpa_supplicant.conf
         /sbin/wpa_supplicant -B -c/etc/wpa_supplicant/wpa_supplicant.conf -iwlan0
      fi
    fi
  fi
fi
echo "END" >> /home/pi/wifi.log
view raw snippet.md hosted with ❤ by GitHub

There's no reason that this shouldn't work on other Debian distros or more widely on Linux, but I have not tested it.

The script needs to be run on a Pi with sudo. If you want to schedule using cron, then type 

sudo crontab -e

and then add a line to run as frequently as you like. For example:

0 */2 * * * /home/pi/wifitest.sh

Don't forget to make the file executable

chmod +x /home/pi/wifitest.sh

Tuesday, 14 August 2018

Testing Adafruit Feather M0 LoRa 900MHz in the Alps

One of my birthday presents this year was  two 900MHz Adafruit feather M0 LoRa 800MHz boards. I hadn't had a chance to try them out  but just before going on holiday I realised that our destination in the Alps might provide a nice testing ground (or mountain) so added them to my hold luggage.

I know that LoRa tech is capable of impressive line-of-sight distances when there are no obstructions, but as I hadn't had time to pack my HAB gear, I needed to find some other way of testing the range.

The Feather boards are a nice size and I was just using a length of wire as an antenna. Example client/server Arduino code from the Radiohead RFM0x library was good enough for my purposes. Each board would blink its LED when it received a packet so I could see if data was being exchanged.

For the tests, one board  stayed back at base in the chalet - shoved out the Velux window for maximum height.


It was very sunny and hot so I decided that the base Feather ought to have some shade.


The second board came along with me on my travels, powered by a battery pack.


Over a few days I took the Feather out with me, first of all just cycling around the town, then for a grander test, heading up the mountain in the Grand Massif tele-cabin.



On the ascent, I was surprised to not be picking up any packets, even though I had been able to get good reception if I wondered about in the car park at the bottom. Thinking that the cabin itself might be acting as a bit of a Faraday cage, I poked the Feather out the window, and immediately started seeing the blinkin' red LED.


The contours of the mountain meant that there was actually quite a lot of rock and trees in between the two boards right at the top of the tele-cabin, and despite some wandering around to try to get better reception, the LED stayed off until we'd trekked some way  further up the mountain.

Fortunately you can also get a chair lift even higher, so we continued  to ascend to 2100m (the chalet itself is at 700m).


At the top the signal was very strong and with binoculars we could actually see the chalet down in the valley.

I recorded the various locations using the GPS Waypoint app on my phone and then exported these via KML into Google Earth. Red markers are places where there was no reception, green are where packets could be received.



This allows me to calculate the straight line distance between the two boards - the maximum distance was 5.86km, 


Even on flat ground,  with trees and buildings in the way, I was still able to exchange packets at 2.60km, and after that point I rounded the edge of the opposite mountain which probably blocked any further data.



So I was very impressed with the boards. They'll certainly be up to relaying data from my new shed....