工業製造
産業用モノのインターネット | 工業材料 | 機器のメンテナンスと修理 | 産業プログラミング |
home  MfgRobots >> 工業製造 >  >> Manufacturing Technology >> 製造プロセス

MKR1000マルチモード環境センサーデッキ - 落雷・ガス・UV・温湿度・気圧を総合監視

必要な部品と用品

Arduino MKR1000 ×1
MOD-1016 落雷センサー(AS3935搭載) ×1
DFRobot MQ-9 ガスセンサー ×1
Adafruit 湿度・温度センサー(HTU21D-F) ×1
Adafruit 気圧/高度/温度センサー(BMP085/180) ×1
SparkFun ML8511 UVセンサー ×1
Chronodot RTC(DS3231搭載、電池バックアップ付き) ×1
SparkFun Photon Weather Shield ×1
Adafruit NeoPixel ×1
ブレッドボード ×1
ジャンパーワイヤ ×1

プロジェクト概要

天気観測が好きで、データを取得・グラフ化したくて始めました。市販のホーム気象ステーションは「今」の詳細や過去1時間・1日の集計程度ですが、もっと多くのことをArduinoでやりたいと考えました。温湿度、気圧は定番ですが、さらに風速・雨量も欲しい。I2Cを習得したら、AS3935落雷検出器のような面白いセンサーも見つかりました!

しかし、通常のArduinoではハードウェア割り込みピンが足りません。SparkFunのPhoton用ウェザーシールドも同様の制約があります。いくつかのセンサーを諦める必要がありました…。

そこでMKR1000を見つけ、8つのハードウェア割り込みがあることに気づきました。これで全部入ります!

MKR1000マルチモード環境センサーデッキ - 落雷・ガス・UV・温湿度・気圧を総合監視
MKR1000:8つのハードウェア割り込みが鍵

センサーの選び方

センサーは大きく3タイプ:

MKR1000はこれらすべてに十分なI/Oを備えています。

電圧レベルに注意! MKR1000、Photon、多くのArduinoは3.3V I/Oです。センサーが5V仕様の場合、レベルシフターが必要です。

使用センサーは一般的で、Adafruit、SparkFun、Element14等で入手可能。多くは5-10ドル。ガスセンサーは10-20ドル。落雷検出器(AS3935)はEmbedded Adventures MOD-1016(アンテナ内蔵、26ドル)を選択。Photon Weather Shieldも購入し、将来的に風速・風向計を追加予定。土壌水分センサーも検討中。センサーによっては長い配線を嫌うものがあります。

センサーのピン割り当て

使用センサーが決まったら、各ピンに割り当てます。紙に書き出し、コードのブロックコメントにも残します。

私のMKR1000ピン割り当て:

始めるにあたって

初心者の方は、使用予定のセンサーを1つずつ、例題コードから始めることをお勧めします。ライブラリをインストールし、ピン番号を自分の割り当てに合わせて修正し、プロジェクトフォルダにコピーを保存。

シリアルモニタで正常に値が読めたら次のセンサーへ。すべて動作確認できたら、本格的な統合スケッチの作成に入ります。

もともとI2C接続のRTC(Chronodot v2.1)を使ってSDカードにログを取る予定でした。MKR1000にも内蔵RTCと電池がありますが、電池のみで時刻を保持できなかったため、Chronodotも併用しています。

MKR1000マルチモード環境センサーデッキ - 落雷・ガス・UV・温湿度・気圧を総合監視
ブレッドボード上の配線。オレンジのジャンパーは3.3V電源(センサーごとに着脱可)

ライブラリについての注意

一部のライブラリはMKR1000やArduino IDE 1.6.7でそのまま動かないことがあります。例:古いATOMIC_*マクロ(AVR libc、IDE 1.6.5で削除)。中級者なら修正可能ですが、初心者にはハードルが高いです。古いライブラリは作者が不在で更新されないことも。

エラーメッセージ(オレンジ色)をよく読み、問題がライブラリ側にあるか確認してください。「arduino」とエラー文で検索し、解決策を探します。見つからなければ作者に連絡を。

個別チップを買って自作しようとしましたが、基板製作(EagleCAD + OSHPark等)ができるまで、AdafruitやSparkFunのモジュールを買うのが賢明です。ライブラリのメンテナンスが行き届いています。

WiFiと暗号化について

