Skip to content

Instantly share code, notes, and snippets.

@IgorDePaula
Created October 19, 2025 17:05
Show Gist options
  • Save IgorDePaula/437c03631061f4c7bcac46d928ca64fb to your computer and use it in GitHub Desktop.
Save IgorDePaula/437c03631061f4c7bcac46d928ca64fb to your computer and use it in GitHub Desktop.
update firmware via aws
/*
* ESP32 AWS IoT Jobs OTA - Arduino IDE
*
* Bibliotecas necessárias (instalar via Library Manager):
* - ArduinoJson by Benoit Blanchon
*
* Board: ESP32 Dev Module
* Partition Scheme: Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS)
*/
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <HTTPClient.h>
#include <Update.h>
#include <ArduinoJson.h>
// ===== CONFIGURAÇÕES - EDITAR AQUI =====
#define WIFI_SSID ""
#define WIFI_PASSWORD ""
#define AWS_IOT_ENDPOINT "xxxx-ats.iot.us-east-1.amazonaws.com"
#define AWS_IOT_THING_NAME "Esp32Teste"
#define FIRMWARE_VERSION "1.0.0"
//String("esp32-") + String((uint64_t)ESP.getEfuseMac(), HEX);
// ===== CERTIFICADOS =====
// Cole seus certificados aqui (veja instruções no final)
const char AWS_CERT_CA[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
)EOF";
const char AWS_CERT_CRT[] PROGMEM = R"KEY(
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
)KEY";
const char AWS_CERT_PRIVATE[] PROGMEM = R"KEY(
-----BEGIN RSA PRIVATE KEY-----
-----END RSA PRIVATE KEY-----
)KEY";
// ===== VARIÁVEIS GLOBAIS =====
WiFiClientSecure net;
PubSubClient mqtt(net);
char currentJobId[64] = {0};
bool otaInProgress = false;
// ===== TÓPICOS MQTT =====
char TOPIC_NOTIFY[128];
char TOPIC_GET[128];
// ===== FUNÇÕES =====
void setupWiFi() {
Serial.print("Conectando WiFi");
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi conectado!");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
}
void updateJobStatus(const char* jobId, const char* status, const char* details = "") {
char topic[256];
snprintf(topic, sizeof(topic), "$aws/things/%s/jobs/%s/update",
AWS_IOT_THING_NAME, jobId);
StaticJsonDocument<512> doc;
doc["status"] = status;
doc["statusDetails"]["message"] = details;
doc["statusDetails"]["version"] = FIRMWARE_VERSION;
char payload[512];
serializeJson(doc, payload);
mqtt.publish(topic, payload);
Serial.printf("Job status: %s\n", status);
}
bool performOTA(const char* url) {
Serial.printf("Iniciando OTA de: %s\n", url);
otaInProgress = true;
HTTPClient http;
http.begin(url);
int httpCode = http.GET();
if (httpCode != HTTP_CODE_OK) {
Serial.printf("HTTP GET falhou: %d\n", httpCode);
http.end();
otaInProgress = false;
return false;
}
int contentLength = http.getSize();
Serial.printf("Tamanho: %d bytes\n", contentLength);
if (contentLength <= 0) {
Serial.println("Tamanho inválido");
http.end();
otaInProgress = false;
return false;
}
bool canBegin = Update.begin(contentLength);
if (!canBegin) {
Serial.println("Não há espaço para OTA");
http.end();
otaInProgress = false;
return false;
}
WiFiClient* stream = http.getStreamPtr();
size_t written = 0;
uint8_t buff[1024];
int lastProgress = -1;
while (http.connected() && (written < contentLength)) {
size_t available = stream->available();
if (available) {
int bytesRead = stream->readBytes(buff, min(available, sizeof(buff)));
size_t bytesWritten = Update.write(buff, bytesRead);
if (bytesWritten != bytesRead) {
Serial.println("Erro ao escrever firmware");
Update.abort();
http.end();
otaInProgress = false;
return false;
}
written += bytesWritten;
int progress = (written * 100) / contentLength;
if (progress != lastProgress && progress % 10 == 0) {
Serial.printf("Download: %d%%\n", progress);
char details[64];
snprintf(details, sizeof(details), "Download %d%%", progress);
updateJobStatus(currentJobId, "IN_PROGRESS", details);
lastProgress = progress;
}
// Manter MQTT ativo
mqtt.loop();
}
delay(1);
}
http.end();
if (written != contentLength) {
Serial.printf("Download incompleto: %d de %d bytes\n", written, contentLength);
Update.abort();
otaInProgress = false;
return false;
}
if (Update.end()) {
Serial.println("OTA concluído!");
if (Update.isFinished()) {
Serial.println("Update finalizado com sucesso!");
otaInProgress = false;
return true;
} else {
Serial.println("Update não finalizado");
otaInProgress = false;
return false;
}
} else {
Serial.printf("Erro no Update: %d\n", Update.getError());
otaInProgress = false;
return false;
}
}
void processJob(const char* payload) {
StaticJsonDocument<2048> doc;
DeserializationError error = deserializeJson(doc, payload);
if (error) {
Serial.print("Erro ao parsear JSON: ");
Serial.println(error.c_str());
return;
}
if (!doc.containsKey("execution")) {
Serial.println("JSON sem 'execution'");
return;
}
const char* jobId = doc["execution"]["jobId"];
JsonObject jobDocument = doc["execution"]["jobDocument"];
if (!jobId || jobDocument.isNull()) {
Serial.println("Job inválido");
return;
}
strncpy(currentJobId, jobId, sizeof(currentJobId) - 1);
Serial.printf("Job ID: %s\n", jobId);
if (!jobDocument.containsKey("files") || !jobDocument["files"].is<JsonArray>()) {
Serial.println("Job sem 'files'");
updateJobStatus(jobId, "FAILED", "Job document invalido");
return;
}
JsonArray files = jobDocument["files"];
if (files.size() == 0) {
Serial.println("Array 'files' vazio");
updateJobStatus(jobId, "FAILED", "Nenhum arquivo especificado");
return;
}
const char* url = files[0]["fileSource"]["url"];
if (!url) {
Serial.println("URL não encontrada");
updateJobStatus(jobId, "FAILED", "URL ausente");
return;
}
Serial.printf("URL: %s\n", url);
updateJobStatus(jobId, "IN_PROGRESS", "Iniciando OTA");
bool success = performOTA(url);
if (success) {
updateJobStatus(jobId, "SUCCEEDED", "OTA completo");
delay(2000);
Serial.println("Reiniciando...");
ESP.restart();
} else {
updateJobStatus(jobId, "FAILED", "OTA falhou");
}
}
void mqttCallback(char* topic, byte* payload, unsigned int length) {
Serial.printf("Mensagem recebida em: %s\n", topic);
char message[length + 1];
memcpy(message, payload, length);
message[length] = '\0';
Serial.println("Payload:");
Serial.println(message);
processJob(message);
}
void connectAWS() {
// Configurar certificados
net.setCACert(AWS_CERT_CA);
net.setCertificate(AWS_CERT_CRT);
net.setPrivateKey(AWS_CERT_PRIVATE);
mqtt.setServer(AWS_IOT_ENDPOINT, 8883);
mqtt.setCallback(mqttCallback);
mqtt.setBufferSize(2048); // Aumentar buffer para jobs grandes
Serial.print("Conectando AWS IoT");
while (!mqtt.connected()) {
if (mqtt.connect(AWS_IOT_THING_NAME)) {
Serial.println("\nAWS IoT conectado!");
// Subscrever ao tópico de notificação de jobs
snprintf(TOPIC_NOTIFY, sizeof(TOPIC_NOTIFY),
"$aws/things/%s/jobs/notify-next", AWS_IOT_THING_NAME);
mqtt.subscribe(TOPIC_NOTIFY);
Serial.printf("Subscrito: %s\n", TOPIC_NOTIFY);
// Solicitar job pendente
snprintf(TOPIC_GET, sizeof(TOPIC_GET),
"$aws/things/%s/jobs/$next/get", AWS_IOT_THING_NAME);
mqtt.publish(TOPIC_GET, "{}");
Serial.println("Solicitando jobs pendentes...");
} else {
Serial.print(".");
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("\n=== AWS IoT Jobs OTA Demo ===");
Serial.printf("Firmware version: %s\n", FIRMWARE_VERSION);
Serial.printf("Thing Name: %s\n", AWS_IOT_THING_NAME);
setupWiFi();
connectAWS();
Serial.println("Sistema pronto! Aguardando jobs...");
}
void loop() {
if (!mqtt.connected() && !otaInProgress) {
connectAWS();
}
if (!otaInProgress) {
mqtt.loop();
}
delay(10);
}
/*
* ===== INSTRUÇÕES DE USO =====
*
* 1. INSTALAR ESP32 BOARD:
* - File → Preferences → Additional Board Manager URLs:
* https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
* - Tools → Board → Boards Manager → Pesquisar "esp32" → Install
*
* 2. INSTALAR BIBLIOTECA:
* - Sketch → Include Library → Manage Libraries
* - Pesquisar "ArduinoJson" → Install (versão 6.x)
*
* 3. CONFIGURAR BOARD:
* - Tools → Board → ESP32 Arduino → ESP32 Dev Module
* - Tools → Partition Scheme → Minimal SPIFFS (1.9MB APP with OTA)
* - Tools → Upload Speed → 921600
* - Tools → Flash Size → 4MB (32Mb)
*
* 4. OBTER CERTIFICADOS:
* a) Root CA:
* - Baixar de: https://www.amazontrust.com/repository/AmazonRootCA1.pem
*
* b) Certificados do Dispositivo:
* - AWS Console → IoT Core → All devices → Things → Create thing
* - Baixar: certificate.pem.crt e private.pem.key
*
* c) Colar certificados nas constantes acima (AWS_CERT_CA, AWS_CERT_CRT, AWS_CERT_PRIVATE)
* - Manter formato: R"EOF( ... )EOF" ou R"KEY( ... )KEY"
* - Incluir as linhas -----BEGIN----- e -----END-----
*
* 5. EDITAR CONFIGURAÇÕES:
* - WIFI_SSID e WIFI_PASSWORD
* - AWS_IOT_ENDPOINT (encontrar em: AWS IoT Console → Settings)
* - AWS_IOT_THING_NAME (nome do seu Thing)
* - FIRMWARE_VERSION (incrementar a cada versão)
*
* 6. PRIMEIRA COMPILAÇÃO E UPLOAD:
* - Sketch → Upload
* - Tools → Serial Monitor (115200 baud)
*
* 7. CRIAR JOB NO AWS:
* a) Upload firmware compilado para S3:
* - Localizar .bin em: C:\Users\SeuUsuario\AppData\Local\Temp\arduino_build_xxxxx\
* - Nome do arquivo: [nome_do_sketch].ino.bin
* - Upload para S3 via console AWS
*
* b) Criar Job Document (job-document.json):
* {
* "operation": "firmware_update",
* "version": "1.1.0",
* "files": [{
* "fileName": "firmware.bin",
* "fileSource": {
* "url": "https://seu-bucket.s3.amazonaws.com/firmware.bin"
* }
* }]
* }
*
* c) Criar Job:
* - AWS IoT Console → Manage → Jobs → Create
* - Target: Selecionar seu Thing
* - Cole o Job Document
*
* 8. MONITORAR:
* - Abrir Serial Monitor
* - Ver progresso do download
* - Aguardar reinicialização automática
*
* ===== TROUBLESHOOTING =====
*
* Erro "Sketch too big":
* → Tools → Partition Scheme → Minimal SPIFFS (1.9MB APP with OTA)
*
* Erro "Connection refused":
* → Verificar certificados
* → Verificar Thing Name
* → Verificar Policy anexada ao certificado
*
* Job não chega:
* → Verificar Job targetted para o Thing correto
* → Verificar subscrição aos tópicos
* → Verificar no Serial Monitor se conectou ao AWS IoT
*
* OTA falha:
* → URL do S3 deve ser pública ou presigned
* → Verificar tamanho do firmware (< 1.9MB com partição padrão)
* → Verificar se a partição OTA está configurada
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment