Toxicantidote ESP32 Wi-Fi selector
Advertisement
Advertisement

ESP32 Wi-Fi selector

May 2022

I 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.