Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Manufacturing Technology >> Производственный процесс

Мониторинг среды в реальном времени: Arduino MKR1000 + Environment Click (BME680) и WolkAbout IoT

Компоненты и материалы

Компонент Количество
Arduino MKR1000 1
MikroE Environment Click (BME680: температурв, влажность, давление, газ, высота) 1

Инструменты

Программное обеспечение и сервисы

О проекте

Подключаем Environment Click к Arduino MKR1000 по I2C (пины 11 — SDA, 12 — SCL, VCC, GND) и отправляем данные на WolkAbout IoT Platform. Замер каждую минуту, публикация каждые 15 минут. При ошибке связи данные буферизируются во Flash-памяти (макс. 96 записей/сутки, флеш выдерживает 10 000 циклов).

Необходимые библиотеки (Library Manager или GitHub)

Настройка скетча

  1. Введите WiFi-креды (строки 33–34):
    const char* ssid = "<YOUR SSID>";
    const char* wifi_pass = "<YOUR WIFI PASSWORD>";
  2. Создайте Device Type на WolkAbout: загрузите Environment click-deviceType.json (приложен) в Device Management → Device Type Management → + Upload.
  3. Создайте Device из этого типа: имя, Connectivity Type = MQTT, галка «Create Default Semantic».
  4. Скопируйте device_key и device_password в скетч:
    const char *device_key = "device_key";
    const char *device_password = "device_password";
  5. Загрузите скетч в плату.

Дашборд на WolkAbout

  1. Visualisation and Monitoring → + Add dashboard → имя, scope = имя устройства.
  2. Add widget → Trending → выберите Temperature, Pressure, Humidity по очереди.
  3. Add widget → Reading → Altitude.
  4. Add widget → Card → Gas Resistance.
  5. Разместите виджеты как удобно.

Теперь разверните устройство в нужной среде и следите за условиями удалённо. Движок правил WolkAbout позволяет получать уведомления при выходе значений за пороги.

Программный код (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;    // каждую минуту
bool publish = false;
const byte publishEvery = 10; // каждые 10 минут
byte publishMin;

// --- Flash circular buffer ---
void _flash_store() { data.valid = true; flash_store.write(data); }
void increase_pointer(uint32_t* p) { if(*p == STORAGE_SIZE-1) *p=0; else (*p)++; }
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; }

// --- WiFi & Wolk ---
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)$", "options": null, "defaultValue": null, "required": false, "readOnly": true },
      { "name": "FIRMWARE_VERSION", "dataType": "STRING", "validationRegex": null, "options": null, "defaultValue": null, "required": false, "readOnly": true },
      { "name": "FILE_DIRECT_DOWNLOAD_ENABLED", "dataType": "BOOLEAN", "validationRegex": "^(true|false)$", "options": null, "defaultValue": null, "required": false, "readOnly": true },
      { "name": "FILE_URL_DOWNLOAD_ENABLED", "dataType": "BOOLEAN", "validationRegex": "^(true|false)$", "options": null, "defaultValue": null, "required": false, "readOnly": true }
    ]
  }
]

Полезные ссылки

Схема подключения

Environment Click подключается к пинам I2C (11, 12), VCC и GND Arduino MKR1000. Схема в разделе Schematics.

Производственный процесс

  1. Сборка Bluetooth-робота-шпиона на Arduino с управлением с телефона
  2. Датчик CO2 K30: мониторинг качества воздуха в реальном времени на Arduino
  3. FlickMote: жестовое IR-пульт для умного дома на Arduino 101 и ультразвуковом датчике
  4. DIY Arduino TV-B-Gone: универсальный пульт для выключения любого телевизора
  5. Создание пользовательских LED-часов с будильником — видно с 12 метров
  6. Find Me: Умный поисковик вещей на Arduino и Bluetooth
  7. Оптимизированные решения питания для проектов на Arduino
  8. Крестики-нолики на Arduino с матрицей MAX7219 и картонным корпусом
  9. Пошаговое руководство: сборка умного четырёхногого робота на Arduino
  10. Бесконтактный термометрический шлюз для быстрого и точного скрининга