How to use wifi module ESP-01/ESP8266 without the headache

Cody Exho
5 min readOct 31, 2022

--

Lately, I have been trying to integrate ESP32 with an Arduino UNO board to make a smart home system like a hydration unit for my home plants, etc. Here I will describe my journey and the main pain points on the way. And why it ended up using ESP8266 by NodeMCU.

So the flow I wanted to achieve:

  1. On the first start of the system, start the access point.
  2. Connect to AP and open a webpage asking you for your local network's SSID and password.
  3. After data is passed, the system should save SSID and password to the EEPROM, stop the access point and connect to the local wifi as a client.

ESP-01

When I started digging into this, I thought it would be an excellent idea for ESP-01. It's small, cheap, and has all the functionality I need.

ESP-01 can be controlled by Arduino using AT protocol via RX/TX pins.

But after digging awhile and getting along with UART AT commands, I’ve found out that your memory on the Arduino will vanish sooner than you would be able to finish your coding. ESP8266 has a lot more memory. For comparison, ESP8266 has 80kB of memory, and Arduino Uno has 2kB. It is like trying to control your PC from a calculator. This just doesn’t work.

Also, ESP-based controllers is a controller themselves have IO pins, and they can be used to solve your issues without the headache of trying to connect them to the Arduino. So when you ask yourself should I control my ESP from Arduino, don’t! Just don’t do that. That doesn’t mean to be this way. Hope this will prevent people from repeating my mistakes.

Below you will see the proper way to use ESP.

ESP-01

ESP8266 from NodeMCU

ESP-01 usage, along with the Arduino, has many issues described above !!!!!

On the contrary, ESP8266 from NodeMCU is an easy walk. It has an ESP module built-in and has good computing power. And there is no wiring needed.

Step 1. Setup.

First things first, we need to set up our Arduino IDE and OS to make it possible to work with the NodeMCU chip.

Install the CH340G driver to make the serial visible for your system.

  1. macOS
  2. Linux
  3. Windows

Go to Tools -> Boards -> Boards Manager. Install esp8266 board manager.

After that, we should go and install ESPAsyncTCP and ESPAsyncWebServer libraries. Clone repositories and remove "-master" in the names. Move those folders into the Arduino/libraries/ folder.

It's all set up to start coding now.

Step 2. Coding.

We are setting up the access point SSID/password.

const char* ssid     = "ESP8266-Access-Point";const char* password = "123456789";

The HTML page to return on server access:

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
html {
font-family: Arial;
display: inline-block;
margin: 0px auto;
text-align: center;
}
h2 {
font-size: 3.0rem;
}
p {
font-size: 3.0rem;
}
.units {
font-size: 1.2rem;
}
.input {
font-size: 20px;
margin-bottom: 10px;
}
.wifi-form {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
}
</style>
</head>
<body>
<div class="wifi-form">
<h1>Test</h1>
<input id="ssid" class="input" type="text" maxlength="32">
<input id="password" class="input" type="password">
<button onclick="connectToWifi()">Connect</button>
</div>
</body>
<script>
function connectToWifi() {
var ssid = document.getElementById("ssid").value;
var password = document.getElementById("password").value;
var xhr = new XMLHttpRequest(); xhr.open("POST", "/", true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(ssid + ":" + password);
}
</script>
</html>)rawliteral";

It's a simple form asking you for your SSID and password to your local wifi.

connectToWifi function will send the data back to the server POST request.

Let's set up the server that will handle those requests.

Add this as a global variable.

AsyncWebServer server(80);

Create an access point function.

void createAccessPoint() {
WiFi.softAP(ssid, password);
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
// Print ESP8266 Local IP Address
Serial.println(WiFi.localIP());
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send_P(200, "text/html", index_html);
});
server.on("/", HTTP_POST, [](AsyncWebServerRequest *request){},NULL, [](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
ssidWifi = "";
passwordWifi = "";
String res((char *)data);
int sepIndex = -1;
for (int i = 0; i < len; ++i) {
if (res[i] != ':') {
if (sepIndex == -1) {
ssidWifi.concat(res[i]);
}
if (i > sepIndex && sepIndex != -1) {
passwordWifi.concat(res[i]);
}
}
else {
sepIndex = i;
}
}
writeEEPROM(ssidWifi, ssidIndex);
writeEEPROM(passwordWifi, passwordIndex);
request->send(200, "text/plain", "SUCCESS");
});
server.begin();
}

Let's go through this.

WiFi.softAP(ssid, password);

This creates an access point with SSID and password passed and the beginning.

IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
// Print ESP8266 Local IP Address
Serial.println(WiFi.localIP());

Logging part. It is needed for debugging.

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send_P(200, "text/html", index_html);
});

Handles HTTP GET requests and returns our HTML page.

server.on("/", HTTP_POST, [](AsyncWebServerRequest *request){},NULL, [](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
ssidWifi = "";
passwordWifi = "";
String res((char *)data);
int sepIndex = -1;
for (int i = 0; i < len; ++i) {
if (res[i] != ':') {
if (sepIndex == -1) {
ssidWifi.concat(res[i]);
}
if (i > sepIndex && sepIndex != -1) {
passwordWifi.concat(res[i]);
}
}
else {
sepIndex = i;
}
}
writeEEPROM(ssidWifi, ssidIndex);
writeEEPROM(passwordWifi, passwordIndex);
request->send(200, "text/plain", "SUCCESS");
});

The above code handles the post request. We need to add the fifth parameter as an AsyncWebServer request to take the body. The algorithm is pretty simple. Clear saved ssidWifi and passwordWifi, which are global variables. Parse SSID:password in such format and save data to EEPROM.

Afterward, we start the server.

ssidIndex equals 0, and it is the start address of data.

passwordIndex equals 33 because SSID max length is 32 bytes, and one byte is reserved for the length of the string. You'll see it in the writeEEPROM function:

void writeEEPROM(String value, int address) {
int len = value.length();
EEPROM.put(address, len);
for (int i = address + 1; i < len + address + 1; ++i) {
EEPROM.put(i, value[i - address - 1]);
}
EEPROM.commit();
}

Now we need to be able to read that data from EEPROM:

String readEEPROM(int address) {
uint8_t len = EEPROM.read(address);
if (len == 255) return "";
String res;
for (int i = address + 1; i < len + address + 1; ++i) {
char c = (char)EEPROM.read(i);
res.concat(c);
}
return res;
}

255 -> means empty data. Here we're getting the first byte assigned to the data length that proceeds afterward, reads that data, and returns the result.

Now we have everything to wrap it all together.

void setup() {
Serial.begin(9600);
EEPROM.begin(128);
ssidWifi = readEEPROM(ssidIndex);
passwordWifi = readEEPROM(passwordIndex);
if (ssidWifi.length() > 0 && passwordWifi.length() > 0 && WiFi.status() != WL_CONNECTED) {
WiFi.softAPdisconnect(true);
server.end();
WiFi.begin(ssidWifi, passwordWifi);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
}
}
else
{
createAccessPoint();
}
}
void loop() {
if (ssidWifi.length() > 0 && passwordWifi.length() > 0 && WiFi.status() != WL_CONNECTED) {
WiFi.softAPdisconnect(true);
server.end();
WiFi.begin(ssidWifi, passwordWifi);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
}
}
}

That's it. We have a working system that starts a server with a webpage where you can set your wifi credentials without needing to hardcode that.

And the link to GitHub working example.

--

--