Skip to content

Instantly share code, notes, and snippets.

@yoursunny
Created January 15, 2018 15:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save yoursunny/b6e4e25ee85e82d0f6ee03cd2e7c3f47 to your computer and use it in GitHub Desktop.
Save yoursunny/b6e4e25ee85e82d0f6ee03cd2e7c3f47 to your computer and use it in GitHub Desktop.
Enter WiFi Credentials on ESP32 with One Button https://yoursunny.com/t/2018/ESP32-WiFi-credentials-one-button/
#include "KeyInWifi.hpp"
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <WiFi.h>
#include <WString.h>
#include <U8x8lib.h>
#define DELAY_CHOICE 250
#define DELAY_STEP 1500
KeyInWifi::KeyInWifi(U8X8& u8x8, int btnPin, ButtonMode btnMode)
: m_debug(nullptr)
, m_u8x8(u8x8)
, m_btnPin(btnPin)
, m_btnMode(btnMode)
{
}
void
KeyInWifi::enableDebug(Print& debug)
{
m_debug = &debug;
}
#define DEBUGF(fmt, ...) do { if (m_debug != nullptr) { m_debug->printf_P(PSTR("%d [KeyInWifi] " fmt "\n"), millis(), ##__VA_ARGS__); } } while (false)
bool
KeyInWifi::execute()
{
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
WiFi.disconnect();
switch (m_btnMode) {
case ButtonMode::NORMAL_LOW:
pinMode(m_btnPin, INPUT_PULLDOWN);
break;
case ButtonMode::NORMAL_HIGH:
pinMode(m_btnPin, INPUT_PULLUP);
break;
}
int nNetworks = WiFi.scanNetworks();
nNetworks = std::min(nNetworks, static_cast<int>(m_u8x8.getRows()));
std::vector<String> ssids;
ssids.resize(nNetworks);
std::vector<const char*> cssids;
cssids.resize(nNetworks);
for (int i = 0; i < nNetworks; ++i) {
ssids[i] = WiFi.SSID(i);
cssids[i] = ssids[i].c_str();
}
WiFi.scanDelete();
int ssidIndex = this->promptMenu(cssids.data(), nNetworks);
String ssid = ssids[ssidIndex];
ssids.clear();
cssids.clear();
m_u8x8.clear();
m_u8x8.drawString(0, 0, "SSID is");
m_u8x8.drawString(0, 1, ssid.c_str());
m_u8x8.drawString(0, 2, "Enter password");
delay(DELAY_STEP);
String password = this->promptString();
m_u8x8.clear();
m_u8x8.drawString(0, 0, "Connecting to");
m_u8x8.drawString(0, 1, ssid.c_str());
m_u8x8.drawString(0, 2, "using password");
m_u8x8.drawString(0, 3, password.c_str());
m_u8x8.drawString(0, 4, "status=");
WiFi.begin(ssid.c_str(), password.c_str());
while (!WiFi.isConnected()) {
m_u8x8.drawGlyph(8, 4, '0' + WiFi.status());
}
m_u8x8.drawString(0, 4, "IP=");
m_u8x8.drawString(4, 4, WiFi.localIP().toString().c_str());
return true;
}
int
KeyInWifi::promptMenu(char const* const* choices, int nChoices)
{
assert(nChoices > 0);
assert(nChoices <= m_u8x8.getRows());
m_u8x8.clear();
for (int i = 0; i < nChoices; ++i) {
m_u8x8.drawString(1, i, choices[i]);
}
while (true) {
for (int i = 0; i < nChoices; ++i) {
m_u8x8.drawGlyph(0, (i - 1 + nChoices) % nChoices, ' ');
m_u8x8.drawGlyph(0, i, '>');
for (int t = 0; t < DELAY_CHOICE; ++t) {
if (digitalRead(m_btnPin) != static_cast<int>(m_btnMode)) {
return i;
}
delay(1);
}
}
}
}
String
KeyInWifi::promptString()
{
String s;
while (true) {
char const* const choices[] = {
s.c_str(),
"<-",
"0123456789 +._-",
"ABCDEFGHIJKLM",
"NOPQRSTUVWXYZ",
"abcdefghijklm",
"nopqrstuvwxyz",
"~!@#$%^&*()",
};
int choice = this->promptMenu(choices, 8);
switch (choice) {
case 0:
return s;
case 1:
s.remove(s.length() - 1);
break;
case 2:
s += this->promptChar("0123456789 +._-");
break;
case 3:
s += this->promptChar("ABCDEFGHIJKLM");
break;
case 4:
s += this->promptChar("NOPQRSTUVWXYZ");
break;
case 5:
s += this->promptChar("abcdefghijklm");
break;
case 6:
s += this->promptChar("nopqrstuvwxyz");
break;
case 7:
s += this->promptChar("!\"#$%&'()*,/:;<=>?@[\\]^`{|}~");
}
}
}
char
KeyInWifi::promptChar(const char* chars)
{
int nChars = std::strlen(chars);
int nRows = m_u8x8.getRows();
int nCols = m_u8x8.getCols();
assert(nChars <= nRows * nCols / 2);
m_u8x8.clear();
for (int i = 0; i < nChars; ++i) {
std::div_t xy = std::div(i, nRows);
m_u8x8.drawGlyph(xy.quot * 2 + 1, xy.rem, chars[i]);
}
while (true) {
for (int i = 0; i < nChars; ++i) {
std::div_t xy = std::div((i - 1 + nChars) % nChars, nRows);
m_u8x8.drawGlyph(xy.quot * 2, xy.rem, ' ');
xy = std::div(i, nRows);
m_u8x8.drawGlyph(xy.quot * 2, xy.rem, '>');
for (int t = 0; t < DELAY_CHOICE; ++t) {
if (digitalRead(m_btnPin) != static_cast<int>(m_btnMode)) {
return chars[i];
}
delay(1);
}
}
}
}
#ifndef KEY_IN_WIFI_HPP
#define KEY_IN_WIFI_HPP
class String;
class Print;
class U8X8;
class KeyInWifi
{
public:
enum class ButtonMode {
NORMAL_LOW = 0,
NORMAL_HIGH = 1,
};
KeyInWifi(U8X8& u8x8, int btnPin, ButtonMode btnMode);
void
enableDebug(Print& debug);
bool
execute();
private:
int
promptMenu(char const* const* choices, int nChoices);
String
promptString();
char
promptChar(const char* chars);
private:
Print* m_debug;
U8X8& m_u8x8;
int m_btnPin;
ButtonMode m_btnMode;
};
#endif // KEY_IN_WIFI_HPP
// Enter WiFi Credentials on ESP32 with One Button
// https://yoursunny.com/t/2018/ESP32-WiFi-credentials-one-button/
#include <U8x8lib.h>
#include "KeyInWifi.hpp"
U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(15, 4, 16);
void
setup()
{
Serial.begin(115200);
Serial.println();
u8x8.begin();
u8x8.setFont(u8x8_font_victoriamedium8_r);
KeyInWifi kiw(u8x8, 0, KeyInWifi::ButtonMode::NORMAL_HIGH);
kiw.enableDebug(Serial);
kiw.execute();
}
void
loop()
{
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment