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

Arduinoベースの信頼性の高いワイヤレス気象観測ステーションを構築する

このチュートリアルでは、Arduinoベースのワイヤレス気象観測ステーションの作り方を学びます。以下の動画をご覧いただくか、書面によるチュートリアルをお読みください。

概要

屋外の温度と湿度はDHT22センサーで測定し、このデータをNRF24L01トランシーバーモジュールで屋内ユニットに無線送信します。屋内ユニットにもDHT22センサーがあり室内の温湿度を測定、さらにDS3231リアルタイムクロックモジュールでArduinoの電源が切れても時刻を保持します。すべてのデータは0.96インチOLEDディスプレイに表示されます。

Arduinoベースの信頼性の高いワイヤレス気象観測ステーションを構築する

Arduinoワイヤレス気象観測ステーションの回路図

回路図とプロジェクトの仕組みを見てみましょう。各モジュールの詳細なチュートリアルは既にありますので、詳細は以下をご覧ください:NRF24L01チュートリアル、DHT22チュートリアル、DS3231チュートリアル。

Arduinoベースの信頼性の高いワイヤレス気象観測ステーションを構築する

必要なコンポーネントは以下のリンクから入手できます:

  • NRF24L01トランシーバーモジュール
  • DHT22センサー
  • DS3231リアルタイムクロック Amazon / Banggood / AliExpress
  • Arduino Nano

リアルタイムクロックモジュールとOLEDディスプレイはI2CプロトコルでArduinoと通信するため、Arduino NanoのI2Cピン(アナログピン4と5)に接続します。NRF24L01トランシーバーのすぐ隣にコンデンサを配置して電源を安定化し、DHT22のデータピンにはプルアップ抵抗を接続して正常動作を確保しています。

電源は屋内ユニットに12V DCアダプター、屋外ユニットには2つのリチウムイオンバッテリー(約7.5V)を使用。データを定期的に送信し、その間はArduinoをスリープモード(消費電流約7mA)にするため、屋外ユニットは約10日間動作可能です。

カスタム設計PCB

電子部品を整理するため、回路図に基づいてEasyEDA無料オンライン回路設計ソフトでカスタムPCBを設計しました。同じPCBを屋内・屋外両ユニットで使用でき、Arduinoのプログラムのみを変えれば済みます。

Arduinoベースの信頼性の高いワイヤレス気象観測ステーションを構築する

設計完了後、製造用のガーバーファイルをエクスポートします。Arduinoワイヤレス気象観測ステーションのEasyEDAプロジェクトファイルはこちらで確認できます。

ガーバーファイルをJLCPCB(この動画のスポンサー)で注文します。

Arduinoベースの信頼性の高いワイヤレス気象観測ステーションを構築する

ガーバーファイルをドラッグ&ドロップしてアップロードし、ガーバービューアーでPCBを確認。問題なければ希望の仕様を選んで注文します。JLCPCB初回注文なら最大10枚まで2ドルで入手可能です。

Arduinoベースの信頼性の高いワイヤレス気象観測ステーションを構築する

数日後、PCBが到着。品質は素晴らしく、設計通りの仕上がりです。

Arduinoベースの信頼性の高いワイヤレス気象観測ステーションを構築する

ピンヘッダーをPCBにはんだ付けして組み立てを開始。これで必要に応じて部品の着脱が容易になります。

Arduinoベースの信頼性の高いワイヤレス気象観測ステーションを構築する

コンデンサとプルアップ抵抗も挿入・はんだ付け。これでPCBのピンヘッダーにコンポーネントを取り付けるだけで済みます。

Arduinoベースの信頼性の高いワイヤレス気象観測ステーションを構築する

次にケース制作。8mm厚のMDFボードを丸ノコでカットしてサイズ合わせ。

Arduinoベースの信頼性の高いワイヤレス気象観測ステーションを構築する

正確な温湿度測定のため、ケース側面に空気が入るスロットをドリルとヤスリで加工(屋内・屋外両ユニット)。

Arduinoベースの信頼性の高いワイヤレス気象観測ステーションを構築する

OLEDディスプレイ用のスロットを前面パネルに、装飾用のアルミ板もカット。

Arduinoベースの信頼性の高いワイヤレス気象観測ステーションを構築する

木工用ボンドとクランプ、ネジでケースを組み立て。

Arduinoベースの信頼性の高いワイヤレス気象観測ステーションを構築する

スプレー塗装で仕上げ(屋外は白、屋内は黒)。乾燥後、PCBをケースに収納。

