Enter WiFi Credentials on ESP32 with One Button

It is considered bad practice to hard-code WiFi credentials into Arduino sketches. Typical recommendations include the WiFiManager library for ESP8266, and SmartConfig for ESP32. Both require the user to have a smartphone to send command to the ESP chip, which otherwise does not have any User Interface (UI).

My ESP32 board is the Heltec WiFi_Kit_32 (paid link) that comes with a 0.96 inch OLED display and a little PRG button connected to pin #0. Can I use these minimal UI to enter WiFi credentials without involving a smartphone?

Heltec WiFi_Kit_32 OLED board with one button

Design with Minimal UI

I took inspiration from Prof Stephen Hawking's computer:

My main interface to the computer is through an open source program called ACAT, written by Intel. This provides a software keyboard on the screen. A cursor automatically scans across this keyboard by row or by column. I can select a character by moving my cheek to stop the cursor. My cheek movement is detected by an infrared switch that is mounted on my spectacles. This switch is my only interface with the computer.

My program has a similar design:

  1. The OLED displays a menu or keyboard.
  2. A cursor scans through this menu or keyboard.
  3. Pressing the button selects the menu item or character at the cursor.

How It Works

When the ESP32 starts, it scans for available WiFi networks, and displays a menu for these WiFi networks:

>hotspot
 +
 yoursunny.com
 attwifi
 freewifi
 AndroidAP
 xfinity
 FiOS

Pressing the button selects a WiFi networks. Then a "keyboard menu" appears:

>Weave-Loose-3
 <-
 0123456789 +._-
 ABCDEFGHIJKLM
 NOPQRSTUVWXYZ
 abcdefghijklm
 nopqrstuvwxyz
 ~!@#$%^&*()

The first option shows the current password, and selecting it confirms this password. The second option, <-, is a backspace that erases the last character. Six remaining options append a character from one of six categories.

Selecting an append action brings the user to the "append menu", where the user can press the button to select a character.

>! ) > {
 " * ? |
 # , @ }
 $ / [ ~
 % : \
 & ; ]
 ' < ^
 ( = `

To enter a WiFi password, you can repeatedly select an "append" option from the keyboard menu, and then select the character. If you make a mistake, you can erase the last character with the backspace option. After entering the complete password, select the first option to confirm.

Let's see it in action, shall we?

The big black button is savaged from a doorbell I made in high school. It is connected to pin #0, and has the same effect of pressing the PRG button on the board itself.

Code Talk

The complete Arduino sketch is available as a GIST and in a ZIP archive.

Let me explain the promptMenu function that displays a menu item:

/** \brief how long to pause on each menu item, in millis
 */
#define DELAY_CHOICE 250

/** \brief display a menu and prompt for a choice
 *  \param choices menu items
 *  \param number of menu items
 *  \return selected menu item index
 */
int
KeyInWifi::promptMenu(char const* const* choices, int nChoices)
{
  assert(nChoices > 0);                 // must have at least one choice
  assert(nChoices <= m_u8x8.getRows()); // code cannot handle more choices than display height

  m_u8x8.clear(); // clear the display
  for (int i = 0; i < nChoices; ++i) {
    // draw menu items at column 1, leaving column 0 for the cursor
    m_u8x8.drawString(1, i, choices[i]);
  }

  while (true) { // loop forever until a selection is made
    for (int i = 0; i < nChoices; ++i) { // loop through all menu items
      m_u8x8.drawGlyph(0, (i - 1 + nChoices) % nChoices, ' '); // clear last cursor
      m_u8x8.drawGlyph(0, i, '>'); // display current cursor
      for (int t = 0; t < DELAY_CHOICE; ++t) { // wait at current cursor position
        if (digitalRead(m_btnPin) != static_cast<int>(m_btnMode)) { // is the button down?
          return i; // if the button is down, a selection is made
        }
        delay(1);
      }
    }
  }
}