Copia, flashea y recibe datos de un ESP32.
El sketch generado ya lleva host, topics, CA y la credencial MQTT de Devicecamp. Añade el Wi-Fi, flashea la placa y sustituye los dos valores de ejemplo por lecturas reales.
1. Prepara Arduino IDE
Instala “esp32 by Espressif Systems” desde el gestor de placas y “PubSubClient by Nick O’Leary” desde el gestor de librerías. WiFi y WiFiClientSecure ya vienen con el core del ESP32.
2. Genera el sketch de tu dispositivo
Crea el dispositivo, elige ESP32 Arduino y genera la credencial MQTT. Devicecamp inserta client ID, usuario, contraseña y topics exactos. Guarda el archivo localmente: la clave solo se muestra una vez y no debe subirse a Git.
3. Pega el sketch completo
La plantilla pública usa marcadores. La versión que aparece al crear el dispositivo está lista: solo cambia WIFI_SSID y WIFI_PASSWORD. Valida TLS, pone el reloj en hora, reconecta con espera progresiva y publica cada minuto.
// Devicecamp ESP32 MQTT/TLS starter
// Install "esp32 by Espressif Systems" and "PubSubClient by Nick O'Leary".
// This file contains a live device credential. Do not commit or share it.
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <time.h>
#include <math.h>
const char WIFI_SSID[] = "YOUR_WIFI_SSID";
const char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD";
const char MQTT_HOST[] = "mqtt.devicecamp.app";
const uint16_t MQTT_PORT = 8883;
const char MQTT_CLIENT_ID[] = "MQTT_CLIENT_ID";
const char MQTT_USERNAME[] = "MQTT_USERNAME";
const char MQTT_PASSWORD[] = "MQTT_PASSWORD";
const char TELEMETRY_TOPIC[] = "v1/devices/DEVICE_ID/telemetry";
const char STATUS_TOPIC[] = "v1/devices/DEVICE_ID/status";
const char DEVICECAMP_CA[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UE
BhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQD
EwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQG
EwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMT
DElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54r
Vygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj1
3Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8K
b4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCN
Aymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ
4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf
1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFu
hjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQH
usEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/r
OPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4G
A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY
9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV
0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwt
hDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJw
TdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nx
e5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZA
JzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahD
YVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9n
JEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJ
m+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----
)EOF";
WiFiClientSecure tls;
PubSubClient mqtt(tls);
unsigned long lastPublishAt = 0;
unsigned long nextReconnectAt = 0;
unsigned long reconnectDelayMs = 1000;
const unsigned long PUBLISH_INTERVAL_MS = 60000;
void connectWiFi() {
WiFi.mode(WIFI_STA);
WiFi.setAutoReconnect(true);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(" connected");
}
void syncClock() {
// Certificate validation needs a reasonably correct clock.
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
Serial.print("Synchronizing clock");
time_t now = time(nullptr);
while (now < 1700000000) {
delay(500);
Serial.print(".");
now = time(nullptr);
}
Serial.println(" ready");
}
void connectMQTT() {
if (mqtt.connected() || millis() < nextReconnectAt) return;
const bool connected = mqtt.connect(
MQTT_CLIENT_ID,
MQTT_USERNAME,
MQTT_PASSWORD,
STATUS_TOPIC,
1,
true,
"{\"status\":\"offline\"}"
);
if (connected) {
Serial.println("MQTT connected");
mqtt.publish(STATUS_TOPIC, "{\"status\":\"online\"}", true);
reconnectDelayMs = 1000;
return;
}
Serial.printf("MQTT failed, state=%d; retry in %lu ms\n",
mqtt.state(), reconnectDelayMs);
nextReconnectAt = millis() + reconnectDelayMs;
reconnectDelayMs = min(reconnectDelayMs * 2, 60000UL);
}
void publishReading() {
// Replace these sample values with reads from your sensor.
const float temperature = 22.4;
const float humidity = 51.0;
const String localIP = WiFi.localIP().toString();
char payload[192];
snprintf(payload, sizeof(payload),
"{\"metrics\":{\"temperature\":%.1f,\"humidity\":%.1f},\"metadata\":{\"local_ip\":\"%s\"}}",
temperature, humidity, localIP.c_str());
if (mqtt.publish(TELEMETRY_TOPIC, payload)) {
Serial.printf("Published: %s\n", payload);
} else {
Serial.println("Publish failed");
}
}
void setup() {
Serial.begin(115200);
connectWiFi();
syncClock();
tls.setCACert(DEVICECAMP_CA);
mqtt.setServer(MQTT_HOST, MQTT_PORT);
mqtt.setBufferSize(512);
mqtt.setKeepAlive(30);
mqtt.setSocketTimeout(10);
}
void loop() {
if (WiFi.status() != WL_CONNECTED) {
WiFi.reconnect();
delay(100);
return;
}
connectMQTT();
mqtt.loop();
const unsigned long now = millis();
if (mqtt.connected() &&
(lastPublishAt == 0 || now - lastPublishAt >= PUBLISH_INTERVAL_MS)) {
publishReading();
lastPublishAt = now;
}
}4. Conecta el sensor
En publishReading(), sustituye temperature y humidity por las lecturas del sensor. Mantén estables los nombres y tipos: Devicecamp descubre las métricas con el primer payload.
5. Flashea y comprueba
Selecciona la placa y el puerto, carga el sketch y abre el monitor serie a 115200 baudios. “Published” confirma el envío; el workbench debe mostrar ambas métricas. Si falla TLS, comprueba primero Wi-Fi, DNS y que haya terminado la sincronización NTP.