Introduction: WebServer: Arduino UNO With WiFi ESP01

About: Do you like technology? Follow my channel on Youtube and my Blog. In them I put videos every week of microcontrollers, arduinos, networks, among other subjects.

Here’s another video about Arduino UNO with the ESP8266, this time in the ESP01 version. This model I consider to be smaller and cheaper and, therefore, less powerful. In this project, we connect our ESP01, without using an AT command, in the Arduino Uno. Both of these have their INO source codes. We set this as an alternative to make a serial link, meaning that we use the codes for the possibility of including WiFiManager, Watchdog, among other features. In short: we will use an ESP8266 for connections while an Arduino Uno answers the requests through the serial.

Step 1: Assembly

In our automation scheme (which is quite simple), the 1.2K and 2.2K resistors at the top are to lower the voltage from 5V to around 3.3V. I stress that if you want to, you can connect several relay modules in this project. In this case, there can be up to 12. However, in this specific example, I used Led.

Step 2: How to Record

Here is the schematic of how to record the ESP01. If you have the adapter, I recommend using it.

In this image other image, you can see the adapter, which I advise the acquisition considering the facilities that it allows. You can see more information on how to record the ESP01 in this video: Recording on ESP01.

Step 3: Demonstration

Step 4: ESP8266.ino

Let's include the lib ESP8266WiFi.h and work with the network parameters. We continue to define the connection timeout and the server object, in addition to declaring the buffer.

#include <ESP8266WiFi.h>
//Troque pelos dados da sua rede const char* ssid = "SSID"; const char* password = "12345678"; const char* ip = ""; //Timeout da conexão #define TIMEOUT 500 #define MAX_BUFFER 300 //Server na porta 80 (padrão http) WiFiServer server(80); //Buffer onde serão gravados os bytes da comunicação uint8_t buffer[MAX_BUFFER];

ESP8266.ino - Setup
My serial communication with the Arduino is 115200, which I consider to be fast. We send information from the network to connect, and then wait for the connection with the access point. We configure the IP and initialize the server. In this part of the code, we use the Disconnect function, because it can zero all my internal communication variables. I say this because it has situations where this action is quite necessary.