Arduinoベースの信頼性の高いワイヤレス気象観測ステーションを構築する

屋内ユニット背面にDCジャックと電源スイッチ、屋外ユニットはジャンパーワイヤーで簡易スイッチとしています。

Arduinoベースの信頼性の高いワイヤレス気象観測ステーションを構築する

ハードウェアは完成。あとはプログラムの仕組みを見ていきましょう。

Arduinoワイヤレス気象観測ステーションのコード

屋外ユニット(送信側)のコード

/* Arduino Wireless Communication Tutorial
   Outdoor unit - Transmitter
   by Dejan Nedelkovski, www.HowToMechatronics.com
   Libraries: NRF24L01 - TMRh20/RF24, https://github.com/tmrh20/RF24/
              DHT22 - DHTlib, https://github.com/RobTillaart/Arduino/tree/master/libraries/DHTlib
              LowPower - https://github.com/rocketscream/Low-Power */
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <dht.h>
#include <LowPower.h>

#define dataPin 8 // DHT22データピン
dht DHT; // DHTオブジェクト作成
RF24 radio(10, 9); // CE, CSN
const byte address[6] = "00001";
char thChar[32] = "";
String thString = "";

void setup() {
  radio.begin();
  radio.openWritingPipe(address);
  radio.setPALevel(RF24_PA_MIN);
  radio.stopListening();
}

void loop() {
  int readData = DHT.read22(dataPin); // センサーからデータ読取
  int t = DHT.temperature; // 温度取得
  int h = DHT.humidity;    // 湿度取得
  thString = String(t) + String(h);
  thString.toCharArray(thChar, 12); // 文字配列に変換
  // 屋内ユニットへ無線送信
  for (int i = 0; i <= 3; i++) { // 確実に届くよう3回送信
    radio.write(&thChar, sizeof(thChar));
    delay(50);
  }
  // 2分間スリープ(15×8秒=120秒)
  for (int sleepCounter = 15; sleepCounter > 0; sleepCounter--) {
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
  }
}

説明: 屋外ユニットは送信側なので、RF24ライブラリ、DHTライブラリ、スリープモード用のLowPowerライブラリをインクルード。インスタンス、ピン、変数を定義後、setup()で無線通信アドレスを初期化。loop()ではDHT22から温湿度を読み取り、整数値を文字列に結合して文字配列に変換、radio.write()で送信。forループで3回送信し受信側の取りこぼしを防止。最後にLowPowerライブラリでArduinoをスリープさせ消費電力を最小化。

屋内ユニット(受信側)のコード

/* Arduino Wireless Communication Tutorial
   Indoor unit - Receiver
   by Dejan Nedelkovski, www.HowToMechatronics.com
   Libraries: DS3231 - https://www.rinkydinkelectronics.com/library.php?id=73
              U8G2 - https://github.com/olikraus/u8g2 */
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <dht.h>
#include <DS3231.h>
#include <U8g2lib.h>
#include <Wire.h>

#define dataPin 8 // DHT22センサー
dht DHT;
DS3231 rtc(SDA, SCL);
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
RF24 radio(10, 9);
const byte address[6] = "00001";
char text[6] = "";
int readDHT22, t, h;
String inTemp, inHum, outTemp, outHum;
String rtcTime, rtcDate;
int draw_state = 0;
unsigned long previousMillis = 0;
long interval = 3000;

// 温度アイコンビットマップ
#define Temperature_20Icon_width 27
#define Temperature_20Icon_height 47
static const unsigned char Temperature_20Icon_bits[] U8X8_PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x80, 0x7f, 0x00, 0x00,
  0xc0, 0xe1, 0x00, 0x00, 0xe0, 0xc0, 0x01, 0x00, 0x60, 0x80, 0xf9, 0x03,
  0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0x79, 0x00,
  0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0xf9, 0x03,
  0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0x01, 0x00, 0x60, 0x8c, 0x79, 0x00,
  0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0xf9, 0x03,
  0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x79, 0x00,
  0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0xf9, 0x03,
  0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x01, 0x00,
  0x70, 0x9e, 0x03, 0x00, 0x38, 0x1e, 0x07, 0x00, 0x18, 0x3e, 0x0e, 0x00,
  0x1c, 0x3f, 0x0c, 0x00, 0x0c, 0x7f, 0x18, 0x00, 0x8c, 0xff, 0x18, 0x00,
  0x8e, 0xff, 0x38, 0x00, 0xc6, 0xff, 0x31, 0x00, 0xc6, 0xff, 0x31, 0x00,
  0xc6, 0xff, 0x31, 0x00, 0x8e, 0xff, 0x38, 0x00, 0x8c, 0xff, 0x18, 0x00,
  0x0c, 0x7f, 0x1c, 0x00, 0x3c, 0x1c, 0x0e, 0x00, 0x78, 0x00, 0x06, 0x00,
  0xe0, 0x80, 0x07, 0x00, 0xe0, 0xff, 0x03, 0x00, 0x80, 0xff, 0x00, 0x00,
  0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

