A minimal WiFi onboarding portal for OpenA3XX devices. When provisioning is required, the device switches to Access Point (AP) mode, hosts a simple web portal, captures WiFi credentials, writes /etc/wpa_supplicant/wpa_supplicant.conf, and reboots into client mode.
The stack is based on hostapd (AP), dnsmasq (DHCP/DNS with captive-style redirect), and a small Flask app for the portal UI.
- AP onboarding flow: Boot into AP mode when WiFi is not configured or when a marker file exists.
- Captive-style DNS redirect: All DNS queries resolve to
10.0.0.1while in AP mode. - Simple web portal: Collects country code, SSID, and password; persists to
wpa_supplicantand reboots. - Systemd integration: Services automatically manage AP/client mode transitions on boot.
- OS: Debian/Ubuntu/Raspberry Pi OS or other systemd-based Debian derivative
- Network interface: A wireless interface named
wlan0withnl80211compatible driver - Privileges: Root access for installation and service management
Packages installed by the installer: hostapd, dnsmasq, python3-flask, wireless-tools, iproute2.
- Install (as root):
sudo bash install.sh
- Trigger provisioning mode and reboot:
sudo touch /boot/PROVISION_WIFI && sudo reboot - Connect to the AP:
- SSID:
OpenA3XX_Setup - Password:
opena3xx
- SSID:
- Open
http://10.0.0.1in a browser. Submit your WiFi credentials. The device will reboot and attempt to join the configured WiFi.
-
systemdservicesopena3xx-boot.service: Runs on boot and decides the mode based on presence of/boot/PROVISION_WIFIor an empty/etc/wpa_supplicant/wpa_supplicant.conf.wifi-portal.service: Runs the Flask portal on port 80 when in AP mode.
-
Boot decision logic (
opt/opena3xx-provision/scripts/boot_selector.sh)- If provisioning is required, starts AP mode via
start_ap.sh. - Otherwise ensures client mode (stops
hostapd/dnsmasq, startsdhcpcd/wpa_supplicant).
- If provisioning is required, starts AP mode via
-
AP setup (
opt/opena3xx-provision/scripts/start_ap.sh)- Stops
wpa_supplicant/dhcpcd, configureswlan0as10.0.0.1/24. - Ensures
hostapdreads/etc/hostapd/hostapd.conf. - Restarts
dnsmasq,hostapd, andwifi-portal.service.
- Stops
-
Portal (
opt/opena3xx-provision/wifi_portal.pyandtemplates/index.html)- Serves a form on
/(port 80) to collectcountry,ssid, andpassword. - Writes
/etc/wpa_supplicant/wpa_supplicant.conf(mode 600), removes/boot/PROVISION_WIFIif present, and reboots.
- Serves a form on
-
Network services configuration
etc/hostapd/hostapd.conf: AP config (default SSIDOpenA3XX_Setup, passphraseopena3xx, countryMT).etc/dnsmasq.d/opena3xx-setup.conf: DHCP range10.0.0.2–10.0.0.50and DNS override to10.0.0.1.
opt/opena3xx-provision/scripts/start_ap.sh→/opt/opena3xx-provision/scripts/start_ap.shopt/opena3xx-provision/scripts/boot_selector.sh→/opt/opena3xx-provision/scripts/boot_selector.shopt/opena3xx-provision/wifi_portal.py→/opt/opena3xx-provision/wifi_portal.pyopt/opena3xx-provision/templates/index.html→/opt/opena3xx-provision/templates/index.htmlopt/opena3xx-provision/systemd/wifi-portal.service→/etc/systemd/system/wifi-portal.serviceopt/opena3xx-provision/systemd/opena3xx-boot.service→/etc/systemd/system/opena3xx-boot.serviceetc/hostapd/hostapd.conf→/etc/hostapd/hostapd.confetc/dnsmasq.d/opena3xx-setup.conf→/etc/dnsmasq.d/opena3xx-setup.conf
Installer actions:
- Installs required packages
- Appends
denyinterfaces wlan0to/etc/dhcpcd.conf(if not present) - Unmasks/enables
hostapd - Enables
wifi-portal.serviceandopena3xx-boot.service
Check status:
sudo systemctl status opena3xx-boot.service
sudo systemctl status wifi-portal.service
sudo systemctl status hostapd dnsmasqStart/stop/restart:
sudo systemctl restart wifi-portal.service
sudo systemctl restart hostapd dnsmasqLogs:
sudo journalctl -u opena3xx-boot.service -b | cat
sudo journalctl -u wifi-portal.service -b | cat- AP SSID/passphrase: Edit
/etc/hostapd/hostapd.conf(ssid,wpa_passphrase,country_code,channel). Restarthostapd. - DHCP/DNS: Edit
/etc/dnsmasq.d/opena3xx-setup.conf(IP range,address=/#/10.0.0.1). Restartdnsmasq. - Default country code: Change
default_countryintemplates/index.htmlor the fallback inwifi_portal.py. - Provision marker: The portal and boot logic use
/boot/PROVISION_WIFI. You can change this path in the scripts/python if desired. - Interface name: The scripts assume
wlan0. If your interface differs, update scripts/configs accordingly.
- Cannot see the AP:
- Ensure
hostapdis enabled and running; checksudo systemctl status hostapd. - Verify your wireless chipset supports AP mode with
nl80211.
- Ensure
- Connected to AP but no portal:
- Browse directly to
http://10.0.0.1. - Check
wifi-portal.servicestatus and logs.
- Browse directly to
- Device doesn’t join WiFi after submit:
- Verify
/etc/wpa_supplicant/wpa_supplicant.confcontents and permissions (600). - Confirm SSID/password and country code are correct.
- Recreate marker and retry:
sudo touch /boot/PROVISION_WIFI && sudo reboot.
- Verify
sudo systemctl disable --now wifi-portal.service opena3xx-boot.service
sudo rm -f /etc/systemd/system/wifi-portal.service /etc/systemd/system/opena3xx-boot.service
sudo rm -rf /opt/opena3xx-provision
sudo rm -f /etc/dnsmasq.d/opena3xx-setup.conf /etc/hostapd/hostapd.conf
sudo systemctl daemon-reload- Default AP credentials are public (
OpenA3XX_Setup/opena3xx). Change them for production. - The portal runs over HTTP in AP mode; use only on the isolated setup network.
- WPA passphrase is written to
wpa_supplicant.confwith restrictive permissions (600). Ensure the device remains physically secure.
install.sh: Installer scriptetc/hostapd/hostapd.conf: AP configurationetc/dnsmasq.d/opena3xx-setup.conf: DHCP/DNS captive-style configopt/opena3xx-provision/scripts/start_ap.sh: Sets up AP mode and restarts servicesopt/opena3xx-provision/scripts/boot_selector.sh: Chooses AP or client mode on bootopt/opena3xx-provision/wifi_portal.py: Flask app serving the portalopt/opena3xx-provision/templates/index.html: Portal UI templateopt/opena3xx-provision/systemd/*.service: Systemd service units
This project is licensed under CC0 1.0 Universal (Public Domain Dedication). See LICENSE for details.