ESP32 DIY Smarter Home – Captive WiFi Portal

Typically the WiFi credentials and other per device settings are hardcoded for each project and if those settings change, you will need to reprogram every device with the updated credentials.  For smaller one off projects that seems acceptable, but there is an even easier way: Juerd Waalboer’s ESP-WiFiSettings.

With this library, if the ESP32 can create a captive WiFi portal on startup if it fails to connect to a WiFi Access Point.  The key feature I love about it is secret credentials never need to touch the software (being instead stored in the SPIFFS on the ESP32).

From my testing an ESP32 configured with a Static IP address will connect to WiFi faster (yielding longer battery life for battery powered sensors). I slightly modified Juerd’s implementation to connect with a known IP, Gateway, and Netmask, if provided.

main.cpp:

#include <Arduino.h>
#include "meas.h"  //NOLINT(build/include_subdir)
#include <SPIFFS.h>
#include <WiFiSettings.h>

Meas meas;

void setup() {
  // put your setup code here, to run once:
  meas = Meas();
  String greeting = meas.get_hello() + "\n";
  printf("%s", greeting.c_str());

  SPIFFS.begin(true);  // On first run, will format after failing to mount
  //To reset stored credentials, run SPIFFS.format()
  //SPIFFS.format();
  // Use stored credentials to connect to your WiFi access point.
  // If no credentials are stored or if the access point is out of reach,
  // an access point will be started with a captive portal to configure WiFi.
  WiFiSettings.connect(true, 30);
  //WiFiSettings.portal();
  printf("Wifi Connect done %lu\n", millis());
  printf("Serial: %d\n", sn);
  printf("MQTT Host:%s:%d\n", host.c_str(), port);
}

void loop() {
  // put your main code here, to run repeatedly:
  printf("Mag: %d, time: %lu\n", meas.get_hall(), millis());
  delay(1000);
}

The platform.ini file has been updated to point to my GitHub fork of ESP-WiFiSettings that supports Static IP, but you can easily include the original ESP-WiFiSettings.

; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:ezsbc]
platform = espressif32
board = esp-wrover-kit
framework = arduino
monitor_speed = 115200
build_flags = -DCORE_DEBUG_LEVEL=5

lib_deps =
  ;ESP-WiFiSettings
  ; KeyOpt/ESP-WiFiSettings.git allows Static IP selection
  https://github.com/KeyOpt/ESP-WiFiSettings.git#master

With that library, the ESP32 will create a local hotspot that you can connect to and configure for your WiFi network which is shown here.

Continuous Integration

Setting up this project with Continuous Integration means that each time we perform a commit, all of the tests that we designed previously will be run.

And for each one of those tests, we can click on it and get a full console log showing exactly what was processed (and what went wrong):

Running with gitlab-runner 13.3.0-rc1 (669fc507)
  on docker-auto-scale ed2dce3a
section_start:1598319418:prepare_executor
Preparing the "docker+machine" executor
Using Docker executor with image python:latest ...
Pulling docker image python:latest ...
Using docker image sha256:79cc46abd78d048923350e8fb4af6cd47177d36606f8834a8e770b7eaf7e4452 for python:latest ...
section_end:1598319449:prepare_executor
section_start:1598319449:prepare_script
Preparing environment
Running on runner-ed2dce3a-project-20446038-concurrent-0 via runner-ed2dce3a-srm-1598319353-74fae3f9...
section_end:1598319454:prepare_script
section_start:1598319454:get_sources
Getting source from Git repository
$ eval "$CI_PRE_CLONE_SCRIPT"
Fetching changes with git depth set to 50...
Initialized empty Git repository in /builds/KeyOpt/esp32-sensor/.git/
Created fresh repository.
Checking out 380ffb51 as wifi_captive_portal...

Skipping Git submodules setup
section_end:1598319456:get_sources
section_start:1598319456:download_artifacts
Downloading artifacts
Downloading artifacts for cpplint (701658283)...
Downloading artifacts from coordinator... ok        id=701658283 responseStatus=200 OK token=aap4s6sn
Downloading artifacts for flawfinder (701658284)...
Downloading artifacts from coordinator... ok        id=701658284 responseStatus=200 OK token=y9CCe75_
Downloading artifacts for cppcheck (701658286)...
Downloading artifacts from coordinator... ok        id=701658286 responseStatus=200 OK token=fs8SM6ED
section_end:1598319457:download_artifacts
section_start:1598319457:step_script
Executing "step_script" stage of the job script
$ pip install -U platformio
Collecting platformio
  Downloading platformio-4.3.4.tar.gz (190 kB)
