ESP32 Wi-Fi selector
May 2022I have started getting in to programming and using the ESP32 from Espressif. They provide me an easy and cost effective way to add Wi-Fi functionality to my projects.
While the inbuilt functionality abstracts away some of the finer details of the connection, I could not find a built in system for reconfigurable connections. So, now I re-use and adapt the code segment below for various projects.
Via the (virtual) serial interface, this code will search for Wi-Fi networks, and allow the user to select a discovered network to connect to. On subsequent resets, it will attempt to reconnect to this network using the credentials saved. Once connected, it will print the network name and RSSI, as well as the ESP32 IP and MAC address. It is assumed that DHCP is available on whatever network this connects to.
#include <WiFi.h>
#include <Preferences.h>
#include <esp_task_wdt.h>
#define CMDPROMPT "ESP32> "
#define TIMEOUT 10000
// cmd line vars
char cmdBuffer[32];
int cmdBufferPosition = 0;
char cmdByte;
int cmdStatus = 0;
int echoInput = 1;
char command[32];
char argument1[32];
char argument2[32];
int wifiCount = 0;
bool connected = false;
bool countdown = false;
Preferences preferences;
String ssid;
String password;
void wifiScan(void);
void setup() {
Serial.begin(9600);
preferences.begin("wifi", false);
Serial.println("Booting..");
WiFi.mode(WIFI_STA);
WiFi.disconnect();
delay(100);
ssid = preferences.getString("ssid", "");
password = preferences.getString("password", "");
if (ssid == "") {
Serial.println("No network saved");
wifiScan();
} else {
countdown = true;
Serial.print("Saved network SSID is ");
Serial.println(ssid);
Serial.print("Auto-connect in ");
unsigned int end = millis() + TIMEOUT;
int last_count = 0;
while (countdown == true) {
unsigned int remaining = end - millis();
if ((remaining < 0) || (remaining > TIMEOUT)) {
break;
} else {
int count = (remaining / 1000);
if (count != last_count) {
last_count = count;
Serial.print(count);
Serial.print("..");
if (Serial.available() > 1) {
countdown = false;
}
}
}
}
if (countdown == true) {
Serial.println("Connecting...");
connect(ssid.c_str(), password.c_str());
} else {
Serial.println("Connection aborted");
wifiScan();
}
}
esp_task_wdt_init(10, true); //enable panic so ESP32 restarts
esp_task_wdt_add(NULL); //add current thread to WDT watch
}
void loop() {
esp_task_wdt_reset();
checkSerial();
if ((WiFi.status() != WL_CONNECTED) && (connected == true)) {
Serial.println("Lost connection to Wi-Fi. Reconnecting...");
WiFi.disconnect();
connected = false;
delay(100);
connect(ssid.c_str(), password.c_str());
}
}
void wifiScan() {
Serial.println("Searching for APs..");
wifiCount = WiFi.scanNetworks();
Serial.println("Networks found:");
for (int i = 0; i < wifiCount; i++) {
Serial.print("\t[");
Serial.print(i);
Serial.print("]\t(");
Serial.print(WiFi.BSSIDstr(i));
Serial.print(") ");
Serial.print(WiFi.SSID(i));
Serial.print(" [Signal ");
Serial.print(WiFi.RSSI(i));
Serial.println("dBm]");
delay(10);
}
}
void checkSerial() {
// command stuff
for (int i = 0; i < Serial.available() && cmdStatus == 0; i++) { // if serial data is available to read
cmdStatus = 0;
cmdByte = Serial.read(); // read it
if (cmdByte == 0x0A || cmdByte == 0x0D) { // newline and carriage return
Serial.flush(); // clear the input buffer
if (cmdBufferPosition == 0) { // if command is blank
resetBuffers(); // reset buffers
} else { // if the command is not blank
cmdStatus = 1; // flag the command as 'ready for processing'
}
} else if (cmdByte == 0x7F) { // backspace
if (cmdBufferPosition != 0) { // don't backspace further than the prompt
cmdBufferPosition--;
if (echoInput == 1) { // if echo is enabled, backspace on the client
Serial.print('\b');
}
}
} else { // other char, add to buffer
// restrict to printable characters
if (cmdByte >= 0x20 && cmdByte <= 0x7E) {
cmdBuffer[cmdBufferPosition] = cmdByte; // append to buffer
cmdBufferPosition++; // increment buffer position
if (echoInput == 1) { // if echo is enabled, echo the character back to the client
Serial.print(cmdByte);
}
}
}
}
if (cmdStatus == 1) { // cmd received, but not processed
// in case of backspace, truncate command buffer
for (int i = cmdBufferPosition; i <= strlen(cmdBuffer); i++) {
cmdBuffer[i] = NULL;
}
handleCommand(cmdBuffer); // process the command
resetBuffers(); // clear the command buffers
}
}
void connectIndex(int index, char *password) {
ssid = WiFi.SSID(index);
const char *ssid_chr = ssid.c_str();
connect(ssid_chr, password);
}
void connect(const char *ssid, const char *password) {
Serial.println("Connecting to network...");
unsigned long start = millis();
unsigned long duration = 0;
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
duration = millis() - start;
if (duration > TIMEOUT) {
Serial.println("WiFi: Connection failed. Is the password correct?");
break;
}
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("WiFi: Connected to network");
preferences.putString("ssid", ssid);
preferences.putString("password", password);
Serial.print("IP address is ");
Serial.println(WiFi.localIP());
connected = true;
} else {
connected = false;
}
}
// serial command interpreter
void handleCommand(char* cmdBuffer) { // handles commands
// init vars
char *command = NULL;
char *argument1 = NULL;
char *argument2 = NULL;
char *buf = cmdBuffer;
command = strtok(buf, " "); // first word (command, before space)
argument1 = strtok(NULL, " "); // second word (argument, after space)
argument2 = strtok(NULL, " "); // third word (argument, after space)
Serial.println(""); // newline
// process command
if (strcasecmp(command, "HELP") == 0) {
Serial.println("Command help:");
Serial.println("\tSCAN - Scan for networks");
Serial.println("\tCONNECT <#> - Connect to a listed network (passwords cannot have spaces)");
Serial.println("\tDISCONNECT - Disconnect from Wi-Fi");
Serial.println("\tFORGET - Forget saved Wi-Fi");
Serial.println("\tWSTATUS - Show current Wi-Fi status");
} else if (strcasecmp(command, "SCAN") == 0) {
wifiScan();
} else if (strcasecmp(command, "CONNECT") == 0) {
int index = atoi(argument1);
if (index <= wifiCount) {
connectIndex(index, argument2);
} else {
Serial.print("Invalid network. Select a number between 0 and ");
Serial.println(((int)wifiCount - 1));
}
} else if (strcasecmp(command, "DISCONNECT") == 0) {
if (connected == true) {
Serial.println("Disconnecting..");
connected = false;
WiFi.disconnect();
Serial.println("Disconnected!");
} else {
Serial.println("Not connected!");
}
} else if (strcasecmp(command, "FORGET") == 0) {
preferences.putString("ssid", "");
preferences.putString("password", "");
Serial.println("Saved network cleared");
} else if (strcasecmp(command, "WSTATUS") == 0) {
if (connected == false) {
Serial.println("Not connected to network");
} else {
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("RSSI: ");
Serial.print(WiFi.RSSI());
Serial.println("dBm");
Serial.print("Our IP: ");
Serial.println(WiFi.localIP());
Serial.print("Our MAC: ");
Serial.println(WiFi.macAddress());
}
} else {
Serial.println("Unknown command. Enter HELP for help.");
}
}
// reset the serial command buffers
void resetBuffers() { // resets command buffers and command state
cmdStatus = 0; // reset the processing state
cmdBufferPosition = 0; // reset the buffer position
for (int x = 0; x < 32; x++) { // clear the buffer
cmdBuffer[x] = NULL; // command buffer
command[x] = NULL; // command
argument1[x] = NULL; // command arguments
argument2[x] = NULL; // command arguments
}
if (echoInput == 1) { // if echo is enabled, print a prompt
Serial.println("");
Serial.print(CMDPROMPT);
}
}
As usual, this is also available via Github.