Menu
Blog Smart Kits
  • Home
  • Arduino
  • ESP32
  • IoT
  • Raspberry Pi
  • Loja Virtual
Blog Smart Kits

ESP8266 – Salvando Credenciais Wi-Fi na EEPROM Através de um Access Point (AP)

Início » ESP8266 – Salvando Credenciais Wi-Fi na EEPROM Através de um Access Point (AP)

Posted on 3 de novembro de 202120 de dezembro de 2021

Neste tutorial vamos mostrar como conectar seu ESP8266 a qualquer rede Wi-Fi de sua escolha sem a necessidade de alterar seu código cada vez que precisar utilizar uma rede diferente. Isso será realizado através da criação de um Ponto de Acesso que exibirá um formulário HTML ao usuário conectado à placa, seja por um computador ou smartphone.

Após a inserção das credenciais de sua rede, a placa ganhará acesso à internet e salvará os dados em sua memória interna para que não haja necessidade de o usuário digitar a senha da rede toda vez que ligar a placa.

Esta função pode ser reaproveitada para tentar uma reconexão à rede caso a placa perca acesso à internet.

Componentes necessários

Para seguir este tutorial, você precisará dos seguintes itens:

  • 1x Módulo WiFi ESP8266 NodeMcu V3.

A programação do NodeMCU será feita através da IDE do Arduino. Caso não saiba como configurar a IDE para programar módulos baseados no ESP8266, recomendamos primeiro ler o seguinte tutorial:

ESP8266 – Como Programar o NodeMCU Através da Arduino IDE

ESP8266 Como Access Point

Vamos iniciar nosso projeto criando um Ponto de Acesso (AP) para nosso NodeMCU. Isso significa que a placa possuirá uma rede própria à qual o usuário poderá se conectar.

Começaremos com uma chamada à biblioteca de Wi-Fi do ESP8266:

Arduino
1
#include <ESP8266WiFi.h>

Agora vamos definir um nome (SSID) e uma senha para nosso AP. Crie uma variável global para cada dado para facilitar sua alteração caso julgue necessário no futuro:

Arduino
1
2
const char *ssid = "ESP8266 Access Point";
const char *password = "smartkitsAP";

Os valores das strings acima podem ser alterados para qualquer valor de sua escolha.

Dentro do setup(), vamos inicializar a comunicação serial de nosso NodeMCU com o computador para que possamos imprimir dados da rede no monitor serial. Usaremos um baud rate (velocidade de transmissão de informação) de 115200:

Arduino
1
Serial.begin(115200);

Ainda no setup(), inicialize o AP utilizando as variáveis criadas anteriormente:

Arduino
1
WiFi.softAP(ssid, password);

E imprima o IP de seu AP:

Arduino
1
Serial.println(WiFi.softAPIP());

É importante saber qual endereço de IP sua placa está usando, pois você se comunicará com a placa através deste endereço. Por padrão, o ESP8266 possui o IP 192.168.4.1, mas é possível alterar este valor através de funções de configuração de AP.

Imagem 1 – IP e nome do AP

Imagem 1 – IP e nome do AP

ESP8266 Como Web Server

Após criarmos o AP de nosso ESP8266, precisamos que ele seja capaz de receber as credenciais da rede a qual você quer conectar a placa para que ela tenha acesso à internet. Logo, ele passará a agir como um servidor Web que receberá requisições do usuário, realizará um processamento delas e enviará respostas a partir de funções que criaremos em nosso código.

No topo de seu código, adicione a chamada:

Arduino
1
#include <ESP8266WebServer.h>

Esta biblioteca é utilizada para criar servidores HTTP síncronos, onde o usuário vai interagir com uma interface Web através de um navegador, o navegador enviará requisições ao servidor e o servidor vai então responder exibindo no navegador uma nova página ao usuário. Este tipo de servidor suporta um único usuário por vez.

Antes de seguirmos para as configurações do servidor, devemos instanciar um objeto da classe como variável global, passando como argumento a porta HTTP padrão de número 80:

Arduino
1
ESP8266WebServer server(80);

Dentro da função setup(), vamos agora configurar o servidor para receber requisições do usuário e direcioná-las a funções que criaremos mais à frente. Isso é realizado através do método on(URI, handlerFunction) do objeto que acabamos de criar. Este método possui dois parâmetros, sendo o primeiro a URI (parte do endereço de link que ficará após o IP do servidor) e o segundo o nome da função associada ao endereço.

A primeira função que criaremos ficará responsável pela raiz de nosso servidor. Será a primeira operação realizada quando o usuário digitar o IP da placa no navegador. Chamaremos esta função de handleRoot():

1
server.on("/", handleRoot);

A segunda função será responsável por conectar a placa a uma rede Wi-Fi a partir dos dados digitados pelo usuário em um formulário que criaremos mais à frente. Vamos chamá-la de handleForm() e sua URI será /action_new_connection:

Arduino
1
server.on("/action_new_connection", handleForm);

Em seguida, teremos uma função para que a placa possa se conectar a partir de informações salvas anteriormente em sua memória interna. Ele se chamará connectEeprom() e sua URI será /action_previous_connection:

Arduino
1
server.on("/action_previous_connection", connectEeprom);

Com todas as funções do servidor declaradas, vamos inicia-lo:

Arduino
1
server.begin();

Por último, agora dentro da função loop(), devemos chamar o método handleClient() para que seja possível processar as requisições do usuário:

Arduino
1
server.handleClient();

HTML

Para exibir uma interface de conexão ao usuário, vamos precisar criar uma página HTML. No NodeMCU podemos fazer isso de duas maneiras: a primeira é fazendo upload de um arquivo HTML para a placa (o que exige configuração adicional da IDE do Arduino) e a segunda é criando uma String que receberá todo o código de sua página HTML. Neste tutorial vamos optar pela segunda.

Nossa interface terá apenas um formulário simples através do qual o usuário enviará as credenciais da rede Wi-Fi para o ESP8266. Ele possuirá três estruturas básicas:

  • Um <select>, uma espécie de dropdown menu que exibirá todas as redes ao alcance do NodeMCU;
  • Um <input> do tipo password, que nada mais é do que uma caixa de texto onde as informações digitadas pelo usuário são convertidas em asteriscos;
  • Dois botões, sendo um deles um <input> do tipo button e outro um <button> contendo um link.

Ao clicar no <input> – de nome “Conectar” – será chamada uma função JavaScript que fará a validação dos dados antes de enviá-los para o ESP8266. Caso o usuário deixe um dos campos vazios, será exibida uma mensagem de erro como mostrada na imagem abaixo:

Imagem 2 – Caso um dos campos esteja vazio, é exibida uma mensagem de erro

Imagem 2 – Caso um dos campos esteja vazio, é exibida uma mensagem de erro

O segundo botão – uma tag <button> – visto na imagem servirá para conectar o NodeMCU a partir de dados previamente salvos em sua memória interna. Veremos mais à frente como ele funcionará.

Nosso HTML será inserido no código através de uma variável global do tipo vetor de caracteres que será armazenada e lida diretamente da memória de programa (Flash) do NodeMCU em vez da SRAM, onde seria normalmente armazenada. Isso é indicado pelo modificador PROGMEM. Faremos isso para que exista sempre uma cópia inalterada da página disponível no nosso programa sem ocupar o espaço dedicado à sua execução.