WiFiでデータをクラウド送信する構想もあります。MKR1000には暗号化エンジンも搭載されており、IoTでのデータ保護に重要です。今回は時間切れで実装見送りですが、「バージョン2」で取り組みます。SAMDモジュール(ボードの心臓部)のブロック図を参考に掲載。

MKR1000マルチモード環境センサーデッキ - 落雷・ガス・UV・温湿度・気圧を総合監視
SAMD21ブロック図

WiFi開始のヒント: 必ず最新版のWiFi101ライブラリを使用してください! 古いとWiFiモジュールを認識しません。Charif Mahmoudi氏の「MKR1000入門」チュートリアルで指摘されています。当時のWiFi101 GitHubはこちら。

アナログセンサーのコツ

ほとんどのアナログ出力センサーモジュールは単純な電圧出力で、analogRead()で読めます。その後、map()関数や計算で実値に変換。抵抗変化型は追加回路が必要。ポテンショメーターで「微調整」することも。

電圧から実値を求める式が必要な場合も。TMP36は摂氏で出力するよう設計されており、電圧測定後に計算で℃、さらに華氏変換。データシートを読むか、先人のコードを参考にするのが近道です。

微小電圧変化を増幅するアンプ内蔵モジュール(ロードセル、土壌水分、加速度等)は、最初からアンプ・ライブラリ付きモジュールを買うのが無難です。

I2Cデバイス使用のコツ

I2Cは「2線式インターフェース」とも呼ばれ、クロック(SCL)とデータ(SDA)の2線(+電源、GND)で通信。マスターとスレーブがデータ線を共有します。

  • 各デバイスに固有の7ビットアドレスあり。同一アドレスの複数使用は避ける(必要ならイネーブルピンで切り替え)
  • 注意: 同一アドレスで両方のVCCを切っても、SDA/SCLからパラサイト電流で動作し、誤読み取りの原因になります。イネーブルピンで制御してください。
  • 仕様上、最遠端のセンサーにSDA/SCLプルアップ抵抗が必要。モジュールによっては基板上に実装済み。
  • 古いハック:SDA/SCLそれぞれに直列抵抗(3.3Vなら220Ω、5Vなら330Ω程度)を入れると通信が安定することがあります。
MKR1000マルチモード環境センサーデッキ - 落雷・ガス・UV・温湿度・気圧を総合監視
バス終端抵抗と直列抵抗の配置違い

データの活用

現在はシリアルモニタ出力のみ。LCD追加も可能。SDカードログから、内蔵WiFiでクラウドアップロードへ移行したいところ。複数のセンサーデッキを緯度経度付きで配置すれば、落雷の三角測量まで夢が広がります!

追記(2017-11-29): MKR1000でハードウェア割り込みが動作せず苦戦中。中学3年生(13-14歳)向け教材として、Adalogger M0ボード(SDカードログ + WiFiモジュール余裕)でノウハウ蓄積中。
授業サイト

センサー作業中にIKEAのSODERSVIKランプを見つけ、中の白色LEDを20個のNeoPixelに置き換え、IRリモコン受信を追加したら面白そうだと思いました。風で雲が流れるような演出、色で温度表示、リモコンで過去12時間の温度変化を一時表示…など。

MKR1000マルチモード環境センサーデッキ - 落雷・ガス・UV・温湿度・気圧を総合監視
IKEA SODERSVIKランプ(NeoPixel化構想)

あなたは何を監視・表示したいですか?

コード

統合監視スケッチ (Arduino)

多数のセンサーを統合。NeoPixelと落雷センサーのライブラリは、AVR-libcのクラシックな割り込みロック手法が最近のIDEで削除されたため動作せず、最終スケッチからは除外しています。年内に解決見込み。

/* RTC-Weather-Sensors_v6_MKR1000 by Zonker Harris Spring 2016
 * Hooray! 8 hardware interrupts + WiFi + crypto (IoT必須!)
 * コメントはアップロード時に無視されるので、たくさん書いてOK。
 * ライブラリはプログラムメモリを消費するので注意。
 */
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP085_U.h>
#include <Adafruit_HTU21DF.h>
// #include <Adafruit_NeoPixel.h> // 割り込み問題で一時除外

Adafruit_BMP085_Unified bmp = Adafruit_BMP085_Unified(10180);
Adafruit_HTU21DF htu = Adafruit_HTU21DF();

