Skip to content

Instantly share code, notes, and snippets.

@ajfisher
Last active November 21, 2023 16:28
Show Gist options
  • Star 39 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save ajfisher/a84889e64565d7a74888 to your computer and use it in GitHub Desktop.
Save ajfisher/a84889e64565d7a74888 to your computer and use it in GitHub Desktop.
Auto WiFi detection and hotspot creation in boot for RPI

Auto WiFi detection or wifi hostpot creation during boot for RPI

Note: These are rough notes and there may be some variance as versions of raspbian get updated but should be pretty reliable as a guide.

This gist provides some instructions and config in order to have your Raspberry PI automatically connect to a roamed network, however if it fails to discover an available network it will set itself up as a wireless access point for you to connect to.

Preconditions:

  • Raspberry Pi running Raspbian (latest is test on Jessie)
  • you're able to get onto a normal wireless network without any issues at all with your default set up.
  • drivers for your wireless device if you need it or a RPi3 with WiFi working.

Ensure the following packages are installed:

apt-get install hostapd wpasupplicant dnsmasq

Now make sure dnsmasq and hostapd don't start as a service every time you boot as we only want this to happen selectively.

update-rc.d -f hostapd remove
update-rc.d -f dnsmasq remove

Default WPA set up

If you haven't got your network interfaces configured to use wpa_supplicant then do this now and test it is all correct and you can connect reliably to at least one network.

To do this you want a config file in /etc/wpa_supplicant/wpa_supplicant.conf (see the file in this gist for example) that has your roaming network definitions. To get a network definition do this:

wpa-passphrase SSID KEY

And it will output something that looks like this:

network={
	ssid="SSID"
	#psk="KEYKEYLEYKEY"
	psk=fef26ff30be96e6149511e66d2530d21b65b24dbf2908f9b44f7834574e1d048
}

At this point you want to add an "id_str" element so you can determine which network is which when you have multiple, so your config now looks like:

network={
	id_str="test_network"
	ssid="SSID"
	#psk="KEYKEYLEYKEY"
	psk=fef26ff30be96e6149511e66d2530d21b65b24dbf2908f9b44f7834574e1d048
}

Add this network config to the wpa_supplicant.conf file. You can add as many of these as individual blocks as you have networks to attach to. Just remember to add your id_str to each one so you can reference it from your network interfaces in a moment.

Okay so now wpa_supplicant is configured, you want to set up your network interfaces in /etc/network/interfaces

See the attached interfaces file and note the wlan stanza - you may need to change this to the device you use.

iface wlan0 inet manual
	wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf

Here we're saying to use wpa-roam which will check the config file we just made for rules and try to find an appropriate network.

You'll also need to add a line that looks like this to the interfaces file after your wlan interface definition:

iface test_network inet dhcp

Notice we're making reference to the name of the network we defined in the wpa_supplicant config. This gives us a hook to define different configs for each network. Mostly you'll use DHCP, but you can use static definitions in here as well if you want your device to come up on a specific IP. Standard iface stanza config applies within each of these sections.

Restart your device and make sure this is all working properly before progressing forward as you want this to work flawlessly every time during boot up.

rpi-config setup

With more modern versions of raspbian the startup runs in parallel. As such you need to drop the network part of this into wait mode or you'll get conflicts during the start up.

raspi-config

Select "Wait for network at boot" and select "Yes".

This will add a few seconds to each boot stage but makes it fair more robust.

hostapd set up

Change the /etc/default/hostapd file to include this line:

DAEMON_CONF="/etc/hostapd/hostapd.conf"

Now create the /etc/hostapd/hostapd.conf file and look at the example attached to this gist. Note you'll probably need to change the interface, the ssid and the wpa_passphrase to something of use for you. Also note the segments that are related to the nl chip being used on an RPi 3 as this has some extra configuration requirements.

Once you have this done run hostapd to just check any config errors. Before you do this you'll probably want to kill your wireless device with ifdown wlanX as hostapd will try to configure it and you may run into issues.

hostapd -d /etc/hostapd/hostapd.conf

Jessie notes:

If you get no errors, background the task and then tail -f /var/log/syslog and then attempt to associate to the wifi network with another device, you should see all of the handshake messages occurring from hostapd and your device. At this point you won't get an IP but you should see the association (and disassociation if you drop your connection).

Older Raspbian notes:

If you get no errors, then background the task and then look at ifconfig. You should now have the wlanX interface up as well as a wlanX.mon interface. If this is all good then you know your config is working for hostapd.

dnsmasq set up

The last piece is to get dnsmasq running. This is fairly straight forward

Use the example in this gist and note that you want to change the interface you listen on and you might want to set your own DHCP-range. Write down what IP address range you want to operate on as you'll need this in a bit. In this example we're using the 192.168.40.0 network.

That's pretty much all you really need to make it work.

Testing the components.

Do this:

ifdown wlanX
service hostapd start
ifconfig wlanX 192.168.40.1
service dnsmasq start

Note that we're configuring the IP after we set up hostapd. This is a quirk that we have to deal with as hostapd can't be bound to an aliased network interface which is annoying as it means we can't use ifup and a network stanza.

Now tail -f /var/log/syslog

If there's no errors, connect to the wireless AP from another machine and you should see all the hand shaking etc happen and you should then be able to ping each machine that has joined the network.

Success!!

Automating this at boot.

To make this work at boot you need to use an rc.local script. /etc/rc.local is the very last thing that runs during init just prior to the shell being made available.

See the example provided in rc.local. The idea here is that you check to see if the network is up - if it is it means you've associated using wpa_supplicant roaming settings or are plugged into the network. If this hasn't ocurred then it goes through the process that we did manually above to create the hotspot.

Going further

