Projetos com Arduino/ESP
Índice
Algumas dicas
Credenciais de WiFi
Deprecated: usar WiFiManager + ArduinoOTA
Para que as credenciais não precisem ficar no mesmo arquivo INO nem nas mesmas pastas do projeto (o que levaria as credenciais a serem sincronizadas com um repositório, eventualmente), criar um arquivo credentials.h no diretório Arduino/libraries/Meslin. Esse diretório, no Linux fica em ~/Arduino/libraries/ e no Windows fica em C:\Users\meslin\Documents\Arduino\libraries\.
O conteúdo do arquivo deverá ser:
#define MY_SSID ("Seu SSID")
#define MY_PASSWORD ("A senha do WiFi")
Ao usar o WiFi, inclua o seguinte header no programa:
#include <credentials.h>
Programação usando OTA (On-The-Air)
Para programa o ESP32 via OTA é necessário que o Python versão 2 (testado com 2.7.18). O Python versão 3 pode estar instalado sem problemas, mas durante a instalação do Python 2 é necessário que a PATH seja modificada para incluir o Python. Essa opção está disponível durante a instalação.
A programação foi testada com a biblioteca ArduinoOTA e o exemplo ArduinoOTA/BasicOTA.
Como gravar ESP12F, ESP32, ESP32-CAM
Circuitos das Figuras A e D foram testados e funcionaram.
Deprecated para ESP12F, ESP32 - usar o programador.
Testado!
Lembrar de colocar a chave em 3.3V.
Teórico (?)
Teórico (?)
Testado!
Lembrar de colocar a chave em 5V.
Modelo Básico para ESP32/ESP8266
Inclui:
Configuração de Wi-Fi pelo WiFiManager
Upload de código via OTA
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager
#include <ArduinoOTA.h>
void setupArduinoOTA(void);
void setupWiFiManager(void);
void setup() {
Serial.begin(115200);
Serial.println("Booting");
setupWiFiManager();
setupArduinoOTA();
pinMode(LED_BUILTIN, OUTPUT); // led blink
}
void loop() {
ArduinoOTA.handle();
// led blink
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(500); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
/**
* setupWiFiManager
* Configura o WiFi Manager
* Utiliza o endereço IP 192.168.0.1
* SSID OTA-Template
* Não tem senha
*/
void setupWiFiManager(void) {
// WiFiManager, Local initialization. Once its business is done, there is no need to keep it around
WiFiManager wm;
// reset settings - wipe stored credentials for testing
// these are stored by the esp library
// wm.resetSettings();
// Automatically connect using saved credentials,
// if connection fails, it starts an access point with the specified name ( "AutoConnectAP"),
// if empty will auto generate SSID, if password is blank it will be anonymous AP (wm.autoConnect())
// then goes into a blocking loop awaiting configuration and will return success result
bool res;
// Apagar as credenciais Wi-Fi armazenadas (descomente a linha abaixo para limpar)
// wm.resetSettings();
//set custom ip for portal
wm.setAPStaticIPConfig(IPAddress(192,168,0,1), IPAddress(192,168,0,1), IPAddress(255,255,255,0));
// res = wm.autoConnect(); // auto generated AP name from chipid
// res = wm.autoConnect("AutoConnectAP"); // anonymous ap
// res = wm.autoConnect("OTA-Template", "password"); // password protected ap
res = wm.autoConnect("OTA-Template"); // no password protected
if (!res) {
Serial.println("Failed to connect");
// ESP.restart();
}
else {
// if you get here you have connected to the WiFi
Serial.println("connected...yeey :)");
}
}
/**
* setupArduinoOTA
* Configura o Arduino OTA para upload de código via Wi-Fi
* Porta 8266 (default)
* Host OTA-Template
* Sem senha
*/
void setupArduinoOTA(void) {
// Port defaults to 8266
// ArduinoOTA.setPort(8266);
// Hostname defaults to esp8266-[ChipID]
ArduinoOTA.setHostname("OTA-Template");
// No authentication by default
// ArduinoOTA.setPassword("admin");
// Password can be set with it's md5 value as well
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
ArduinoOTA.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH) {
type = "sketch";
} else { // U_FS
type = "filesystem";
}
// NOTE: if updating FS this would be the place to unmount FS using FS.end()
Serial.println("Start updating " + type);
});
ArduinoOTA.onEnd([]() { Serial.println("\nEnd"); });
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) {
Serial.println("Auth Failed");
} else if (error == OTA_BEGIN_ERROR) {
Serial.println("Begin Failed");
} else if (error == OTA_CONNECT_ERROR) {
Serial.println("Connect Failed");
} else if (error == OTA_RECEIVE_ERROR) {
Serial.println("Receive Failed");
} else if (error == OTA_END_ERROR) {
Serial.println("End Failed");
}
});
ArduinoOTA.begin();
Serial.println("Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
Scanner de Dispositivos OTA
Esse programa deve ser utilizado para suprir a deficiência do Visual Studio Code em descobrir os dispositivos OTA na rede.
import socket
import struct
# Configurações do multicast
MULTICAST_GROUP = '224.0.0.251' # Endereço do mDNS (ArduinoOTA usa multicast para anúncios)
PORT = 5353 # Porta padrão do mDNS (multicast DNS)
TIMEOUT = 20 # tempo de timeout para descobrir dispositivos OTA
def discover_arduino_ota_devices(timeout=5):
"""
Descobre dispositivos ArduinoOTA na rede local.
:param timeout: Tempo em segundos para escanear a rede.
:return: Lista de dispositivos encontrados (endereço IP e nomes).
"""
# Cria um socket UDP
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Adiciona o socket ao grupo multicast
group = struct.pack('4sl', socket.inet_aton(MULTICAST_GROUP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, group)
# Define um tempo limite para receber respostas
sock.settimeout(timeout)
# Envia uma mensagem multicast de descoberta
query = (
"\x00\x00" # Transaction ID
"\x00\x00" # Flags
"\x00\x01" # Número de perguntas (1)
"\x00\x00" # Respostas
"\x00\x00" # Autoridade
"\x00\x00" # Adicional
"\x08_arduino" # Name: _arduino
"\x04_tcp" # Type: _tcp
"\x05local" # Domain: local
"\x00" # Fim do nome
"\x00\x0C" # Tipo: PTR (nome de domínio apontado)
"\x00\x01" # Classe: IN (Internet)
).encode('utf-8')
sock.sendto(query, (MULTICAST_GROUP, PORT))
devices = []
try:
print("Escaneando dispositivos...")
while True:
# Recebe respostas de dispositivos
data, addr = sock.recvfrom(1024)
# Decodifica o nome OTA do dispositivo da resposta (mDNS)
device_name = extract_ota_name(data)
if device_name:
print(f"Dispositivo encontrado: IP={addr[0]}, Nome OTA={device_name}")
devices.append((addr[0], device_name))
else:
print(f"Dispositivo encontrado: IP={addr[0]}, Nome desconhecido")
devices.append((addr[0], "Nome desconhecido"))
except socket.timeout:
print("Fim do escaneamento.")
sock.close()
return devices
def extract_ota_name(data):
"""
Extrai o nome OTA do dispositivo da resposta mDNS.
:param data: Dados recebidos do dispositivo.
:return: Nome do dispositivo (string) ou None.
"""
try:
# Localiza o nome do dispositivo na resposta mDNS
response = data.decode(errors='ignore')
if "_arduino\x04_tcp\x05local" in response:
strBusca = "\x00\x00\x0C\x00\x01\x00\x00\x11\x00"
start = response.find(strBusca)
if start != -1:
# Extrai o nome antes do domínio "_arduino._tcp.local"
nome = response[start+len(strBusca)+2:start+len(strBusca)+2+ord(response[start+len(strBusca)+1:start+len(strBusca)+2])]
segments = response.split("_arduino._tcp.local")
if len(nome) > 0:
return nome
else:
return None
except Exception as e:
print(f"Erro ao interpretar resposta mDNS: {e}")
return None
# Descobre os dispositivos e lista os resultados
def main():
devices = discover_arduino_ota_devices(timeout=TIMEOUT)
print("\nDispositivos encontrados:")
for device in devices:
print(f" - {device}")
return
if __name__ == '__main__':
main()