// ピン定義
int UVOUT = A1;       // ML8511 UV
int lightOut = A2;    // TEMT6000 可視光
int tmp36out = A3;    // TMP36 温度
int mq9out = A4;      // MQ-9 ガス
int sounderPin = 6;   // ピエゾサウンダー(オンボードLED共有)
int neopixelPin = 7;  // NeoPixel出力

// Chronodot RTC変数
int addrRTC = 0x68;
int seconds, minutes, hours, day, date, month, years;
int temprtc, tempfrtc;

void setup() {
  Serial.begin(9600);
  delay(1000);
  Serial.println("i2c Sensor Deck");
  
  pinMode(UVOUT, INPUT);
  pinMode(lightOut, INPUT);
  pinMode(tmp36out, INPUT);
  pinMode(sounderPin, OUTPUT);
  pinMode(neopixelPin, OUTPUT);

  if (!bmp.begin()) {
    Serial.println("BMP085/180 not detected!");
    while(1);
  }
  displaySensorDetails();

  if (!htu.begin()) {
    Serial.println("HTU21-DF not found!");
    while(1);
  }
  
  // RTC初期設定(初回のみアンコメントしてアップロード)
  /* seconds=0; minutes=41; hours=20; day=7; date=3; month=1; years=16; initChrono(); */
}

void loop() {
  beep(50, 2);
  
  // Chronodot RTC読み取り
  Wire.beginTransmission(0x68);
  Wire.write((byte)0);
  Wire.endTransmission();
  Wire.requestFrom(0x68, 13);
  while(Wire.available()) {
    seconds = Wire.read();
    minutes = Wire.read();
    hours = Wire.read();
    day = Wire.read();
    date = Wire.read();
    month = Wire.read();
    years = Wire.read();
    // アラーム等は省略
    temprtc = Wire.read();  // 11h: 温度整数部
    tempfrtc = Wire.read(); // 12h: 温度小数部
  }
  // BCD→10進変換処理...
  get_date();
  Serial.print("ChronoDot - ");
  Serial.print(hours); Serial.print(":");
  Serial.print(minutes); Serial.print(":");
  Serial.print(seconds); Serial.print(" 20");
  Serial.print(years); Serial.print(" ");
  Serial.print(month); Serial.print(" ");
  Serial.print(date); Serial.print("\t");
  Serial.print(temprtc); Serial.println(" C");
  delay(100);

  // BMP180 気圧・温度
  sensors_event_t event;
  bmp.getEvent(&event);
  float BMPtemperature;
  bmp.getTemperature(&BMPtemperature);
  Serial.print("Temp: "); Serial.print(BMPtemperature); Serial.print(" C\t");
  if (event.pressure) {
    Serial.print("BMP180 - Pres: "); Serial.print(event.pressure); Serial.print(" hPa\t");
    float seaLevelPressure = SENSORS_PRESSURE_SEALEVELHPA;
    Serial.print("Alt: "); Serial.print(bmp.pressureToAltitude(seaLevelPressure, event.pressure)); Serial.println(" m");
  } else { Serial.println("Sensor error"); }
  delay(100);

  // HTU21-DF 湿度・温度
  float HTUtemperature = htu.readTemperature();
  Serial.print("HTU21-DF - Temp: "); Serial.print(HTUtemperature); Serial.print(" C\tHum: ");
  Serial.print(htu.readHumidity()); Serial.println("%");
  delay(100);

  // TMP36 温度
  int reading = averageAnalogRead(tmp36out);
  float tmp36voltage = 0.0032258064516129 * reading; // 3.3V/1023
  float tmp36temperatureC = (tmp36voltage - 0.5) * 100;
  Serial.print("TMP36 - temp: "); Serial.print(tmp36temperatureC); Serial.print(" C\t");
  float tmp36temperatureF = (tmp36temperatureC * 9.0 / 5.0) + 32.0;
  Serial.print(tmp36temperatureF); Serial.print(" F, out: "); Serial.print(tmp36voltage); Serial.println("v");
  delay(100);

  // TEMT6000 可視光
  int vLevel = averageAnalogRead(lightOut);
  float newVOutVolts = 0.0032258064516129 * vLevel;
  Serial.print("TEMT6000 out: "); Serial.println(vLevel);
  delay(100);

  // ML8511 UV
  int uvLevel = averageAnalogRead(UVOUT);
  float newOutVolts = 0.0032258064516129 * uvLevel;
  float uvIntensity = mapfloat(newOutVolts, 0.99, 2.8, 0.0, 15.0);
  Serial.print("ML8511 UV out: "); Serial.print(uvLevel);
  Serial.print(" / UV Intensity: "); Serial.println(uvIntensity);
  delay(100);

  // MQ-9 ガス
  int MQ9volts = analogRead(mq9out);
  Serial.print("MQ-9 Gas: "); Serial.println(MQ9volts, DEC);
  delay(100);

  Serial.println("");
  delay(3500); // ループ約5秒
}

