Monitorización Ambiental en Tiempo Real con Arduino MKR1000 y Sensor Environment Click
Componentes y materiales
| Componente | Cantidad |
|---|---|
| Arduino MKR1000 | 1 |
| MikroE Environment Click (BME680) | 1 |
Herramientas necesarias
- Cables jumper hembra-hembra (Adafruit)
Software y servicios en línea
- Arduino IDE
- Plataforma IoT WolkAbout
Sobre el proyecto
Introducción
Teníamos sensores Environment Click (BME680) a mano y decidimos conectarlos al Arduino MKR1000 para visualizar datos en WolkAbout IoT Platform.
Objetivo: medir cada minuto y publicar cada 15 minutos. Si falla la publicación (red ocupada, etc.), los datos se persisten en Flash del dispositivo (buffer circular). Máx. 96 escrituras/día → minimiza desgaste Flash (garantía 10.000 ciclos).
Montaje hardware
Environment Click se conecta a pines I2C del MKR1000 (pines 11=SDA, 12=SCL), Vcc y GND.
Configuración software
Librerías necesarias (instalables desde Gestor de librerías Arduino IDE o descargando .zip de GitHub):
- WiFi101
- Adafruit Unified Sensor Driver
- Adafruit BME680 Library
- RTCZero
- FlashStorage
- WolkConnect
Copia el sketch Environment monitoring (adjunto) al IDE. Implementa buffer circular + persistencia en Flash (FlashStorage). Lectura y publicación temporizadas con RTCZero. Al conectar WiFi, hace ping a WolkAbout para obtener epoch actual y sincronizar RTC.
Una interrupción de alarma pone flags read / publish; el loop() ejecuta tareas. Para bajo consumo: WiFi en low-power mode, MKR1000 en low-power, sensor BME680 entra en low-power automáticamente hasta que se pide lectura.
Modificaciones obligatorias en el sketch
- Credenciales WiFi (líneas 33-34):
const char* ssid = "<*TU_SSID*>"; const char* wifi_pass = "<*TU_PASSWORD_WIFI*>"; - Credenciales dispositivo WolkAbout:
const char *device_key = "device_key"; const char *device_password = "device_password";
Para obtener credenciales: crea Device Type en WolkAbout (archivo Environment click-deviceType.json adjunto). En WolkAbout → Device Management > Device Type Management > + > Upload → selecciona JSON. Luego Create device: nombre, Connectivity Type = MQTT, marca Create Default Semantic. Guarda → te da device_key y device_password.
Dashboard en WolkAbout
- Ve a Visualisation and Monitoring > + Add dashboard (nombre, scope = nombre dispositivo).
- Add widget > Trending → selecciona Temperature de tu dispositivo.
- Repite para Pressure y Humidity.
- Añade widget Reading → Altitude.
- Añade widget Card → Gas Resistance.
- Reorganiza a tu gusto.
Despliega el dispositivo en el entorno elegido y monitoriza condiciones remotamente.
Conclusión
Conectando Arduino MKR1000 + Environment Click a WolkAbout IoT Platform, creas una forma eficiente energéticamente de reportar condiciones ambientales remotas. Extensible con motor de reglas de WolkAbout para notificaciones al superar umbrales. Más info en User Guides de WolkAbout.
Código
Sketch principal (Environment monitoring sketch)
#include <Adafruit_Sensor.h>
#include <Adafruit_BME680.h>
#include <bme680_defs.h>
#include <bme680.h>
#include <WiFi101.h>
#include <RTCZero.h>
#include <FlashStorage.h>
#include "WolkConn.h"
#include "MQTTClient.h"
#define STORAGE_SIZE 32
#define SEALEVELPRESSURE_HPA (1013.25)
typedef struct {
boolean valid;
outbound_message_t outbound_messages[STORAGE_SIZE];
uint32_t head;
uint32_t tail;
boolean empty;
boolean full;
} Messages;
static Messages data;
const char* ssid = "ssid";
const char* wifi_pass = "wifi_pass";
const char *device_key = "device_key";
const char *device_password = "device_password";
const char* hostname = "api-demo.wolkabout.com";
int portno = 1883;
WiFiClient espClient;
PubSubClient client(espClient);
static wolk_ctx_t wolk;
FlashStorage(flash_store, Messages);
Adafruit_BME680 bme;
RTCZero rtc;
bool read = false;
const byte readEvery = 1; // Leer cada minuto (<60)
bool publish = false;
const byte publishEvery = 10; // Publicar cada 10 min (<60)
byte publishMin;
void _flash_store() { data.valid = true; flash_store.write(data); }
void increase_pointer(uint32_t* p) { *p = (*p == STORAGE_SIZE-1) ? 0 : *p+1; }
void _init() {
data = flash_store.read();
if (!data.valid) { data.head=0; data.tail=0; data.empty=true; data.full=false; }
}
bool _push(outbound_message_t* m) {
if (data.full) increase_pointer(&data.head);
memcpy(&data.outbound_messages[data.tail], m, sizeof(outbound_message_t));
increase_pointer(&data.tail);
data.empty=false; data.full=(data.tail==data.head); return true;
}
bool _peek(outbound_message_t* m) { memcpy(m, &data.outbound_messages[data.head], sizeof(outbound_message_t)); return true; }
bool _pop(outbound_message_t* m) {
memcpy(m, &data.outbound_messages[data.head], sizeof(outbound_message_t));
increase_pointer(&data.head);
data.full=false; data.empty=(data.tail==data.head); return true;
}
bool _is_empty() { return data.empty; }
void init_wifi() {
if (WiFi.status() != WL_CONNECTED) {
while (WiFi.begin(ssid, wifi_pass) != WL_CONNECTED) delay(1000);
}
}
void setup_wifi() {
delay(10);
if (WiFi.status() != WL_CONNECTED) {
int n=0;
while (WiFi.begin(ssid, wifi_pass) != WL_CONNECTED) {
if (++n == 10) { Serial.println("Couldn't reach WiFi!"); break; }
delay(1000);
}
}
}
void setup() {
pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, LOW);
_init();
init_wifi();
wolk_init(&wolk, NULL, NULL, NULL, NULL, device_key, device_password, &client, hostname, portno, PROTOCOL_JSON_SINGLE, NULL, NULL);
wolk_init_custom_persistence(&wolk, _push, _peek, _pop, _is_empty);
if (!bme.begin()) digitalWrite(LED_BUILTIN, HIGH);
bme.setTemperatureOversampling(BME680_OS_8X);
bme.setHumidityOversampling(BME680_OS_2X);
bme.setPressureOversampling(BME680_OS_4X);
bme.setIIRFilterSize(BME680_FILTER_SIZE_3);
bme.setGasHeater(320, 150);
delay(200);
read = true; publish = true;
wolk_connect(&wolk); delay(100);
wolk_update_epoch(&wolk);
while (!wolk.pong_received) { wolk_process(&wolk); digitalWrite(LED_BUILTIN, HIGH); delay(1000); }
digitalWrite(LED_BUILTIN, LOW);
wolk_disconnect(&wolk);
rtc.begin(); rtc.setEpoch(wolk.epoch_time);
rtc.setAlarmTime(rtc.getHours(), (rtc.getMinutes()+readEvery)%60, rtc.getSeconds());
rtc.enableAlarm(rtc.MATCH_MMSS);
rtc.attachInterrupt(alarmMatch);
publishMin = (rtc.getMinutes()+publishEvery)%60;
WiFi.lowPowerMode();
}
void loop() {
if (read) {
read = false;
if (!bme.performReading()) digitalWrite(LED_BUILTIN, HIGH);
wolk_add_numeric_sensor_reading(&wolk, "T", bme.temperature, rtc.getEpoch());
wolk_add_numeric_sensor_reading(&wolk, "H", bme.humidity, rtc.getEpoch());
wolk_add_numeric_sensor_reading(&wolk, "P", bme.pressure/100.0, rtc.getEpoch());
wolk_add_numeric_sensor_reading(&wolk, "GR", bme.gas_resistance, rtc.getEpoch());
wolk_add_numeric_sensor_reading(&wolk, "A", bme.readAltitude(SEALEVELPRESSURE_HPA), rtc.getEpoch());
int alarmMin = (rtc.getMinutes()+readEvery)%60;
rtc.setAlarmMinutes(alarmMin);
delay(100);
}
if (publish) {
publish = false;
setup_wifi();
wolk_connect(&wolk);
if (!wolk.is_connected) _flash_store();
delay(100);
if (wolk_publish(&wolk) == W_TRUE) _flash_store();
publishMin = (rtc.getMinutes()+publishEvery)%60;
delay(100);
wolk_disconnect(&wolk);
delay(100);
}
delay(100);
}
void alarmMatch() {
read = true;
if (publishMin == rtc.getMinutes()) publish = true;
}
Device Type JSON (Environment click-deviceType.json)
[
{
"name": "Environment click",
"description": "Device type for the BME680 sensor on MikroElektronika's board Environment Click",
"guid": "def68ff6-63c5-4f9c-a066-bcc944b7bd2d",
"deviceType": "STANDARD",
"protocol": "JSON",
"connectivityType": null,
"firmwareUpdateType": null,
"feeds": [
{ "name": "Temperature", "reference": "T", "unit": { "name": "CELSIUS", "guid": "7c805d10-a879-11e9-83cd-0a0027000005" } },
{ "name": "Humidity", "reference": "H", "unit": { "name": "HUMIDITY_PERCENT", "guid": "7c8080a3-a879-11e9-83cd-0a0027000005" } },
{ "name": "Pressure", "reference": "P", "unit": { "name": "MILLIBAR", "guid": "7c807980-a879-11e9-83cd-0a0027000005" } },
{ "name": "Gas Resistance", "reference": "GR", "unit": { "name": "OHM", "guid": "7c805890-a879-11e9-83cd-0a0027000005" } },
{ "name": "Altitude", "reference": "A", "unit": { "name": "METRE", "guid": "7c805a59-a879-11e9-83cd-0a0027000005" } }
],
"actuators": [],
"alarms": [],
"configs": [],
"attributes": [
{ "name": "CONNECTIVITY_TYPE", "dataType": "ENUM", "validationRegex": "^(MQTT|HTTP)$", "options": ["MQTT","HTTP"], "defaultValue": "MQTT", "required": true, "readOnly": true },
{ "name": "FIRMWARE_UPDATE_ENABLED", "dataType": "BOOLEAN", "validationRegex": "^(true|false)$", "defaultValue": null, "required": false, "readOnly": true },
{ "name": "FIRMWARE_VERSION", "dataType": "STRING", "defaultValue": null, "required": false, "readOnly": true },
{ "name": "FILE_DIRECT_DOWNLOAD_ENABLED", "dataType": "BOOLEAN", "validationRegex": "^(true|false)$", "required": false, "readOnly": true },
{ "name": "FILE_URL_DOWNLOAD_ENABLED", "dataType": "BOOLEAN", "validationRegex": "^(true|false)$", "required": false, "readOnly": true }
]
}
]
Enlaces de librerías y repositorios
- WolkAbout Environment Monitoring (repo con sketch)
- Arduino WiFi101 Shield
- Adafruit Unified Sensor Driver
- Adafruit BME680 Library
- RTC Library para Arduino (SAMD21)
- FlashStorage library
- WolkConnect-Arduino
Esquemático
Ver imagen en sección "Schematics" (conexión I2C Environment Click a MKR1000).
Proceso de manufactura
- Construye un Spybot controlado por Bluetooth con Arduino
- Sensor K30 CO₂: Monitorización en tiempo real de la calidad del aire interior
- FlickMote: Mando IR por gestos para Smart Home
- TV‑B‑Gone DIY con Arduino: Mando universal para apagar cualquier TV
- Construye un Reloj Maestro LED Personalizado con Alarma – Visible desde 12 Metros
- Find Me: Localizador Inteligente de Objetos con Arduino y Bluetooth
- Soluciones de Alimentación Optimizadas para Proyectos Arduino
- Tres en raya con Arduino, matriz LED MAX7219 y carcasa de cartón
- Construye un Robot Cuadrúpedo Inteligente con Arduino: Guía Paso a Paso
- Puerta de control de temperatura sin contacto para detección rápida y precisa
- Sistema de monitorización de incendios forestales en tiempo real con alertas SMS