ESP32 + BME280, enviando datos en minutos.
Receta Arduino completa para BME280 con MQTT/TLS. Genera la credencial, configura el Wi-Fi y flashea la placa.
1. Instala las librerías
Instala “esp32 by Espressif Systems”, “PubSubClient by Nick O’Leary” y “Adafruit BME280 Library” desde Arduino IDE. Acepta las dependencias que solicite.
2. Conecta el sensor
Conecta el módulo por I2C. El sketch prueba primero la dirección 0x76; cámbiala a 0x77 si esa es la dirección de tu placa.
Métricas enviadas: temperature, humidity, pressure_hpa
3. Genera una credencial real
Crea un dispositivo en Devicecamp y elige ESP32 Arduino. Selecciona este sensor antes de copiar o descargar el sketch; la versión generada incluye la identidad y los topics MQTT exactos.
4. Revisa la plantilla pública
MQTT_CLIENT_ID, MQTT_USERNAME y MQTT_PASSWORD son marcadores en esta página pública.
// Devicecamp ESP32 MQTT/TLS starter
// Install "esp32 by Espressif Systems" and "PubSubClient by Nick O'Leary".
// Also install "Adafruit BME280 Library" and its prompted dependencies.
// 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>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.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);
Adafruit_BME280 bme;
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() {
const float temperature = bme.readTemperature();
const float humidity = bme.readHumidity();
const float pressureHpa = bme.readPressure() / 100.0F;
if (isnan(temperature) || isnan(humidity) || isnan(pressureHpa)) {
Serial.println("BME280 read failed");
return;
}
const String localIP = WiFi.localIP().toString();
char payload[256];
snprintf(payload, sizeof(payload),
"{\"metrics\":{\"temperature\":%.2f,\"humidity\":%.2f,\"pressure_hpa\":%.2f},\"metadata\":{\"local_ip\":\"%s\"}}",
temperature, humidity, pressureHpa, 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);
// Most BME280 breakouts use 0x76; try 0x77 if yours is not found.
if (!bme.begin(0x76)) {
Serial.println("BME280 not found. Check wiring and I2C address.");
while (true) delay(1000);
}
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;
}
}5. Flashea y comprueba
Configura WIFI_SSID y WIFI_PASSWORD, carga el sketch y abre el monitor serie a 115200 baudios. “Published” confirma el envío; las métricas aparecerán en el workbench tras el primer payload aceptado.