// 補助関数群(平均化アナログ読み取り、float版map、BCD変換、RTC操作、ビープ音等)
// 詳細は元コード参照

アナログ読み取り平均化関数

SparkFunのML8511ライブラリにあった巧妙なハック。あらゆるアナログ読み取りに応用可能。Nathan Seidle氏にビールを奢りたくなるコード(Beerwareライセンス)。

int averageAnalogRead(int pinToRead) {
  byte numberOfReadings = 8;
  unsigned int runningValue = 0;
  for (int x = 0; x < numberOfReadings; x++)
    runningValue += analogRead(pinToRead);
  runningValue /= numberOfReadings;
  return runningValue;
}

I2Cバススキャナー

I2Cデバイスのアドレスが不明な場合にスキャン。既知センサーの説明も表示。

#include <Wire.h>
void setup() { Wire.begin(); Serial.begin(9600); Serial.println("\nI2C Scanner"); }
void loop() {
  byte error, address; int nDevices = 0;
  Serial.println("Scanning...");
  for (address = 1; address < 127; address++) {
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
    if (error == 0) {
      Serial.print("I2C device at 0x"); if (address<16) Serial.print("0");
      Serial.print(address, HEX);
      if (address == 3) Serial.print(" - AS3935 Lightning");
      if (address == 64) Serial.print(" - HTU21D Humidity/Temp");
      if (address == 104) Serial.print(" - DS1307 RTC (Chronodot)");
      if (address == 119) Serial.print(" - BMP180 Pressure/Temp");
      Serial.println(); nDevices++;
    } else if (error == 4) {
      Serial.print("Unknown error at 0x"); if (address<16) Serial.print("0");
      Serial.println(address, HEX);
    }
  }
  if (nDevices == 0) Serial.println("No I2C devices found\n");
  else Serial.println("done\n");
  delay(5000);
}

ML8511 デモコード(3.3V CPU対応版)

int UVOUT = A1;
void setup() { Serial.begin(9600); pinMode(UVOUT, INPUT); Serial.println("ML8511 example"); }
void loop() {
  int uvLevel = averageAnalogRead(UVOUT);
  float newOutVolts = 0.0032258064516129 * uvLevel;
  float uvIntensity = mapfloat(newOutVolts, 0.99, 2.8, 0.0, 15.0);
  Serial.print("ML8511 out: "); Serial.print(uvLevel);
  Serial.print(" / UV Intensity: "); Serial.println(uvIntensity);
  delay(100);
}

回路図

Fritzing初心者でパーツが見つからなかったため、モックアップ図を作成。

MKR1000マルチモード環境センサーデッキ - 落雷・ガス・UV・温湿度・気圧を総合監視
センサーデッキ構成イメージ

製造プロセス

  1. センサーデータをRaspberry Piに統合:実践ガイド
  2. Raspberry Piでライントラッキングセンサー – シンプルなKY-033ドア/ライン検出器
  3. GY-91モジュールでRaspberry Pi上に堅牢な温度・センサーAPIを構築
  4. Raspberry Piと湿度センサーで作る自動エアロポニックスシステム
  5. Raspberry Pi GPIOマスター:PIRモーションセンサーをB+/Model 2でインターフェース
  6. Acconeer A111パルスレーダーをRaspberry Piと統合:実践ガイド
  7. HC-SR501 PIRモーションセンサーをRaspberry Piで接続・調整・プログラミングする方法
  8. Python/MicroPythonセンサーロガー:センサーデータをGoogleスプレッドシートにシームレス保存
  9. Raspberry Pi 2からAzure Event HubsへAdafruit 10DOF IMUデータを送信(Windows 10 IoT Core)
  10. Raspberry Pi のグレースフルシャットダウンとスリープモード:DIY 電源管理回路
  11. センサーフューションが自律システムの信頼性、セキュリティ、効率を高める仕組み