산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Manufacturing Technology >> 제조공정

아두이노 MKR1000과 Environment Click으로 실시간 환경 모니터링

필요한 부품 및 소모품

부품 수량
아두이노 MKR1000 1
MikroE Environment Click (BME680 센서 탑재) 1

필요한 도구

앱 및 온라인 서비스

프로젝트 소개

소개

Environment Click 센서가 있어 아두이노 MKR1000에 연결하고 WolkAbout IoT Platform으로 데이터를 시각화해 보기로 했습니다. 1분마다 측정하고 15분마다 한 번씩 플랫폼에 전송합니다. 전송 실패 시(네트워크 혼잡 등) 측정 결과를 장치의 플래시 메모리에 저장해 데이터 손실을 방지합니다. 하루 최대 96회 쓰기로, 아두이노가 보장하는 10,000회 쓰기 수명 내에서 안전하게 운용 가능합니다.

하드웨어 설정

Environment Click을 MKR1000의 I2C 핀(11번 SDA, 12번 SCL), VCC, GND에 연결합니다.

소프트웨어 설정

다음 라이브러리들이 필요합니다 (아두이노 IDE 라이브러리 매니저에서 설치 또는 GitHub에서 zip 다운로드 후 추가):

'Environment monitoring' 스케치를 아두이노 IDE에 복사해 다음 설정을 수정하세요:

const char* ssid = "";
const char* wifi_pass = "";
const char *device_key = "device_key";
const char *device_password = "device_password";

WolkAbout IoT Platform에서 디바이스 생성:

  1. 첨부된 Environment click-deviceType.json을 이용해 디바이스 타입 생성 (Device Management → Device Type Management → + → Upload).
  2. 해당 타입으로 디바이스 생성: 이름 지정, Connectivity Type = MQTT, 'Create Default Semantic' 체크.
  3. 저장 시 발급되는 Device Key와 Device Password를 스케치에 입력.

대시보드 위젯 구성:

  1. Visualization and Monitoring → + Add Dashboard → 이름/범위 설정.
  2. + Add Widget → Trending 선택 → 온도(T), 습도(H), 기압(P) 각각 추가.
  3. Reading 위젯으로 고도(A) 추가, Card 위젯으로 가스 저항(GR) 추가.
  4. 위젯 자유 배치 후 저장.

이제 장치를 원하는 환경에 배치하고 원격으로 조건을 모니터링하세요.

결론

아두이노 MKR1000과 Environment Click을 WolkAbout IoT Platform에 연결해, 설치 장소의 환경 조건을 에너지 효율적으로 원격 모니터링하는 시스템을 구축했습니다. WolkAbout의 규칙 엔진을 활용하면 임계값 초과 시 알림 수신 등 확장도 가능합니다. 자세한 기능은 User Guides 참고.

코드

Environment monitoring 스케치 (Arduino)

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#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;
const byte readEvery = 1;
bool publish;
const byte publishEvery = 10;
byte publishMin;

void _flash_store() { data.valid = true; flash_store.write(data); }

void increase_pointer(uint32_t* pointer) {
    if ((*pointer) == (STORAGE_SIZE - 1)) (*pointer) = 0;
    else (*pointer)++;
}

void _init() {
    data = flash_store.read();
    if (data.valid == false) {
        data.head = 0; data.tail = 0; data.empty = true; data.full = false;
    }
}

bool _push(outbound_message_t* outbound_message) {
    if (data.full) increase_pointer(&data.head);
    memcpy(&data.outbound_messages[data.tail], outbound_message, sizeof(outbound_message_t));
    increase_pointer(&data.tail);
    data.empty = false;
    data.full = (data.tail == data.head);
    return true;
}

bool _peek(outbound_message_t* outbound_message) {
    memcpy(outbound_message, &data.outbound_messages[data.head], sizeof(outbound_message_t));
    return true;
}

bool _pop(outbound_message_t* outbound_message) {
    memcpy(outbound_message, &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 numAttempts = 0;
        while (WiFi.begin(ssid, wifi_pass) != WL_CONNECTED) {
            numAttempts++;
            if (numAttempts == 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;
}

Environment click-deviceType.json (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 }
    ]
  }
]

참고 자료

회로도

페이지 하단의 회로도 이미지 참고 (I2C 연결: MKR1000 핀 11=SDA, 12=SCL).

제조공정

  1. 블루투스 제어 아두이노 스파이봇(Spybot) 만들기
  2. K30 CO2 센서: 실시간 실내 공기질 모니터링
  3. 플릭모트(FlickMote): 스마트 홈을 위한 제스처 제어 IR 리모컨
  4. DIY 아두이노 TV B-Gone: 모든 TV를 끄는 만능 리모컨
  5. 아두이노로 만드는 대형 LED 마스터 시계 (알람 기능 포함, 12m 거리에서도 식별 가능)
  6. 아두이노와 블루투스로 만드는 스마트 물건 찾기 'Find Me'
  7. 아두이노 프로젝트를 위한 최적화된 전원 솔루션
  8. 아두이노 틱택토 게임 - MAX7219 LED 매트릭스와 골판지 케이스
  9. 스마트 아두이노 4족 보행 로봇 제작: 단계별 완벽 가이드
  10. 비접촉 체온 측정 게이트: 아두이노 MKR1000으로 구현하는 자동 발열 검사 시스템