// 湿度アイコンビットマップ
#define Humidity_20Icon_width 27
#define Humidity_20Icon_height 47
static const unsigned char Humidity_20Icon_bits[] U8X8_PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00,
  0x00, 0x70, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00,
  0x00, 0xdc, 0x01, 0x00, 0x00, 0x8e, 0x01, 0x00, 0x00, 0x86, 0x03, 0x00,
  0x00, 0x06, 0x03, 0x00, 0x00, 0x03, 0x07, 0x00, 0x80, 0x03, 0x06, 0x00,
  0x80, 0x01, 0x0c, 0x00, 0xc0, 0x01, 0x1c, 0x00, 0xc0, 0x00, 0x18, 0x00,
  0xe0, 0x00, 0x38, 0x00, 0x60, 0x00, 0x30, 0x00, 0x70, 0x00, 0x70, 0x00,
  0x30, 0x00, 0xe0, 0x00, 0x38, 0x00, 0xc0, 0x00, 0x18, 0x00, 0xc0, 0x01,
  0x1c, 0x00, 0x80, 0x01, 0x0c, 0x00, 0x80, 0x03, 0x0e, 0x00, 0x80, 0x03,
  0x06, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x07,
  0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06,
  0x63, 0x00, 0x00, 0x06, 0x63, 0x00, 0x00, 0x06, 0x63, 0x00, 0x00, 0x06,
  0xe3, 0x00, 0x00, 0x06, 0xc7, 0x00, 0x00, 0x06, 0xc6, 0x01, 0x00, 0x07,
  0x86, 0x03, 0x00, 0x03, 0x0e, 0x1f, 0x00, 0x03, 0x0e, 0x1e, 0x80, 0x01,
  0x1c, 0x00, 0xc0, 0x01, 0x38, 0x00, 0xe0, 0x00, 0x78, 0x00, 0x70, 0x00,
  0xf0, 0x00, 0x38, 0x00, 0xe0, 0x07, 0x1f, 0x00, 0x80, 0xff, 0x0f, 0x00,
  0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00
};

void setup() {
  radio.begin();
  radio.openReadingPipe(0, address);
  radio.setPALevel(RF24_PA_MIN);
  radio.startListening();
  u8g2.begin();
  rtc.begin();
}

void loop() {
  if (radio.available()) {
    radio.read(&text, sizeof(text));
    outTemp = String(text[0]) + String(text[1]) + char(176) + "C";
    outHum = String(text[2]) + String(text[3]) + "%";
  }
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;
    u8g2.firstPage();
    do {
      switch (draw_state) {
        case 0: drawDate(); break;
        case 1: drawInTemperature(); break;
        case 2: drawInHumidity(); break;
        case 3: drawOutTemperature(); break;
        case 4: drawOutHumidity(); break;
      }
    } while (u8g2.nextPage());
    draw_state++;
    if (draw_state > 4) draw_state = 0;
  }
}

void drawDate() {
  String dowa = rtc.getDOWStr();
  dowa.remove(3);
  rtcDate = dowa + " " + rtc.getDateStr();
  u8g2.setFont(u8g2_font_timB14_tr);
  u8g2.setCursor(0, 15);
  rtcTime = rtc.getTimeStr();
  rtcTime.remove(5);
  u8g2.print(rtcDate);
  u8g2.setFont(u8g2_font_fub30_tf);
  u8g2.setCursor(8, 58);
  u8g2.print(rtcTime);
}

void drawInTemperature() {
  readDHT22 = DHT.read22(dataPin);
  t = DHT.temperature;
  inTemp = String(t) + char(176) + "C";
  u8g2.setFont(u8g2_font_helvR14_tr);
  u8g2.setCursor(24, 15);
  u8g2.print("INDOOR");
  u8g2.setFont(u8g2_font_fub30_tf);
  u8g2.setCursor(36, 58);
  u8g2.print(inTemp);
  u8g2.drawXBMP(0, 17, Temperature_20Icon_width, Temperature_20Icon_height, Temperature_20Icon_bits);
}