Collecting bottle<0.13
  Downloading bottle-0.12.18-py3-none-any.whl (89 kB)
Collecting click<8,>=5
  Downloading click-7.1.2-py2.py3-none-any.whl (82 kB)
Collecting colorama
  Downloading colorama-0.4.3-py2.py3-none-any.whl (15 kB)
Collecting pyserial!=3.3,<4,>=3
  Downloading pyserial-3.4-py2.py3-none-any.whl (193 kB)
Collecting requests<3,>=2.4.0
  Downloading requests-2.24.0-py2.py3-none-any.whl (61 kB)
Collecting semantic_version<3,>=2.8.1
  Downloading semantic_version-2.8.5-py2.py3-none-any.whl (15 kB)
Collecting tabulate<1,>=0.8.3
  Downloading tabulate-0.8.7-py3-none-any.whl (24 kB)
Collecting pyelftools<1,>=0.25
  Downloading pyelftools-0.26-py2.py3-none-any.whl (136 kB)
Collecting marshmallow>=2
  Downloading marshmallow-3.7.1-py2.py3-none-any.whl (45 kB)
Collecting urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1
  Downloading urllib3-1.25.10-py2.py3-none-any.whl (127 kB)
Collecting idna<3,>=2.5
  Downloading idna-2.10-py2.py3-none-any.whl (58 kB)
Collecting certifi>=2017.4.17
  Downloading certifi-2020.6.20-py2.py3-none-any.whl (156 kB)
Collecting chardet<4,>=3.0.2
  Downloading chardet-3.0.4-py2.py3-none-any.whl (133 kB)
Building wheels for collected packages: platformio
  Building wheel for platformio (setup.py): started
  Building wheel for platformio (setup.py): finished with status 'done'
  Created wheel for platformio: filename=platformio-4.3.4-py3-none-any.whl size=294313 sha256=aeace2d62cbb2b9a30ae916384366aebbd3e40280aa218ddcbc3030e5adc919b
  Stored in directory: /root/.cache/pip/wheels/22/76/4e/8451b93142686519520b707538b8ced937c1b7a3e79f369ef5
Successfully built platformio
Installing collected packages: bottle, click, colorama, pyserial, urllib3, idna, certifi, chardet, requests, semantic-version, tabulate, pyelftools, marshmallow, platformio
Successfully installed bottle-0.12.18 certifi-2020.6.20 chardet-3.0.4 click-7.1.2 colorama-0.4.3 idna-2.10 marshmallow-3.7.1 platformio-4.3.4 pyelftools-0.26 pyserial-3.4 requests-2.24.0 semantic-version-2.8.5 tabulate-0.8.7 urllib3-1.25.10
$ platformio run -e ezsbc
********************************************************************************
If you like PlatformIO, please:
- follow us on Twitter to stay up-to-date on the latest project news > https://twitter.com/PlatformIO_Org
- star it on GitHub > https://github.com/platformio/platformio
- try PlatformIO IDE for embedded development > https://platformio.org/platformio-ide
********************************************************************************

Processing ezsbc (platform: espressif32; board: esp-wrover-kit; framework: arduino)
--------------------------------------------------------------------------------
PlatformManager: Installing espressif32
Downloading...
Unpacking...
espressif32 @ 1.12.4 has been successfully installed!
The platform 'espressif32' has been successfully installed!
The rest of packages will be installed automatically depending on your build environment.
PackageManager: Installing toolchain-xtensa32 @ ~2.50200.0
Downloading...
Unpacking...
toolchain-xtensa32 @ 2.50200.80 has been successfully installed!
PackageManager: Installing framework-arduinoespressif32 @ ~3.10004.191002
Downloading...
Unpacking...
framework-arduinoespressif32 @ 3.10004.200129 has been successfully installed!
PackageManager: Installing tool-esptoolpy @ ~1.20600.0
Downloading...
Unpacking...
tool-esptoolpy @ 1.20600.0 has been successfully installed!
CorePackageManager: Installing tool-scons @ ~3.30102.0
Downloading...
Unpacking...
tool-scons @ 3.30102.0 has been successfully installed!
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/espressif32/esp-wrover-kit.html
PLATFORM: Espressif 32 1.12.4 > Espressif ESP-WROVER-KIT
HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash
DEBUG: Current (ftdi) On-board (ftdi) External (esp-prog, iot-bus-jtag, jlink, minimodule, olimex-arm-usb-ocd, olimex-arm-usb-ocd-h, olimex-arm-usb-tiny-h, olimex-jtag-tiny, tumpa)
PACKAGES: 
 - framework-arduinoespressif32 3.10004.200129 (1.0.4) 
 - tool-esptoolpy 1.20600.0 (2.6.0) 
 - toolchain-xtensa32 2.50200.80 (5.2.0)
LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
LibraryManager: Installing ESP-WiFiSettings
git version 2.20.1
Cloning into '/builds/KeyOpt/esp32-sensor/.pio/libdeps/ezsbc/_tmp_installing-btwywnhv-package'...
ESP-WiFiSettings @ ab6d62e has been successfully installed!
Found 28 compatible libraries
Scanning dependencies...
Dependency Graph
|-- <ESP-WiFiSettings> 3.3.1 #ab6d62e
|   |-- <DNSServer> 1.1.0
|   |   |-- <WiFi> 1.0
|   |-- <SPIFFS> 1.0
|   |   |-- <FS> 1.0
|   |-- <WebServer> 1.0
|   |   |-- <WiFi> 1.0
|   |   |-- <FS> 1.0
|   |-- <WiFi> 1.0
|-- <measurements>
|-- <SPIFFS> 1.0
|   |-- <FS> 1.0
Building in release mode
Compiling .pio/build/ezsbc/src/main.cpp.o
Generating partitions .pio/build/ezsbc/partitions.bin
Compiling .pio/build/ezsbc/lib789/WiFi/ETH.cpp.o
Compiling .pio/build/ezsbc/lib789/WiFi/WiFi.cpp.o
Compiling .pio/build/ezsbc/lib789/WiFi/WiFiAP.cpp.o
Compiling .pio/build/ezsbc/lib789/WiFi/WiFiClient.cpp.o
Compiling .pio/build/ezsbc/lib789/WiFi/WiFiGeneric.cpp.o
Compiling .pio/build/ezsbc/lib789/WiFi/WiFiMulti.cpp.o
Compiling .pio/build/ezsbc/lib789/WiFi/WiFiSTA.cpp.o
Compiling .pio/build/ezsbc/lib789/WiFi/WiFiScan.cpp.o
Compiling .pio/build/ezsbc/lib789/WiFi/WiFiServer.cpp.o
Compiling .pio/build/ezsbc/lib789/WiFi/WiFiUdp.cpp.o
Archiving .pio/build/ezsbc/lib789/libWiFi.a
Indexing .pio/build/ezsbc/lib789/libWiFi.a
Compiling .pio/build/ezsbc/lib43b/DNSServer/DNSServer.cpp.o
Archiving .pio/build/ezsbc/lib43b/libDNSServer.a
Indexing .pio/build/ezsbc/lib43b/libDNSServer.a
Compiling .pio/build/ezsbc/lib790/FS/FS.cpp.o
Compiling .pio/build/ezsbc/lib790/FS/vfs_api.cpp.o
Archiving .pio/build/ezsbc/lib790/libFS.a
Indexing .pio/build/ezsbc/lib790/libFS.a
Compiling .pio/build/ezsbc/lib117/SPIFFS/SPIFFS.cpp.o
Archiving .pio/build/ezsbc/lib117/libSPIFFS.a
Indexing .pio/build/ezsbc/lib117/libSPIFFS.a
Compiling .pio/build/ezsbc/lib5f9/WebServer/Parsing.cpp.o
Compiling .pio/build/ezsbc/lib5f9/WebServer/WebServer.cpp.o
Compiling .pio/build/ezsbc/lib5f9/WebServer/detail/mimetable.cpp.o
Archiving .pio/build/ezsbc/lib5f9/libWebServer.a
Indexing .pio/build/ezsbc/lib5f9/libWebServer.a
Compiling .pio/build/ezsbc/lib807/ESP-WiFiSettings/WiFiSettings.cpp.o
Archiving .pio/build/ezsbc/lib807/libESP-WiFiSettings.a
Indexing .pio/build/ezsbc/lib807/libESP-WiFiSettings.a
Compiling .pio/build/ezsbc/libf4c/measurements/meas.cpp.o
Archiving .pio/build/ezsbc/libf4c/libmeasurements.a
Indexing .pio/build/ezsbc/libf4c/libmeasurements.a
Archiving .pio/build/ezsbc/libFrameworkArduinoVariant.a
Indexing .pio/build/ezsbc/libFrameworkArduinoVariant.a
Compiling .pio/build/ezsbc/FrameworkArduino/Esp.cpp.o
Compiling .pio/build/ezsbc/FrameworkArduino/FunctionalInterrupt.cpp.o
Compiling .pio/build/ezsbc/FrameworkArduino/HardwareSerial.cpp.o
Compiling .pio/build/ezsbc/FrameworkArduino/IPAddress.cpp.o
Compiling .pio/build/ezsbc/FrameworkArduino/IPv6Address.cpp.o
Compiling .pio/build/ezsbc/FrameworkArduino/MD5Builder.cpp.o
Compiling .pio/build/ezsbc/FrameworkArduino/Print.cpp.o
Compiling .pio/build/ezsbc/FrameworkArduino/Stream.cpp.o
Compiling .pio/build/ezsbc/FrameworkArduino/StreamString.cpp.o
Compiling .pio/build/ezsbc/FrameworkArduino/WMath.cpp.o
Compiling .pio/build/ezsbc/FrameworkArduino/WString.cpp.o
Compiling .pio/build/ezsbc/FrameworkArduino/base64.cpp.o
Compiling .pio/build/ezsbc/FrameworkArduino/cbuf.cpp.o
Compiling .pio/build/ezsbc/FrameworkArduino/esp32-hal-adc.c.o
Compiling .pio/build/ezsbc/FrameworkArduino/esp32-hal-bt.c.o
Compiling .pio/build/ezsbc/FrameworkArduino/esp32-hal-cpu.c.o
Compiling .pio/build/ezsbc/FrameworkArduino/esp32-hal-dac.c.o
Compiling .pio/build/ezsbc/FrameworkArduino/esp32-hal-gpio.c.o
Compiling .pio/build/ezsbc/FrameworkArduino/esp32-hal-i2c.c.o
Compiling .pio/build/ezsbc/FrameworkArduino/esp32-hal-ledc.c.o
Compiling .pio/build/ezsbc/FrameworkArduino/esp32-hal-matrix.c.o
Compiling .pio/build/ezsbc/FrameworkArduino/esp32-hal-misc.c.o
Compiling .pio/build/ezsbc/FrameworkArduino/esp32-hal-psram.c.o
Compiling .pio/build/ezsbc/FrameworkArduino/esp32-hal-rmt.c.o
Compiling .pio/build/ezsbc/FrameworkArduino/esp32-hal-sigmadelta.c.o
Compiling .pio/build/ezsbc/FrameworkArduino/esp32-hal-spi.c.o
Compiling .pio/build/ezsbc/FrameworkArduino/esp32-hal-time.c.o
Compiling .pio/build/ezsbc/FrameworkArduino/esp32-hal-timer.c.o
Compiling .pio/build/ezsbc/FrameworkArduino/esp32-hal-touch.c.o
Compiling .pio/build/ezsbc/FrameworkArduino/esp32-hal-uart.c.o
Compiling .pio/build/ezsbc/FrameworkArduino/libb64/cdecode.c.o
Compiling .pio/build/ezsbc/FrameworkArduino/libb64/cencode.c.o
Compiling .pio/build/ezsbc/FrameworkArduino/main.cpp.o
Compiling .pio/build/ezsbc/FrameworkArduino/stdlib_noniso.c.o
Compiling .pio/build/ezsbc/FrameworkArduino/wiring_pulse.c.o
Compiling .pio/build/ezsbc/FrameworkArduino/wiring_shift.c.o
Archiving .pio/build/ezsbc/libFrameworkArduino.a
Indexing .pio/build/ezsbc/libFrameworkArduino.a
Linking .pio/build/ezsbc/firmware.elf
Building .pio/build/ezsbc/firmware.bin
esptool.py v2.6
Retrieving maximum program size .pio/build/ezsbc/firmware.elf
Checking size .pio/build/ezsbc/firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [=         ]  12.3% (used 40392 bytes from 327680 bytes)
Flash: [======    ]  60.2% (used 789004 bytes from 1310720 bytes)
========================= [SUCCESS] Took 46.90 seconds =========================
$ mv .pio/build/ezsbc/firmware.bin firmware_ezsbc.bin
section_end:1598319513:step_script
section_start:1598319513:upload_artifacts_on_success
Uploading artifacts for successful job
Uploading artifacts...
firmware_ezsbc.bin: found 1 matching files and directories 
Uploading artifacts as "archive" to coordinator... ok  id=701658287 responseStatus=201 Created token=BssDteFq
section_end:1598319515:upload_artifacts_on_success
Job succeeded

My main.cpp includes prompts for a MQTT server and port, which will be discussed in the next post.

The source code for this post can be found here.