Arduino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
const char MAIN_page[] PROGMEM = R"=====(
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>HTML Form ESP8266 - SmartKits</title>
  <style>
    body {color: #434343; font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; font-size: 14px; background-color: #eeeeee; margin-top: 100px;}
    .container {margin: 0 auto; max-width: 400px; padding: 30px; box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23); background-color: #ffffff; border-radius: 10px;}
  h2 {text-align: center; margin-bottom: 20px; margin-top: 0px; color: #0ee6b1; font-size: 35px;}
  #titleGreen {color: #00E1AA;}
  #titleBlack {color: #000000;}
    h3 {text-align: center; margin-bottom: 40px; margin-top: 0px; color: #336859; font-size: 35px;}
    form .field-group {box-sizing: border-box; clear: both; padding: 4px 0; position: relative; margin: 1px 0; width: 100%;}
    .text-field {font-size: 15px; margin-bottom: 4%; -webkit-appearance: none; display: block; background: #fafafa; color: #636363; width: 100%; padding: 15px 0px 15px 0px; text-indent: 10px; border-radius: 5px; border: 1px solid #e6e6e6; background-color: transparent;}
    .text-field:focus {border-color: #00bcd4; outline: 0;}
    .button-container {box-sizing: border-box; clear: both; margin: 1px 0 0; padding: 4px 0; position: relative; width: 100%;}
    .button {background: #00E1AA; border: none; border-radius: 5px; color: #ffffff; cursor: pointer; display: block; font-weight: bold; font-size: 16px; padding: 15px 0; text-align: center; text-transform: uppercase; width: 100%; -webkit-transition: background 250ms ease; -moz-transition: background 250ms ease; -o-transition: background 250ms ease; transition: background 250ms ease;}
    p {text-align: center; text-decoration: none; color: #87c1d3; font-size: 18px;}
    a {text-decoration: none; color: #ffffff; margin-top: 0%;}
    #status {text-align: center; text-decoration: none; color: #336859; font-size: 14px;}
  </style>
  <script>
  function validateForm() {
    var ssid = document.forms["myForm"]["ssid"].value;
    var password = document.forms["myForm"]["password"].value;
    var status = document.getElementById("statusDiv");
    if (ssid == "" && password == "") {
    status.innerHTML = "<p id='status' style='color:red;'>Insira SSID e senha.</p>";
    return false;
    }
    else if (ssid == "") {
    status.innerHTML = "<p id='status' style='color:red;'>Insira o SSID.</p>";
    return false;
    }
    else if (password == "") {
    status.innerHTML = "<p id='status' style='color:red;'>Insira a senha.</p>";
    return false;
    }
    else {
    status.innerHTML = "<p id='status'>Conectando...</p>";
    return true;
    }
  }
  </script>
</head>
<body>
<div class="container">
  <h2><span id="titleGreen">smart</span><span id="titleBlack">kits</span></h2>
  <h3>Conexão ESP8266</h3>
  <form name="myForm" action="/action_new_connection" onsubmit="return validateForm()" method="post">
    <div class="field-group">
  <select class='text-field' name='ssid'></select>
    </div>
    <br>
    <div class="field-group">
    <input class="text-field" type="password" name="password" length=64 placeholder="Password">
    </div>
  <br>
  <div id="statusDiv">
    <br><br>
  </div>
    <div class="button-container">
    <input class="button" type="submit" value="Conectar">
    </div>
  </form>
  <p>OU</p>
  <div class="button-container">
    <button class="button" type="button" onclick="window.location.href='/action_previous_connection'">Conectar à última rede utilizada</button>
  </div>
</div>
</body>
</html>
)=====";

Note que nosso código CSS está embutido na tag <head> de nosso HTML para melhorar a aparência da página.

Processando Requisições no ESP8266

Vamos agora criar as funções handleRoot() e handleForm() que definimos anteriormente para o nosso web server.

O propósito desta primeira será apenas exibir nossa interface de conexão para o usuário. Porém, antes de fazer isso devemos buscar por todas as redes Wi-Fi ao alcance do NodeMCU para que nosso <select> não fique vazio no HTML. Será então criada uma função que chamaremos de listSSID() para realizar essa tarefa.

Listando Redes Wi-Fi Próximas

Começaremos recuperando nosso HTML da memória Flash, pois vamos precisar edita-lo:

Arduino
1
String index = (const __FlashStringHelper*) MAIN_page;

Em seguida, chamaremos a função WiFi.scanNetworks() que nos retornará a quantidade de redes disponíveis. Vamos armazenar este valor em uma variável do tipo int:

Arduino
1
int n = WiFi.scanNetworks();

Esta operação também armazena dentro do objeto WiFi informações como SSID e potência de sinal de todas as redes encontradas.

Agora que temos a quantidade de redes próximas e suas informações, podemos chamar o método WiFi.SSID() dentro de um laço de repetição para adicionar o nome de todas as redes ao nosso HTML. Este método recebe como argumento o índice de cada uma das redes dentro de uma lista criada dentro do objeto.

Aplicaremos o método replace() na String que contém nosso HTML. Para preencher o <select> com os SSIDs das redes.

Nossa função listSSID() ficará assim:

Arduino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
String listSSID() {
  String index = (const __FlashStringHelper*) MAIN_page; //Lê a página HTML
  String networks = "";
  int n = WiFi.scanNetworks();
  Serial.println("Scan done.");
  if (n == 0) {
    Serial.println("Nenhuma rede foi encontrada.");
    index.replace("<select class='text-field' name='ssid'></select>", "<select class='text-field' name='ssid'><option value='' disabled selected>Nenhuma rede encontrada</option></select>");
    index.replace("<br><br>", "<p id='status' style='color:red;'>Rede não encontrada.</p>");
    return index;
  }
  else {
    Serial.printf("%d networks found.\n", n);
    networks += "<select class='text-field' name='ssid'><option value='' disabled selected>SSID</option>";
    // Pega o SSID de cada rede encontrada
    for (int i = 0; i < n; ++i)
    {
      networks += "<option value='" + WiFi.SSID(i) + "'>" + WiFi.SSID(i) + "</option>";
    }
    networks += "</select>";
  }
  index.replace("<select class='text-field' name='ssid'></select>", networks);
  return index;
}

Dentro da função handleRoot(), faremos uma chamada à listSSID() armazenando seu resultado em uma String que será então enviada para o usuário. Para isso, é usado o método send() de nosso servidor. Ele possui três parâmetros: status message, tipo de conteúdo e por último o conteúdo em si – no caso, nossa String.

Arduino
1
2
3
4
void handleRoot() {
String index = listSSID(); //Recebe página HTML editada
server.send(200, "text/html", index); //Envia página web ao usuário
}

Recebendo Dados do Formulário

Agora vamos criar a função handleForm() que processará os dados informados pelo usuário.

Voltando ao nosso HTML, vemos que tanto nosso <select> quanto nosso <input> do tipo “password” possuem atributos chamados “name” (“nome” em inglês).

XHTML
1
<select class='text-field' name='ssid'></select>

XHTML
1
<input class="text-field" type="password" name="password" length=64 placeholder="Password">

Estes “nomes” são utilizados para identificar cada campo do formulário para que seus valores possam ser acessados pelo NodeMCU através do método arg() de nosso servidor. Vamos armazenar estes valores em variáveis, nos certificar de que os campos não estão vazios e então enviar os dados para uma nova função que chamaremos de connectToWiFi().

Arduino
1
2
3
4
5
6
7
8
void handleForm() {
String ssidWifi = server.arg("ssid");
String passwordWifi = server.arg("password");
 
if(!ssidWifi.equals("") && !passwordWifi.equals("")) {
  connectToWiFi(ssidWifi, passwordWifi);
}
}

Antes de seguirmos para esta função, vamos criar no topo do código – abaixo da inclusão de bibliotecas – uma macro que corresponderá ao LED embutido na placa, conectado ao pino D4:

Arduino
1
#define ONBOARD_LED D4

Conectando à Rede Wi-Fi

Dentro de connectToWiFi(), passaremos as credenciais da rede para o método WiFi.begin(), que é responsável por conectar o NodeMCU à rede Wi-Fi.

Vamos então criar um laço de repetição que vai esperar pelo resultado desta operação, verificado pelo método WiFi.status(). Caso a conexão ocorra normalmente, vamos acender o LED, exibir o endereço IP do dispositivo no monitor serial e enviar uma nova página ao usuário contendo uma mensagem de sucesso. Caso contrário, será enviada uma página com uma mensagem de erro.

Arduino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void connectToWiFi(String ssidWifi, String passwordWifi) {
  int count = 0;
  WiFi.begin(ssidWifi.c_str(), passwordWifi.c_str());     //Conecta ao seu roteador Wi-Fi
 
  // Espera pela conexão
  while ( count < 15 ) {
    delay(500);
    if (WiFi.status() == WL_CONNECTED) {
      salvarEeprom(ssidWifi, passwordWifi);
      //Se a conexão acontecer, mostre o endereço IP no monitor serial
      Serial.println("Conectado ao WiFi");
      Serial.print("Endereço IP: ");
      Serial.println(WiFi.localIP());  //Endereço IP atribuído a seu ESP
      digitalWrite(ONBOARD_LED, LOW); //Ativa o LED da placa
      String responsePage = (const __FlashStringHelper*) MAIN_page; //Lê conteúdo do HTML
      responsePage.replace("<br><br>", "<p id='status'>Conectado!</p>"); //Aciona mensagem de sucesso
      server.send(200, "text/html", responsePage); //Envia a página web
      return;
    }
    else if (WiFi.status() == WL_CONNECT_FAILED) {
      String responsePage = (const __FlashStringHelper*) MAIN_page; //Lê conteúdo do HTML
      responsePage.replace("<br><br>", "<p id='status' style='color:red;'>Falha na conexão.</p>"); //Aciona mensagem de erro
      server.send(200, "text/html", responsePage); //Envia a página web
    }
    count++;
  }
  //Caso ocorra algum erro não detectado dentro do while
  String responsePage = (const __FlashStringHelper*) MAIN_page; //Lê conteúdo do HTML
  responsePage.replace("<br><br>", "<p id='status' style='color:red;'>Erro.</p>");
  server.send(200, "text/html", responsePage);
  return;
}

Imagem 3 – Mensagem de sucesso

Imagem 3 – Mensagem de sucesso

Imagem 4 – Mensagem de erro

Imagem 4 – Mensagem de erro

Caso tenha problemas para receber o resultado da operação (timeout), teste diminuir o número de iterações do laço de repetição.

EEPROM do ESP8266

A EEPROM (Electrically-Erasable Programmable Read-Only Memory) é um tipo de memória não-volátil utilizada para armazenar pequenas quantidades de dados que não são perdidos após o desligamento do dispositivo. Ela é muito útil para salvar dados de configuração, credenciamento de usuários ou leituras de sensores por exemplo. Neste projeto, vamos usá-la para salvar a última rede a qual o NodeMCU se conectou para que não seja necessário o usuário inserir novamente o nome da rede e sua senha toda vez que ligar o dispositivo. A reconexão poderá ser realizada através de um simples clique de botão.

O ESP8266 possui uma biblioteca padrão utilizada para a manipulação da EEPROM. Para iniciá-la, basta adicionar a seguinte chamada ao topo de seu código:

Arduino
1
#include <EEPROM.h>

Salvando Dados

Para salvar as credenciais da rede Wi-Fi, vamos criar uma função chamada salvarEeprom() que terá como parâmetros o nome e a senha da rede:

Arduino
1
void salvarEeprom(String ssidWifi, String passwordWifi) {}

Diferente das placas Arduino, o ESP8266 não possui uma memória EEPROM real. Ela é emulada utilizando uma sessão de sua memória Flash. Por isso, no ESP8266 é necessário informar durante a inicialização da EEPROM a quantidade de bytes que serão utilizados. Isso acontece através da função EEPROM.begin(size), que aceita valores de 4 a 4096 bytes. Para este tutorial, vamos usar 98 bytes:

Arduino
1
EEPROM.begin(98);

A EEPROM pode ser interpretada como um array (vetor) de bytes, com cada posição dela sendo 1 byte. Isso significa que em cada posição de nossa EEPROM poderemos armazenar uma única variável do tipo char (caractere), totalizando 98 caracteres quando ocupada por completo.

Sabemos que 1 único byte também pode representar valores numéricos de 0 a 255, logo, vamos reservar as duas primeiras posições da EEPROM para armazenar dois valores numéricos que indicarão os tamanhos (quantidade de caracteres) do SSID e da senha. Estes valores serão usados para dizer ao programa quantas posições cada uma destas informações está ocupando na memória e possibilitar a leitura dos dados de forma independente.

Para salvar dados na EEPROM, precisamos chamar a função EEPROM.write(address, value), onde address é o índice (de 0 a 97) e value será nossa variável de tamanho. Como estamos usando objetos do tipo String para representar o SSID e a senha, podemos usar o método length() da classe String para pegar os seus tamanhos. Sendo assim, temos:

Arduino
1
2
EEPROM.write(0, ssidWifi.length());
EEPROM.write(1, passwordWifi.length());

Em seguida, criaremos dois laços de repetição para armazenar as credenciais na memória:

1
2
3
4
5
6
7
8
9
for(int i = 2; i < 2+ssidWifi.length(); i++) {
Serial.print(ssidWifi.charAt(i-2));
EEPROM.write(i, ssidWifi.charAt(i-2));
}
 
for(int j = 2+ssidWifi.length(); j < 2+ssidWifi.length()+passwordWifi.length(); j++) {
Serial.print(passwordWifi.charAt(j-2-ssidWifi.length()));
EEPROM.write(j, passwordWifi.charAt(j-2-ssidWifi.length()));
}

Ao final, é necessário chamar a função EEPROM.commit() para assegurar que as mudanças sejam salvas na memória Flash e a função EEPROM.end() para encerrar as operações na EEPROM.

Com a biblioteca EEPROM padrão do ESP8266, a memória Flash é reprogramada toda vez que os dados da EEPROM são gravados, o que pode desgasta-la rapidamente. Para evitar operações desnecessárias na Flash, vamos adicionar uma condição para que os dados sejam salvos: a operação só será realizada caso os dados informados pelo usuário sejam diferentes daqueles já presentes na memória. Isso será verificado por uma função que chamaremos de compareEeprom().

Nossa função salvarEeprom() ficará assim:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void salvarEeprom(String ssidWifi, String passwordWifi) {
  EEPROM.begin(98); // Tamanho da FLASH reservado para EEPROM. Pode ser de 4 a 4096 bytes
 
  if(!compareEeprom(ssidWifi, passwordWifi)) {
 
    EEPROM.write(0, ssidWifi.length());
    for(int i = 2; i < 2+ssidWifi.length(); i++) {
      EEPROM.write(i, ssidWifi.charAt(i-2));
    }
    
    EEPROM.write(1, passwordWifi.length());
    for(int j = 2+ssidWifi.length(); j < 2+ssidWifi.length()+passwordWifi.length(); j++) {
      EEPROM.write(j, passwordWifi.charAt(j-2-ssidWifi.length()));
    }
    
    EEPROM.commit(); // Salva alterações na FLASH
  }
  EEPROM.end(); // Apaga a cópia da EEPROM salva na RAM
}

Comparando Dados da Memória EEPROM

Em nossa função compareEeprom() serão realizadas operações apenas de leitura, o que não prejudicará a integridade da Flash. Caso o valor lido seja igual ao recebido pelo usuário, a função retornará true e não salvará nada na EEPROM. Caso o valor lido seja diferente do recebido, a função retornará false e os dados presentes na EEPROM serão substituídos.

Para ler uma informação da memória, usamos a função EEPROM.read(address), onde address é a posição a ser lida. Lembrando que as duas primeiras posições são valores numéricos que não fazem parte das credenciais do Wi-Fi, a função compareEeprom() ficará assim:

Arduino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
boolean compareEeprom(String ssidWifi, String passwordWifi) {
  int idLength = int(EEPROM.read(0)); // Tamanho do SSID armazenado (número de bytes)
  int passLength = int(EEPROM.read(1)); // Tamanho do Password armazenado (número de bytes)
  String id = "";
  String pass = "";
 
  for(int i = 2; i < 2+idLength; i++) {
    id = id + char(EEPROM.read(i));
  }
 
  for(int j = 2+idLength; j < 2+idLength+passLength; j++) {
    pass = pass + char(EEPROM.read(j));
  }
 
  if(id.equals(ssidWifi) && pass.equals(passwordWifi)) { // Se dados já presentes na memória.
    return true;
  }
  else {
    return false;
  }
}

Conexão Através da EEPROM

Nossa última função de acesso à EEPROM será a que realiza a conexão Wi-Fi a partir dos dados armazenados na memória, sendo chamada pelo usuário ao clicar no botão “Conectar à Última Rede Utilizada.” Ela vai se assemelhar bastante à função compareEeprom(), mas ao invés de retornarmos um valor booleano, ao final da função vamos fazer uma chamada à função connectToWiFi() passando como parâmetros os valores de SSID e senha lidos da EEPROM. Chamaremos essa função de connectEeprom():

Arduino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void connectEeprom() {
  EEPROM.begin(98); // Tamanho da FLASH reservado para EEPROM. Pode ser de 4 a 4096 bytes
  
  int ssidSize = (int)EEPROM.read(0); // Tamanho do SSID armazenado (número de bytes)
  int passwordSize = (int)EEPROM.read(1); // Tamanho do Password armazenado (número de bytes)
  String ssidWifi = "";
  String passwordWifi = "";
  
  for(int i = 2; i < 2+ssidSize; i++) {
    ssidWifi.concat(char(EEPROM.read(i)));
  }
  
  for(int j = 2+ssidSize; j < 2+ssidSize+passwordSize; j++) {
    passwordWifi.concat(char(EEPROM.read(j)));
  }
  
  EEPROM.end(); // Apaga a cópia da EEPROM salva na RAM
  
  connectToWiFi(ssidWifi, passwordWifi);
}

Código do ESP8266

Aqui está o código completo pronto para ser carregado para sua placa:

Arduino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
#include <ESP8266WiFi.h>      
#include <ESP8266WebServer.h>
#include <EEPROM.h>
 
#define ONBOARD_LED D4 //Led embutido
 
const char MAIN_page[] PROGMEM = R"=====(
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>HTML Form ESP8266 - SmartKits</title>
  <style>
    body {color: #434343; font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; font-size: 14px; background-color: #eeeeee; margin-top: 100px;}
    .container {margin: 0 auto; max-width: 400px; padding: 30px; box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23); background-color: #ffffff; border-radius: 10px;}
  h2 {text-align: center; margin-bottom: 20px; margin-top: 0px; color: #0ee6b1; font-size: 35px;}
  #titleGreen {color: #00E1AA;}
  #titleBlack {color: #000000;}
    h3 {text-align: center; margin-bottom: 40px; margin-top: 0px; color: #336859; font-size: 35px;}
    form .field-group {box-sizing: border-box; clear: both; padding: 4px 0; position: relative; margin: 1px 0; width: 100%;}
    .text-field {font-size: 15px; margin-bottom: 4%; -webkit-appearance: none; display: block; background: #fafafa; color: #636363; width: 100%; padding: 15px 0px 15px 0px; text-indent: 10px; border-radius: 5px; border: 1px solid #e6e6e6; background-color: transparent;}
    .text-field:focus {border-color: #00bcd4; outline: 0;}
    .button-container {box-sizing: border-box; clear: both; margin: 1px 0 0; padding: 4px 0; position: relative; width: 100%;}
    .button {background: #00E1AA; border: none; border-radius: 5px; color: #ffffff; cursor: pointer; display: block; font-weight: bold; font-size: 16px; padding: 15px 0; text-align: center; text-transform: uppercase; width: 100%; -webkit-transition: background 250ms ease; -moz-transition: background 250ms ease; -o-transition: background 250ms ease; transition: background 250ms ease;}
    p {text-align: center; text-decoration: none; color: #87c1d3; font-size: 18px;}
    a {text-decoration: none; color: #ffffff; margin-top: 0%;}
    #status {text-align: center; text-decoration: none; color: #336859; font-size: 14px;}
  </style>
  <script>
  function validateForm() {
    var ssid = document.forms["myForm"]["ssid"].value;
    var password = document.forms["myForm"]["password"].value;
    var status = document.getElementById("statusDiv");
    if (ssid == "" && password == "") {
    status.innerHTML = "<p id='status' style='color:red;'>Insira SSID e senha.</p>";
    return false;
    }
    else if (ssid == "") {
    status.innerHTML = "<p id='status' style='color:red;'>Insira SSID.</p>";
    return false;
    }
    else if (password == "") {
    status.innerHTML = "<p id='status' style='color:red;'>Insira senha.</p>";
    return false;
    }
    else {
    status.innerHTML = "<p id='status'>Conectando...</p>";
    return true;
    }
  }
  </script>
</head>
<body>
<div class="container">
  <h2><span id="titleGreen">smart</span><span id="titleBlack">kits</span></h2>
  <h3>Conexão ESP8266</h3>
  <form name="myForm" action="/action_new_connection" onsubmit="return validateForm()" method="post">
    <div class="field-group">
  <select class='text-field' name='ssid'></select>
    </div>
    <br>
    <div class="field-group">
    <input class="text-field" type="password" name="password" length=64 placeholder="Password">
    </div>
  <br>
  <div id="statusDiv">
    <br><br>
  </div>
    <div class="button-container">
    <input class="button" type="submit" value="Conectar">
    </div>
  </form>
  <p>OU</p>
  <div class="button-container">
    <button class="button" type="button" onclick="window.location.href='/action_previous_connection'">Conectar à última rede utilizada</button>
  </div>
</div>
</body>
</html>
)=====";
 
const char *ssid = "ESP8266 Access Point"; // Nome da rede WiFi que será criada
const char *password = "smartkitsAP";   // Senha para se conectar nesta rede
ESP8266WebServer server(80); //Server utiliza a porta 80
 
void setup() {
  pinMode(ONBOARD_LED, OUTPUT); //LED embutido
  Serial.begin(115200);
 
  WiFi.softAP(ssid, password);            
  Serial.print("Access Point \"");
  Serial.print(ssid);
  Serial.println("\" iniciado");
 
  Serial.print("IP address:\t");
  Serial.println(WiFi.softAPIP());        
  //Tratamento de rotas
  server.on("/", handleRoot);      
  server.on("/action_new_connection", handleForm);
  server.on("/action_previous_connection", connectEeprom);
  server.begin();                
  Serial.println("Servidor HTTP iniciado");
}
 
void loop() {
  server.handleClient();          //Trata requisições de clientes
  if(WiFi.status() != WL_CONNECTED) {
    digitalWrite(ONBOARD_LED, HIGH); //Desative o LED
  }
}
 
void handleRoot() {
String index = listSSID(); //Leia o conteúdo HTML
server.send(200, "text/html", index); //Enviar pagina Web
}
 
void handleForm() {
String ssidWifi = server.arg("ssid");
String passwordWifi = server.arg("password");
Serial.printf("SSID: %s\n", ssidWifi);
Serial.printf("Password: %s\n", passwordWifi);
 
if(!ssidWifi.equals("") && !passwordWifi.equals("")) {
  connectToWiFi(ssidWifi, passwordWifi);
}
}
 
void connectToWiFi(String ssidWifi, String passwordWifi) {
  int count = 0;
  WiFi.begin(ssidWifi.c_str(), passwordWifi.c_str());     //Conecta com seu roteador
  Serial.println("");
 
  //Espera por uma conexão
  while ( count < 15 ) {
    delay(500);
    Serial.print(".");
    if (WiFi.status() == WL_CONNECTED) {
      Serial.println("");
      salvarEeprom(ssidWifi, passwordWifi);
      Serial.println("");
      //Se a conexão ocorrer com sucesso, mostre o endereço IP no monitor serial
      Serial.println("Conectado ao WiFi");
      Serial.print("IP address: ");
      Serial.println(WiFi.localIP());  //Endereço IP do ESP8266
      digitalWrite(ONBOARD_LED, LOW); //Acende o LED
      String responsePage = (const __FlashStringHelper*) MAIN_page; //Leia o conteúdo HTML
      responsePage.replace("<br><br>", "<p id='status'>Conectado!</p>");
      server.send(200, "text/html", responsePage);
      return;
    }
    else if (WiFi.status() == WL_CONNECT_FAILED) {
      String responsePage = (const __FlashStringHelper*) MAIN_page;
      responsePage.replace("<br><br>", "<p id='status' style='color:red;'>Falha na conexão.</p>");
      server.send(200, "text/html", responsePage);
    }
    count++;
  }
  Serial.println();
  Serial.println("Timed out.");
  String responsePage = (const __FlashStringHelper*) MAIN_page;
  responsePage.replace("<br><br>", "<p id='status' style='color:red;'>Erro.</p>");
  server.send(200, "text/html", responsePage);
  return;
}
 
String listSSID() {
  String index = (const __FlashStringHelper*) MAIN_page; //Leia o conteúdo HTML
  String networks = "";
  int n = WiFi.scanNetworks();
  Serial.println("Scan done.");
  if (n == 0) {
    Serial.println("Nenhuma rede encontrada.");
    index.replace("<select class='text-field' name='ssid'></select>", "<select class='text-field' name='ssid'><option value='' disabled selected>Nenhuma rede encontrada</option></select>");
    index.replace("<br><br>", "<p id='status' style='color:red;'>Rede não encontrada.</p>");
    return index;
  }
  else {
    Serial.printf("%d networks found.\n", n);
    networks += "<select class='text-field' name='ssid'><option value='' disabled selected>SSID</option>";
    for (int i = 0; i < n; ++i)
    {
      // Imprime o SSID de cada rede encontrada
      networks += "<option value='" + WiFi.SSID(i) + "'>" + WiFi.SSID(i) + "</option>";
    }
    networks += "</select>";
  }
  index.replace("<select class='text-field' name='ssid'></select>", networks);
  return index;
}
 
void salvarEeprom(String ssidWifi, String passwordWifi) {
  EEPROM.begin(98); // Tamanho da FLASH reservado para EEPROM. Pode ser de 4 a 4096 bytes
 
  if(!compareEeprom(ssidWifi, passwordWifi)) {
    Serial.println("Salvando:");
    EEPROM.write(0, ssidWifi.length());
    Serial.println(ssidWifi.length());
    
    for(int i = 2; i < 2+ssidWifi.length(); i++) {
      Serial.print(ssidWifi.charAt(i-2));
      EEPROM.write(i, ssidWifi.charAt(i-2));
    }
    Serial.println("");
    
    Serial.println("Salvando:");
    EEPROM.write(1, passwordWifi.length());
    Serial.println(passwordWifi.length());
    
    for(int j = 2+ssidWifi.length(); j < 2+ssidWifi.length()+passwordWifi.length(); j++) {
      Serial.print(passwordWifi.charAt(j-2-ssidWifi.length()));
      EEPROM.write(j, passwordWifi.charAt(j-2-ssidWifi.length()));
    }
    Serial.println("");
    
    EEPROM.commit(); // Salva alterações na FLASH
  }
  EEPROM.end(); // Apaga a cópia da EEPROM salva na RAM
}
 
boolean compareEeprom(String ssidWifi, String passwordWifi) {
  int idLength = int(EEPROM.read(0)); // Tamanho do SSID armazenado (número de bytes)
  int passLength = int(EEPROM.read(1)); // Tamanho do Password armazenado (número de bytes)
  String id = "";
  String pass = "";
 
  Serial.println("Lendo SSID:");
  Serial.print("Tamanho:");
  Serial.println(idLength);
  for(int i = 2; i < 2+idLength; i++) {
    Serial.print("Posição ");
    Serial.print(i);
    Serial.print(": ");
    id = id + char(EEPROM.read(i));
    Serial.println(id[i-2]);
  }
  Serial.println("");
 
  Serial.println("Lendo senha:");
  Serial.print("Tamanho:");
  Serial.println(passLength);
  for(int j = 2+idLength; j < 2+idLength+passLength; j++) {
    Serial.print("Posição ");
    Serial.print(j);
    Serial.print(": ");
    pass = pass + char(EEPROM.read(j));
    Serial.println(pass[j-2-idLength]);
    Serial.println(pass);
  }
  Serial.println("");
 
  Serial.print("SSID é igual: ");
  Serial.println(id.equals(ssidWifi));
 
  Serial.print("Senha é igual: ");
  Serial.println(pass.equals(passwordWifi));
 
  if(id.equals(ssidWifi) && pass.equals(passwordWifi))
  {
    Serial.println("Dados já presentes na memória.");
    return true;
  }
  else
  {
    return false;
  }
}
 
void connectEeprom() {
  EEPROM.begin(98); // Tamanho da FLASH reservado para EEPROM. Pode ser de 4 a 4096 bytes
  
  int ssidSize = (int)EEPROM.read(0); // Tamanho do SSID armazenado (número de bytes)
  int passwordSize = (int)EEPROM.read(1); // Tamanho do Password armazenado (número de bytes)
  String ssidWifi = "";
  String passwordWifi = "";
  
  Serial.println("Lendo:");
  for(int i = 2; i < 2+ssidSize; i++) {
    Serial.print(char(EEPROM.read(i)));
    ssidWifi.concat(char(EEPROM.read(i)));
  }
  Serial.println("");
  
  Serial.println("Lendo:");
  for(int j = 2+ssidSize; j < 2+ssidSize+passwordSize; j++) {
    Serial.print(char(EEPROM.read(j)));
    passwordWifi.concat(char(EEPROM.read(j)));
  }
  Serial.println("");
  
  EEPROM.end(); // Apaga a cópia da EEPROM salva na RAM
  
  Serial.println("Leu:");
  Serial.println(ssidWifi);
  Serial.println(passwordWifi);
  
  connectToWiFi(ssidWifi, passwordWifi);
}

 

Gostou? Deixe seu comentário logo abaixo, não deixe de conferir outras postagens do nosso blog. Confira também a nossa loja virtual e encontre todos os componentes utilizados no projeto no post.

Gabriel Martins de Freitas

Graduando em Sistemas e Mídias Digitais. Tenho experiência com Arduino e ESP8266. Atualmente compartilhando meu conhecimento no blog da Smart Kits.

See author's posts

Twittar
Compartilhar
Pin
Compartilhar

Deixe um comentário Cancelar resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Postagens recentes

  • Como Funciona o Potenciômetro com Chave
  • Usando Múltiplos Sensores de Distância Laser VL53L0X
  • Como Programar o LilyPad Arduino
  • Como usar o Módulo Joystick 5D Rocker JY50
  • Arduino – Interface Através do Monitor Serial

Comentários

  • Yure Albuquerque em Primeiros passos com o Arduino Mega WiFi
  • Marcone em Primeiros passos com o Arduino Mega WiFi
  • Yure Albuquerque em Primeiros passos com o Arduino Mega WiFi
  • Yure Albuquerque em ESP32 pinout – Guia Básico de GPIOs
  • Williams em ESP8266 – Cadastro RFID (MFRC522) com webserver

Categorias

  • Arduino
  • Embarcados
  • ESP32
  • ESP8266
  • IoT
  • Raspberry Pi

Quem somos

A Smart Kits é uma loja virtual que atua no comércio de componentes eletrônicos, com foco na venda de Arduinos, módulos Shields arduinos, sensores eletrônicos, componentes para automação e robótica. Temos uma grande variedade de componentes e excelentes preços.

Acesse nossa loja

Redes Sociais

Todos os direitos reservados à Blog Smart Kits ©2022 | CNPJ: 20.228.852/0001-48