void setup() {
Serial.begin(115200); //Envia a informação da rede para conectar WiFi.disconnect(); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); //Espera a conexão com o access point while (WiFi.status() != WL_CONNECTED) { delay(500); } //Configura o IP IPAddress ipAddress; ipAddress.fromString(ip); WiFi.config(ipAddress, WiFi.gatewayIP(), WiFi.subnetMask()); //Inicializa o server server.begin(); }

ESP8266.ino - 1/4 loop

Check if the client has been connected. If no one connected, we just return without doing anything. In the case of a connection, it marks the time the client connected, and the number of bytes read.

void loop() {
//Verifica se alguem se conectou WiFiClient client = server.available(); if (!client) { //Se ninguém conectou apenas retorna sem fazer nada return; } //Marca o tempo que o cliente se conectou e a quantidade //de bytes lidos uint32_t connectedTime = millis(); int bytesRead = 0;

ESP8266.ino - loop 2/4

We continue to harvest the client connection data. If the time passes the maximum time without reading any byte, we close the connection.

//Enquanto o cliente estiver conectado
while(client.connected()) { //Tempo agora uint32_t now = millis(); //Quanto tempo passou desde a conexão com o cliente uint32_t ellapsed = now - connectedTime; //Se o tempo passou do tempo máximo e não leu nenhum byte if(ellapsed > TIMEOUT && bytesRead == 0) { //Fecha a conexão com o cliente client.stop(); break; }

ESP8266.ino - 3/4 loop

We verify if the client has bytes to be read. We send the bytes by serial, and we increase the byte counter read.

int available = client.available();
//Se o cliente possui bytes a serem lidos if(available) { int bufferSize = available < MAX_BUFFER ? available : MAX_BUFFER; int readCount =, bufferSize); //Envia os bytes pela serial e aumenta o contador de bytes lidos Serial.write(buffer, readCount); Serial.flush(); bytesRead += readCount; }

ESP8266.ino - loop 4/4

We check here if the serial has bytes to be read from UNO to ESP01. We also analyze if it is the byte that defines the termination of the connection. We send what has not yet been sent, and wait for a time for the client to receive it. We send the bytes to the client and close the connection.

available = Serial.available();
//Se a serial possui bytes a serem lidos if(available) { int bufferSize = available < MAX_BUFFER ? available : MAX_BUFFER; //Lê os bytes Serial.readBytes(buffer, bufferSize); //Se for o byte que define a finalização da conexão if(buffer[bufferSize-1] == 127) { client.write(buffer, bufferSize-1); //Envia o que ainda não tenha sido enviado client.flush(); //Espera um tempo para o cliente receber delay(100); //Fecha a conexão com o cliente e sai do 'while' client.stop(); break; } //Envia os bytes para o cliente client.write(buffer, bufferSize); } }//while(client.connected()) }//loop

Step 5: Arduino.ino

Here we define the pin where the first relay is, and how many pins will be used. Finally, we initialize the pins.

#define FIRST_PIN 2 //Pino onde está o primeiro relê

#define PINS_COUNT 12 //Quantos pinos serão utilizados
//Mantém o estado atual dos pinos (HIGH ou LOW) int pinsStatus[PINS_COUNT]; void setup() { Serial.begin(115200); //Inicializa os pinos setupPins(); }

Arduino.ino - setupPins

In this step, we point out the pins that are connected with the relays as outputs.

void setupPins()
{ //Coloca os pinos que estão ligados os relês como saída for(int i=0; i

Arduino.ino - loop

We check here if there is a new customer. We execute the reading of the request and, if the request is not for the favicon, we execute the action with the value passed in the request. We then send the customer response.

void loop()
{ //Verifica se há um novo cliente if (Serial.available()) { //Faz a leitura da requisição char* request = readRequest(); //Se a requisição não for para o favicon if(strstr(request, "favicon") == NULL) { //Executamos a ação com o valor passado na requisição execute(getAction(request), getValue(request)); //Envia a resposta ao cliente sendResponse(); } else { Serial.print( "HTTP/1.1 404 Not Found\r\n" "Content-Length: 0\r\n" "Connection: close\r\n" "\r\n"); } Serial.write(127); } }

Arduino.ino - readRequest 1/2

We set out to read the first line of the requisition. Remembering that at this stage, only the first line of the request interests us.

//Faz a leitura da primeira linha da requisição
char* readRequest() { bool currentLineIsBlank = true; char request[50]; int i = 0; bool firstLine = true; while (true){ while(!Serial.available()); char c =; //Apenas a primeira linha da requisição nos interessa if(firstLine){ request[i] = c; i++; }

Arduino.ino - readRequest 2/2

We show here that the last line of the request is \ r \ n alone, after the previous line \ r \ n.

If any character other than \ n and \ r has been read, the line is not blank.

if (c == '\n'){
//A última linha da requisição é um \r\n sozinho, após o \r\n da linha anterior if(currentLineIsBlank){ //Se chegou aqui é porque a requisição foi lida por completo break; } currentLineIsBlank = true; firstLine = false; } else if (c != '\r'){ //Se leu qualquer caracter que não for \n e \r significa que a linha não está em branco currentLineIsBlank = false; } } return request; }

Arduino.ino - sendResponse

Observe in this part the function that sends the HTML to the client.

//Envia o HTML para o cliente
void sendResponse() { //Envia o cabeçalho HTTP Serial.print( "HTTP/1.0 200 OK\r\n" "Content-Type: text/html; charset=UTF-8\r\n" "Connection: close\r\n" "\r\n"); Serial.println(""); Serial.println(" "); head();//Envia o cabeçalho do HTML body();//Envia o corpo do HTML Serial.println("

"); }

Arduino.ino - head

Already, in this part, we sent the CSS to modify the appearance of the page.

//Envia o CSS para modificar a aparência da página
void head() { Serial.println(F("<head>"

"<meta name='viewport' content='width=device-width, initial-scale=1.0'>"


"text-align: center;" "font-family: sans-serif;" "font-size: 14px;" "}" "p{" "color:#555;" "font-size: 12px;" "}" ".button{" "outline: none;" "display: block;" "border: 1px solid #555;" "border-radius:18px;" "width: 150px;" "height: 30px;" "margin: 10px;" "margin-left: auto;" "margin-right: auto;" "cursor: pointer;" "}" ".button_off{" "background-color:#FFF;" "color: #555;" "}" ".button_on{" "background-color:#2C5;" "color: #fff;" "}" "</style>"



Arduino.ino - body

We create the body and the buttons by creating a button for each pin that has a relay.

//Cria o body e os botões
void body() { Serial.println("<body>"); String buttons = ""; //Cria um botão para cada pino que possui um relê for(int i=0; i<PINS_COUNT; i++)

buttons.concat(button(i)); } Serial.println(buttons); Serial.println("</body>");


Arduino.ino - button

We create a button with the appearance and action corresponding to the current state of the relay.

//Cria um botão com a aparência e ação correspondente ao estado atual do relê
String button(int number) { String label = String(number + 1); String className = "button "; className += pinsStatus[number] == HIGH ? "button_on" : "button_off"; String action = pinsStatus[number] == HIGH ? "off" : "on"; return "<buttonclass=\"" + className + "\"onclick=\"location.href='?" + action + "=" + label + "'\">" + label + "</button>";


Arduino.ino - getAction getValue

We return the action that the client wishes to execute (on/off), as well as the value (relay number) that the action will execute.

//Retorna a ação que o cliente deseja executar (on off)
String getAction(char *request) { return getStringBetween(request, '?', '='); } //Retorna o valor (numero do relê) que a ação será executada String getValue(char *request) { return getStringBetween(request, '=', ' '); }

Arduino.ino - getStringBetween

We return the string between the first character "start" and "end". We also return the memory address of the "start" character.

//Retorna a string que fica entre o primeiro caractere 'start' e o primeiro caractere 'end'
String getStringBetween(char* input, char start, char end) { String str = ""; //retorna o endereço de memória do caractere 'start' char* c = strchr(input, start); //Se não achou o caractere if(c == NULL) { return ""; } //Vai para o próximo caractere c++; //Enquanto não chegar ao caractere 'end' ou ao final da string while(*c != end && *c!='\0') { str += *c; c++; } return str; }

Arduino.ino - execute

Finally, we execute the action next to the value (relay number). We check if it is one of the two actions we expect (On/Off), and we number from 1. However, the array starts from 0. Then, we take out 1. The pin number, therefore, will be the index plus the pin number where the relays begin. You must remember that the relays need to be in sequence from the starting pin (FIRST_PIN).

//Executada a ação junto ao valor (número do relê)
void execute(String action, String value) { //Se é uma das duas ações que esperamos if(action == "on" || action == "off") { //Os relês são numerados a partir do 1, max o array começa do 0 //então tiramos 1 int index = value.toInt() - 1; //O número do pino será o índice mais o número do pino onde os relês //começam. Os relês devem estar em sequência a partir do pino inicial (FIRST_PIN) int pinNumber = FIRST_PIN + index; int status = action == "on" ? HIGH : LOW; digitalWrite(pinNumber, status); pinsStatus[index] = status; } }

"         "body{"             "text-align: center;"             "font-family: sans-serif;"             "font-size: 14px;"         "}"         "p{"             "color:#555;"             "font-size: 12px;"         "}"         ".button{"             "outline: none;"             "display: block;"             "border: 1px solid #555;"             "border-radius:18px;"             "width: 150px;"             "height: 30px;"             "margin: 10px;"             "margin-left: auto;"             "margin-right: auto;"             "cursor: pointer;"         "}"         ".button_off{"             "background-color:#FFF;"             "color: #555;"         "}"         ".button_on{"             "background-color:#2C5;"             "color: #fff;"         "}"     "

Step 6: Files

Download the files