As I have this on a robot, I actually use the RPi GPIO pins to light up LEDs during rc.local's execution to tell me what is going on. This is pretty easy and is readily googleable but very useful if you don't have a monitor and can't use a serial terminal.

# Configuration file for dnsmasq.
#
# Format is one option per line, legal options are the same
# as the long options legal on the command line. See
# "/usr/sbin/dnsmasq --help" or "man 8 dnsmasq" for details.
# Listen on this specific port instead of the standard DNS port
# (53). Setting this to zero completely disables DNS function,
# leaving only DHCP and/or TFTP.
#port=5353
# The following two options make you a better netizen, since they
# tell dnsmasq to filter out queries which the public DNS cannot
# answer, and which load the servers (especially the root servers)
# unnecessarily. If you have a dial-on-demand link they also stop
# these requests from bringing up the link unnecessarily.
# Never forward plain names (without a dot or domain part)
domain-needed
# Never forward addresses in the non-routed address spaces.
#bogus-priv
# Add other name servers here, with domain specs if they are for
# non-public domains.
#server=/localnet/192.168.0.1
# If you want dnsmasq to listen for DHCP and DNS requests only on
# specified interfaces (and the loopback) give the name of the
# interface (eg eth0) here.
# Repeat the line for more than one interface.
interface=wlan0
# Or you can specify which interface _not_ to listen on
#except-interface=
# Or which to listen on by address (remember to include 127.0.0.1 if
# you use this.)
#listen-address=
# If you want dnsmasq to provide only DNS service on an interface,
# configure it as shown above, and then use the following line to
# disable DHCP and TFTP on it.
#no-dhcp-interface=
# On systems which support it, dnsmasq binds the wildcard address,
# even when it is listening on only some interfaces. It then discards
# requests that it shouldn't reply to. This has the advantage of
# working even when interfaces come and go and change address. If you
# want dnsmasq to really bind only the interfaces it is listening on,
# uncomment this option. About the only time you may need this is when
# running another nameserver on the same machine.
#bind-interfaces
# Uncomment this to enable the integrated DHCP server, you need
# to supply the range of addresses available for lease and optionally
# a lease time. If you have more than one network, you will need to
# repeat this for each network on which you want to supply DHCP
# service.
#dhcp-range=192.168.0.50,192.168.0.150,12h
dhcp-range=192.168.40.5,192.168.40.100,255.255.255.0,12h
# Defaults for hostapd initscript
#
# See /usr/share/doc/hostapd/README.Debian for information about alternative
# methods of managing hostapd.
#
# Uncomment and set DAEMON_CONF to the absolute path of a hostapd configuration
# file and hostapd will be started during system boot. An example configuration
# file can be found at /usr/share/doc/hostapd/examples/hostapd.conf.gz
#
DAEMON_CONF="/etc/hostapd/hostapd.conf"
# Additional daemon options to be appended to hostapd command:-
# -d show more debug messages (-dd for even more)
# -K include key data in debug messages
# -t include timestamps in some debug messages
#
# Note that -B (daemon mode) and -P (pidfile) options are automatically
# configured by the init.d script and must not be added to DAEMON_OPTS.
#
#DAEMON_OPTS=""
interface=wlan0
# use rtl driver if you're using a WiPi, the nl driver if you're
# using an RPi3 onboard wifi chip.
#driver=rtl871xdrv
driver=nl80211
ssid=tempwifi
hw_mode=g
channel=6
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_passphrase=qwertyuiop
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP
# these are required for the nl chip
ieee80211n=1
wmm_enabled=1
ht_capab=[HT40][SHORT-GI-20][DSSS_CCK-40]
auto lo
iface lo inet loopback
allow-hotplug eth0
iface eth0 inet dhcp
allow-hotplug wlan0
iface wlan0 inet manual
wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.
RED='\033[0;31m'
NC='\033[0m'
GRN='\033[0;32m'
_APIP="192.168.40.1" # change this to whatever you've set in dnsmasq
_WLAN_DEVICE="wlan0" # change this to whatever your wlan device is (wlan0, etc)
# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
printf "At least one network interface is configured and is up\n"
printf "My IP address is %s\n" "$_IP"
else
printf "No network interface has come up so let's configure the access point\n"
ifdown $_WLAN_DEVICE
sleep 8
printf "Bringing up hostapd\n"
service hostapd restart
sleep 8
printf "Configuring wlan interface\n"
ifconfig $_WLAN_DEVICE $_APIP
sleep 8
printf "Configuring DNSMasq\n"
service dnsmasq restart
sleep 8
printf "You should now have an access point\n"
fi
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
# add IP to the issue file
sed -i_bak -e '/IP/d' /etc/issue
IPADD=`/sbin/ifconfig | sed '/Bcast/!d' | awk '{print $2}'| awk '{print $2}' FS=":"`
else
IPADD='NO IP'
fi
echo "IP: ${GRN}$_IP${NC}" >> /etc/issue
exit 0
ctrl_interface=/var/run/wpa_supplicant
network={
id_str="network_string"
ssid="NETWORK"
psk=reallylongpskstring
}
@destinationunknown
Copy link

Minor correction: On Raspbian stretch, the command wpa-passphrase is invalid and wpa_passphrase should be used instead. (Can't confirm for Jessie)

@qickrooms
Copy link

wpa-passphrase SSID KEY SHOULD be wpa_passphrase SSID KEY

@Golgomaph
Copy link

Golgomaph commented Feb 15, 2021

Hi,

this thing works even today.

Just a little change is necessary in rc.local:

Instead of

printf "Bringing up hostapd\n"
service hostapd restart
sleep 8

it needs to be

printf "Bringing up hostapd\n"
systemctl unmask hostapd
systemctl enable hostapd
service hostapd restart
sleep 8

Tested on raspi3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment