Claude como ingeniero senior: diseño de una estación meteorológica con Heltec V3 y BME280
Uno de los usos más potentes de Claude es el que menos se ve en los tutoriales habituales: pedirle que investigue a fondo un problema técnico antes de escribir una sola línea de código. En esta sesión pusimos eso a prueba con un proyecto real de hardware: una estación meteorológica portátil con la Heltec WiFi LoRa 32 V3, el sensor BME280 y envío de datos a ThingSpeak por WiFi.
El prompt de partida fue deliberadamente ambicioso. Le pedimos a Claude que actuara como ingeniero senior especializado en sistemas embebidos, que consultara documentación oficial, repositorios de GitHub y foros técnicos, y que entregara un informe estructurado en seis apartados: esquema eléctrico, selección de librerías con justificación, problemas frecuentes reportados por otros usuarios, ejemplo de código moderno, referencias utilizadas e información contradictoria encontrada. Esta última instrucción, señalar dónde hay contradicción y razonar cuál es más fiable, es la que más diferencia al resultado de una búsqueda convencional.
El informe que entregó Claude tiene valor real antes de tocar el soldador. El punto más crítico del esquema eléctrico es la coexistencia en el mismo bus I2C del OLED integrado en la placa (dirección 0x3C, pines GPIO17/18 en la V3) y el BME280 (dirección 0x76 o 0x77): como las direcciones son distintas, no hay conflicto y no hace falta un segundo bus. Pero Claude también señaló el error número uno que arruina la mayoría de los primeros proyectos: usar los pines I2C de la versión V2 en una placa V3, donde cambiaron. El segundo error clásico es no encender el pin Vext (GPIO36 en LOW) antes de inicializar el OLED, lo que deja la pantalla permanentemente en negro.
En el apartado de librerías Claude identificó una controversia real que circula por los foros: qué librería usar para el OLED de la Heltec. La oficial del fabricante integra OLED y LoRa en un solo paquete, pero arrastra dependencias pesadas y ha roto proyectos con cambios de versión. La recomendación actual de la comunidad es usar U8g2 o la librería no oficial heltec_unofficial de ropg, más ligera y específica para la V3. Claude lo explicó, indicó de dónde venía la contradicción y eligió entre opciones justificando la elección, que es exactamente lo que haría un colega con experiencia.
El código de ejemplo que generó implementa el modo forzado del BME280: en lugar de mantener el sensor midiendo de forma continua (lo que produce un sesgo de temperatura de hasta 3 °C por autocalentamiento), se dispara una sola conversión, se leen los datos y el sensor vuelve a dormir. Después de mostrar los datos en el OLED y enviarlos a ThingSpeak por HTTP, el ESP32 entra en deep sleep durante 10 minutos. El ciclo completo (despertar, medir, enviar, dormir) ocupa pocos segundos de los 600 que dura el intervalo, lo que alarga considerablemente la autonomía de la batería LiPo.
Hay un matiz importante que Claude también señaló y que no aparece en la mayoría de los tutoriales de ThingSpeak: la cuenta gratuita impone un mínimo de 15 segundos entre actualizaciones de un canal, y enviar más rápido no produce error sino un rechazo silencioso (respuesta “0”). Con un ciclo de 10 minutos el límite es irrelevante, pero es el tipo de detalle que solo aparece cuando alguien ha tropezado con él.
La lección del vídeo no es específicamente sobre BME280 ni sobre la Heltec V3. Es sobre el flujo de trabajo: antes de un proyecto de hardware con componentes nuevos, una sesión de investigación bien promteada con Claude produce en minutos el equivalente a horas de lectura de foros, datasheets y repositorios de GitHub. El informe no reemplaza la verificación con el hardware real, y Claude lo deja claro en el propio documento señalando qué datos deben confirmarse contra las fuentes primarias antes de fabricar. Pero llegar al soldador con esa base cambia completamente la experiencia del proyecto.
Informe técnico: Estación meteorológica científica con Heltec WiFi LoRa 32 V3, BME280 y ThingSpeak
Nota metodológica importante (leer primero): El entorno de investigación de este informe no dispuso de acceso web en vivo (las herramientas disponibles se limitaban a catálogos de Autodesk y AWS, no aplicables a electrónica embebida). En consecuencia, las cifras de pines, polaridades y factores están reconstruidas a partir de conocimiento técnico de ingeniería embebida y deben confirmarse contra las fuentes primarias listadas en el apartado 5 —en especial el pinout oficial de la V3, el ejemplo de batería de Heltec y el datasheet de Bosch— antes de fabricar la placa. A lo largo del informe se indica el nivel de confianza de cada dato crítico.
TL;DR
- Es un proyecto perfectamente viable: el BME280 comparte el bus I2C interno del OLED (SDA=GPIO17 / SCL=GPIO18) sin conflicto de direcciones (OLED = 0x3C, BME280 = 0x76/0x77), se usa el BME280 en modo forzado para minimizar el autocalentamiento, y se envía a ThingSpeak por HTTP o MQTT respetando el límite mínimo de 15 s entre actualizaciones.
- Los dos errores que arruinan la mayoría de los primeros proyectos con la V3 son: (1) usar pines I2C de la V2 —la V3 los cambió—, y (2) no alimentar el OLED por Vext (GPIO36, activo en LOW), lo que deja la pantalla en negro.
- Recomendación de librerías: Adafruit_BME280 (sensor), HTTPClient nativo o la librería oficial de ThingSpeak (envío), y para el OLED U8g2 o la moderna heltec_unofficial (ropg) en lugar de la pesada librería oficial de Heltec.
Key Findings
- Coexistencia I2C resuelta por direccionamiento: OLED (0x3C) y BME280 (0x76 o 0x77) tienen direcciones distintas, por lo que comparten un solo bus I2C sin conflicto. No hace falta un segundo bus.
- La V3 no es la V2: cambia el MCU (ESP32‑S3 frente al ESP32 clásico), el radio (SX1262 frente a SX1276) y los pines del OLED. La mayor parte de la información contradictoria en internet proviene de mezclar ambas generaciones.
- El autocalentamiento se controla con modo forzado, no con librerías: una medición puntual seguida de sueño elimina casi todo el sesgo térmico; el resto se corrige con un offset calibrado in situ.
- El OLED de la V3 se alimenta por Vext, no directamente; olvidar encender Vext es la causa clásica de “pantalla en blanco”.
- ThingSpeak gratuito exige ≥15 s entre envíos; con un ciclo de 10 minutos el límite es irrelevante, pero la Write API Key debe ser la correcta.
- El brownout tras deep sleep es el fallo de campo más habitual en montajes con batería: el pico de corriente de WiFi al despertar puede reiniciar el chip si la LiPo o el desacoplo son débiles.
Details
1. Esquema eléctrico
1.1 La placa Heltec WiFi LoRa 32 V3. Monta un MCU ESP32‑S3FN8 (no el ESP32 clásico de la V2) y un transceptor LoRa Semtech SX1262. Incorpora una pantalla OLED SSD1306 de 0,96″, 128×64, monocromo gobernada por I2C, más un gestor de carga LiPo con conector JST y carga por USB‑C. (Confianza alta en el conjunto; confirmar variante exacta del módulo en heltec.org.)
1.2 Bus I2C y coexistencia OLED + BME280. El punto crítico: en la V3 el OLED interno está cableado a pines dedicados que cambiaron respecto a la V2. Los valores documentados para la V3 son:
- SDA_OLED = GPIO17
- SCL_OLED = GPIO18
- RST_OLED = GPIO21
(Confianza media‑alta; verificar en el pinout oficial.) El OLED responde a 0x3C; el BME280 a 0x76 (SDO a GND) o 0x77 (SDO a VDDIO/3,3 V). Como las direcciones son distintas, ambos dispositivos conviven en el mismo bus I2C (GPIO17/GPIO18): conecta SDA del BME280 a GPIO17 y SCL a GPIO18. El maestro I2C direcciona a cada esclavo por su dirección, por lo que no hay colisión.
Alternativa: usar un segundo bus (Wire1) en GPIO libres (p. ej. GPIO41/42 o GPIO45/46) para aislar el sensor. Es innecesario aquí, pero útil si hay ruido o cables largos.
1.3 Dirección I2C del BME280. Los módulos genéricos tipo GY‑BME280 suelen venir con SDO a GND → 0x76; los breakouts de Adafruit vienen a 0x77. Ejecuta un I2C scanner al arrancar para confirmar. Aviso: hay módulos vendidos como “BME280” que en realidad son BMP280 (sin humedad); una lectura de humedad nula o inexistente lo delata.
1.4 Alimentación del BME280. VDD 1,71–3,6 V y VDDIO 1,2–3,6 V, por lo que 3,3 V es el valor correcto. Dos opciones:
- Desde el pin 3V3 (siempre activo): sencillo, pero el sensor consume durante el deep sleep.
- Desde Vext (control por GPIO36, activo en LOW): permite apagar el sensor durante el sueño y ahorrar batería. Recomendado para uso portátil.
1.5 Gestión de batería LiPo en la V3. Conector JST para LiPo de 1 celda (3,7 V nominal) y carga por USB‑C. Lectura de tensión:
- VBAT_Read = GPIO1 (a través de un divisor resistivo).
- ADC_Ctrl = GPIO37: hay que activarlo para habilitar el divisor antes de leer y desactivarlo después para no drenar batería.
Existe controversia sobre la polaridad de ADC_Ctrl y sobre el factor multiplicador del divisor (comúnmente citado en torno a ×4,9), que varían según la revisión de placa. Usa la función de ejemplo de Heltec (readVoltage/getBatteryVoltage) como referencia y calíbrala con un multímetro. (Confianza media; dependiente de revisión.)
1.6 Protección para uso exterior.
- Abrigo meteorológico (radiation shield): imprescindible. La radiación solar directa sobre el sensor falsea la temperatura varios grados. Usa un abrigo tipo Stevenson o los platos apilados impresos en 3D con ventilación natural.
- Separación térmica placa‑sensor: el ESP32‑S3, el regulador y el IC de carga generan calor. Coloca el BME280 en un brazo separado con cable, físicamente alejado de la placa y fuera de su misma cavidad de aire.
- Humedad/condensación: protege la electrónica en caja IP con respiradero de membrana (tipo Gore‑Tex). El sensor necesita contacto con el aire exterior (rejilla); la electrónica no.
- Ventilación: la ventilación forzada (mini ventilador) es lo ideal en estaciones científicas; para uso divulgativo, un abrigo pasivo bien ventilado es suficiente.
2. Selección de bibliotecas
2.1 BME280.
- Adafruit_BME280 (+ Adafruit_Sensor + Adafruit_BusIO): la más robusta, documentada y mantenida; soporta modo forzado, oversampling e IIR. Recomendada.
- SparkFun BME280: también buena, API algo distinta.
- Seeed Grove BME280: válida en ecosistema Grove, menos flexible en bajo consumo.
- API oficial de Bosch (BME280_driver): máxima fidelidad al datasheet pero verbosa; innecesaria para uso divulgativo.
Justificación: Adafruit_BME280 ofrece el mejor equilibrio entre facilidad, soporte de modo forzado (clave contra el autocalentamiento) y comunidad amplia.
2.2 ThingSpeak.
- HTTPClient nativo (ESP32): control total, sin dependencias, ideal para un GET a
/update. Recomendado por simplicidad y depurabilidad. - Librería oficial ThingSpeak (MathWorks): cómoda (
setField(),writeFields()), gestiona el formateo; útil si envías muchos campos. - PubSubClient (MQTT): recomendable si priorizas consumo energético (menos overhead que HTTPS) o varios canales; requiere credenciales de dispositivo MQTT de ThingSpeak.
Justificación: para una estación que despierta, envía unos pocos campos y vuelve a dormir, HTTPClient es lo más ligero y depurable. MQTT gana si se prioriza el consumo o el tiempo de conexión.
2.3 OLED (punto de mayor controversia).
- Librería oficial “Heltec ESP32 Dev‑Boards / Series Dev‑boards”: gestiona OLED + LoRa + batería, pero es pesada, con cambios de versión que rompen sketches y dependencias arrastradas.
- U8g2 (olikraus): la opción más robusta y universal para SSD1306; tú gestionas Vext y pines.
- ThingPulse esp8266-oled-ssd1306 (SSD1306Wire): ligera y específica, muy usada en placas Heltec/TTGO.
- Adafruit_SSD1306 + Adafruit_GFX: funciona, pero debes gestionar manualmente Vext, RST y pines.
- heltec_unofficial (ropg): librería moderna que envuelve radio + OLED + batería específicamente para la V3 con mínimo boilerplate; muy recomendada en foros recientes.
Recomendación: si no usas LoRa en este proyecto, evita la librería oficial pesada y usa U8g2 o ThingPulse SSD1306Wire; si quieres experiencia “todo integrado” moderna para la V3, usa heltec_unofficial (ropg). En cualquier caso, enciende Vext (GPIO36 LOW) y pulsa el reset del OLED (GPIO21) antes de inicializar.
2.4 Deep sleep en ESP32‑S3.
esp_sleep_enable_timer_wakeup(us)+esp_deep_sleep_start()ponen el chip en sueño profundo.- Tras deep sleep el ESP32 arranca por
setup()desde cero (no continúaloop()); usa memoria RTC_DATA_ATTR para conservar contadores. - Apaga radio, periféricos y Vext antes de dormir.
- El consumo en deep sleep del ESP32‑S3 es del orden de decenas de µA, frente a decenas/centenares de mA despierto con WiFi.
3. Problemas frecuentes (LEER ANTES DE PROGRAMAR)
- Pines I2C equivocados (V2 vs V3): el error número uno. Los sketches de la V2 usan otros SDA/SCL; en la V3 son GPIO17/GPIO18 (OLED). Resultado: pantalla en blanco o sensor no detectado.
- OLED sin alimentar por Vext: en la V3 el OLED se alimenta por Vext. Si no pones GPIO36 en LOW (activo bajo) antes de inicializar, la pantalla queda negra. Junto al reset (GPIO21), es la causa clásica de “OLED no funciona”.
- Autocalentamiento del BME280: en modo normal continuo el sensor puede leer del orden de +1 a +3 °C por encima de la temperatura real, agravado por el calor de la placa. Solución: modo forzado con medición puntual cada X minutos, oversampling ×1 y separación física del sensor.
- Reconexión WiFi / brownout tras deep sleep: el pico de corriente de la radio al despertar (~300–500 mA transitorios) puede disparar el detector de brownout con baterías débiles o divisores limitantes, provocando bucles de reinicio. Mitigaciones: condensador de bulk en el riel, LiPo con capacidad suficiente, arrancar WiFi tras estabilizar y, como último recurso, ajustar el detector de brownout. La reconexión es completa tras deep sleep; usar IP estática + canal/BSSID cacheado acelera y reduce el tiempo despierto.
- Librería Heltec: versiones y compatibilidad: los cambios entre versiones rompen ejemplos, y mezclar la oficial con U8g2/Adafruit provoca conflictos de pines/bus. Fija una versión y no mezcles librerías de OLED.
- ThingSpeak: rate limiting y autenticación: la cuenta gratuita impone mínimo 15 s entre actualizaciones de un canal; enviar más rápido devuelve
0(rechazado). Usa la Write API Key del canal (no la de lectura ni la de usuario). Con MQTT necesitas credenciales de dispositivo MQTT.
4. Ejemplo de código moderno (Arduino / ESP32‑S3)
Diseño: BME280 en modo forzado en el bus I2C del OLED (GPIO17/18), lectura de T/H/P, visualización en OLED, envío por HTTPClient a ThingSpeak y deep sleep de 10 minutos. Usa U8g2 (portable) y gestiona Vext.
/*
* Estación meteorológica Heltec WiFi LoRa 32 V3 + BME280 + ThingSpeak
* OLED: SSD1306 128x64 (I2C 0x3C) en SDA=GPIO17, SCL=GPIO18, RST=GPIO21
* OLED alimentado por Vext = GPIO36 (ACTIVO EN LOW)
* BME280 en el MISMO bus I2C (dir 0x76 o 0x77), modo FORZADO
* Envío HTTP a ThingSpeak; deep sleep 10 min
*/
#include <Wire.h>
#include <U8g2lib.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <WiFi.h>
#include <HTTPClient.h>
// ---------- Pines Heltec V3 ----------
#define SDA_OLED 17
#define SCL_OLED 18
#define RST_OLED 21
#define VEXT_CTRL 36 // LOW = Vext ON (alimenta OLED)
// ---------- WiFi / ThingSpeak ----------
const char* WIFI_SSID = "TU_SSID";
const char* WIFI_PASS = "TU_PASSWORD";
const char* TS_WRITE_KEY = "TU_WRITE_API_KEY";
const char* TS_HOST = "http://api.thingspeak.com/update";
// ---------- Sueño ----------
#define uS_TO_S 1000000ULL
#define SLEEP_MINUTES 10
// ---------- Corrección autocalentamiento (calibrar in situ) ----------
const float TEMP_OFFSET = 0.0; // ej. -1.0 si detectas +1°C de sesgo
Adafruit_BME280 bme; // I2C
uint8_t bmeAddr = 0x76;
// OLED por HW I2C (U8g2)
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /*reset=*/ RST_OLED, SCL_OLED, SDA_OLED);
RTC_DATA_ATTR int bootCount = 0;
void vextON() { pinMode(VEXT_CTRL, OUTPUT); digitalWrite(VEXT_CTRL, LOW); delay(50); }
void vextOFF() { pinMode(VEXT_CTRL, OUTPUT); digitalWrite(VEXT_CTRL, HIGH); }
void oledMsg(const String& l1, const String& l2 = "", const String& l3 = "") {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_6x12_tr);
u8g2.drawStr(0, 12, l1.c_str());
if (l2.length()) u8g2.drawStr(0, 28, l2.c_str());
if (l3.length()) u8g2.drawStr(0, 44, l3.c_str());
u8g2.sendBuffer();
}
bool initBME() {
// Prueba 0x76 y luego 0x77 (gestion de error de direccion)
if (bme.begin(0x76, &Wire)) { bmeAddr = 0x76; }
else if (bme.begin(0x77, &Wire)) { bmeAddr = 0x77; }
else return false;
// Config "weather monitoring" Bosch: modo forzado, oversampling x1, filtro off
bme.setSampling(Adafruit_BME280::MODE_FORCED,
Adafruit_BME280::SAMPLING_X1, // temp
Adafruit_BME280::SAMPLING_X1, // pres
Adafruit_BME280::SAMPLING_X1, // hum
Adafruit_BME280::FILTER_OFF);
return true;
}
bool connectWiFi(uint32_t timeoutMs = 15000) {
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
uint32_t t0 = millis();
while (WiFi.status() != WL_CONNECTED && millis() - t0 < timeoutMs) {
delay(300);
}
return WiFi.status() == WL_CONNECTED;
}
bool sendThingSpeak(float t, float h, float p) {
if (WiFi.status() != WL_CONNECTED) return false;
HTTPClient http;
String url = String(TS_HOST) + "?api_key=" + TS_WRITE_KEY +
"&field1=" + String(t, 2) +
"&field2=" + String(h, 2) +
"&field3=" + String(p, 2);
http.begin(url);
int code = http.GET();
String payload = http.getString(); // entry id, o "0" si rechazado
http.end();
return (code == 200 && payload.toInt() > 0);
}
void goToSleep() {
vextOFF(); // apaga OLED/sensor
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
esp_sleep_enable_timer_wakeup(SLEEP_MINUTES * 60ULL * uS_TO_S);
esp_deep_sleep_start(); // reinicia por setup() al despertar
}
void setup() {
Serial.begin(115200);
bootCount++;
vextON(); // 1) alimenta OLED y BME280 por Vext
Wire.begin(SDA_OLED, SCL_OLED); // 2) bus I2C compartido
u8g2.begin(); // 3) init OLED
oledMsg("Estacion meteo", "Boot #" + String(bootCount), "Iniciando...");
// --- Sensor ---
if (!initBME()) {
oledMsg("ERROR", "BME280 no detectado", "Revisar cableado");
delay(3000);
goToSleep(); // reintenta en el siguiente ciclo
}
bme.takeForcedMeasurement(); // dispara una medicion en modo forzado
float t = bme.readTemperature() + TEMP_OFFSET;
float h = bme.readHumidity();
float p = bme.readPressure() / 100.0F; // hPa
oledMsg("T: " + String(t,1) + " C",
"H: " + String(h,1) + " %",
"P: " + String(p,1) + " hPa");
// --- WiFi + envio ---
if (connectWiFi()) {
bool ok = sendThingSpeak(t, h, p);
oledMsg("T:" + String(t,1) + " H:" + String(h,0),
"P:" + String(p,0) + " hPa",
ok ? "ThingSpeak OK" : "TS FALLO");
} else {
oledMsg("WiFi FALLO", "Sin conexion", "Duermo y reintento");
}
delay(2500); // deja ver la pantalla
goToSleep();
}
void loop() { /* nunca se ejecuta: deep sleep reinicia por setup() */ }
Notas del código:
- Se prueba 0x76 y 0x77 automáticamente (gestión de error de dirección).
- Modo forzado:
takeForcedMeasurement()dispara una sola conversión y el sensor vuelve a dormir → mínimo autocalentamiento. TEMP_OFFSETpermite corregir el sesgo por autocalentamiento tras calibrar contra un termómetro de referencia.- El límite de 15 s de ThingSpeak es trivial de respetar con ciclo de 10 min.
- Gestión de errores: sensor no detectado (mensaje + reintento tras dormir), WiFi caído (mensaje y reintento en el siguiente ciclo), y verificación de la respuesta de ThingSpeak (
payload.toInt() > 0). - Para MQTT usarías PubSubClient contra el broker MQTT de ThingSpeak con credenciales de dispositivo, publicando en
channels/<ID>/publish.
5. Referencias (fuentes primarias a verificar)
- Documentación oficial Heltec V3: https://heltec.org/project/wifi-lora-32-v3/
- Docs y pinout de Heltec: docs.heltec.org (diagrama de pinout WiFi LoRa 32 V3)
- Repositorio oficial: github.com/HelTecAutomation/Heltec_ESP32
- Librería no oficial para V3: github.com/ropg/heltec_esp32_lora_v3
- U8g2: github.com/olikraus/u8g2 · ThingPulse SSD1306: github.com/ThingPulse/esp8266-oled-ssd1306
- Adafruit BME280: github.com/adafruit/Adafruit_BME280_Library · Guía: learn.adafruit.com/adafruit-bme280-humidity-barometric-pressure-temperature-sensor-breakout
- Datasheet Bosch BME280 (referencia BST‑BME280‑DS002)
- ThingSpeak REST API (Write Data): es.mathworks.com/help/thingspeak · api.thingspeak.com
- Foros: r/esp32 (Reddit), esp32.com, foro Heltec (community.heltec.cn), Arduino Forum, Stack Overflow
6. Información contradictoria
- Pines I2C de la V3: hay confusión porque circulan pinouts de la V2. El consenso para la V3 es SDA=GPIO17, SCL=GPIO18, RST=GPIO21 para el OLED interno. Prioriza el pinout oficial de heltec.org sobre blogs antiguos.
- Polaridad de Vext: la mayoría de fuentes indican activo en LOW (GPIO36 LOW enciende Vext/OLED). Algún ejemplo antiguo lo invierte; verifica con el ejemplo oficial de tu versión.
- Lectura de batería (ADC_Ctrl y factor divisor): discrepancias sobre la polaridad de GPIO37 y el multiplicador (~×4,9). Depende de la revisión de placa; calibra con multímetro y usa la función de ejemplo de Heltec.
- Qué librería OLED usar: contradicción real entre “usa la oficial de Heltec” (integra LoRa) y “evítala, usa U8g2/ThingPulse/ropg”. Mi valoración: para un proyecto sin LoRa como éste, la oficial añade peso y fragilidad; U8g2 o heltec_unofficial (ropg) son más fiables y mantenibles hoy.
- Magnitud del autocalentamiento: las cifras varían (+0,5 a +3 °C) según montaje y modo. Es específico de cada instalación: la única solución fiable es medir el sesgo in situ y aplicar offset, además de usar modo forzado.
Recommendations
- Valida el hardware antes que nada: carga un I2C scanner y confirma que ves 0x3C (OLED) y 0x76/0x77 (BME280) en GPIO17/18 con Vext en LOW. No escribas más código hasta que el scanner los detecte. (Umbral de avance: ambos dispositivos visibles en el bus.)
- Usa modo forzado del BME280 desde el primer día y separa físicamente el sensor de la placa con un brazo de cable.
- Fija versiones de librerías (Adafruit_BME280 + U8g2 o ropg) y no mezcles librerías de OLED en el mismo proyecto.
- Calibra el offset de temperatura comparando con un termómetro de referencia durante 24–48 h antes de dar por buenos los datos; ajusta
TEMP_OFFSET. - Presupuesto energético: mide el consumo en deep sleep; si aparecen reinicios por brownout, añade condensador de bulk al riel y verifica capacidad/estado de la LiPo. (Umbral: si observas reinicios en el arranque de WiFi, actúa sobre el desacoplo antes que sobre el software.)
- ThingSpeak: usa la Write API Key correcta y mantén ≥15 s entre envíos (trivial con ciclo de 10 min). Considera MQTT si buscas minimizar el tiempo de radio y alargar batería.
Umbrales que cambian la decisión: si necesitaras precisión de laboratorio (<±0,3 °C), el BME280 no basta —usa un sensor con abrigo de ventilación activa y una referencia trazable—; si necesitas LoRa además de WiFi, entonces sí conviene la librería oficial de Heltec o la de ropg, que integran el SX1262.
Caveats
- Limitación de verificación: sin acceso web en vivo, las cifras de pines, polaridades y factores se han reconstruido desde conocimiento técnico y deben confirmarse contra las fuentes primarias del apartado 5 (pinout oficial de la V3, ejemplo de batería de Heltec y datasheet de Bosch) antes de fabricar la placa.
- Los pines GPIO17/18/21/36/37/1 y el factor de batería dependen de la revisión de placa; valida con tu unidad concreta.
- La magnitud del autocalentamiento y el offset de temperatura son específicos de cada montaje; calibra in situ y no extrapoles cifras de otros proyectos.
- El ejemplo de código es un punto de partida funcional pero no está probado en tu hardware concreto; verifica polaridad de Vext y direcciones I2C con el I2C scanner antes de confiar en las lecturas.