void drawInHumidity() {
  h = DHT.humidity;
  inHum = String(h) + "%";
  u8g2.setFont(u8g2_font_helvR14_tr);
  u8g2.setCursor(24, 15);
  u8g2.print("INDOOR");
  u8g2.setFont(u8g2_font_fub30_tf);
  u8g2.setCursor(36, 58);
  u8g2.print(inHum);
  u8g2.drawXBMP(0, 17, Humidity_20Icon_width, Humidity_20Icon_height, Humidity_20Icon_bits);
}

void drawOutTemperature() {
  u8g2.setFont(u8g2_font_helvR14_tr);
  u8g2.setCursor(12, 15);
  u8g2.print("OUTDOOR");
  u8g2.setFont(u8g2_font_fub30_tf);
  u8g2.setCursor(36, 58);
  u8g2.print(outTemp);
  u8g2.drawXBMP(0, 17, Temperature_20Icon_width, Temperature_20Icon_height, Temperature_20Icon_bits);
}

void drawOutHumidity() {
  u8g2.setFont(u8g2_font_helvR14_tr);
  u8g2.setCursor(12, 15);
  u8g2.print("OUTDOOR");
  u8g2.setFont(u8g2_font_fub30_tf);
  u8g2.setCursor(36, 58);
  u8g2.print(outHum);
  u8g2.drawXBMP(0, 17, Humidity_20Icon_width, Humidity_20Icon_height, Humidity_20Icon_bits);
}

説明: 受信側の屋内ユニットでは、DS3231リアルタイムクロック用ライブラリとOLEDディスプレイ用U8G2ライブラリを追加。インスタンス、ピン、変数を定義し、温度・湿度アイコンをビットマップとして定義します。

温度アイコンビットマップ:

#define Temperature_20Icon_width 27
#define Temperature_20Icon_height 47
static const unsigned char Temperature_20Icon_bits[] U8X8_PROGMEM = { ... }

ビットマップ作成にはGIMP(オープンソース画像エディタ)を使用。描画後、.xbm形式でエクスポートし、メモ帳で開いてビットマップデータをArduinoコードにコピーします。

Arduinoベースの信頼性の高いワイヤレス気象観測ステーションを構築する
Arduinoベースの信頼性の高いワイヤレス気象観測ステーションを構築する

PROGMEM修飾子を使って定数として定義すると、ビットマップがSRAMではなくフラッシュメモリに格納され、メモリ節約になります。

static const unsigned char Temperature_20Icon_bits[] U8X8_PROGMEM // フラッシュメモリに保存
static unsigned char Temperature_20Icon_bits[] // SRAMに保存

setup()では無線通信、OLEDディスプレイ、リアルタイムクロックを初期化。

loop()ではNRF24L01で着信データを常時チェック。データがあればradio.read()で読み取り、最初の2文字を温度、次の2文字を湿度の文字列変数に格納。

millis()関数でinterval変数(3秒)ごとに表示を切り替え。delay()を使わないのは、待機中に屋外ユニットからのデータを取りこぼさないためです。

U8G2ライブラリのfirstPage()とnextPage()で5つの画面を順番に表示。drawDate()で日時、drawInTemperature()で室内温度、drawInHumidity()で室内湿度、drawOutTemperature()で屋外温度、drawOutHumidity()で屋外湿度を表示します。

このArduinoプロジェクトをお楽しみいただけたら幸いです。ご質問はコメント欄でお気軽にお尋ねください。

製造プロセス

  1. Raspberry Pi搭載DIYソーラー駆動気象ステーション
  2. Raspberry Pi 2とADS-WS1で屋外気象観測ステーションを構築
  3. Raspberry Piで気象観測ステーションを構築し、日次天気データをメール送信
  4. 高度気象ステーション v2.0:温度・気圧・湿度・高度のリアルタイム監視
  5. Arduinoオーディオ周波数検出器 – 高感度サウンドモジュールで最大音量ピークの周波数を測定
  6. スマートワイヤレス照明スイッチコントローラー:Arduinoで物理スイッチを遠隔操作
  7. Arduino三目並べ:MAX7219 LEDマトリクスと段ボールケースで自作
  8. SparkFun 7セグメントディスプレイで作る信頼性の高いArduinoカウントダウンタイマー
  9. 10ドルで作る携帯型Arduino気象観測ステーション – Nano、DHT22、BMP180、LCDで簡単DIY
  10. eDOT:高精度Arduino時計・気象ステーション(IRリモコン・自動輝度調整内蔵)
  11. ArduinoとThingSpeakで作るリアルタイム気象モニタリングシステム