Arduinoで温度と湿度を測ってシリアル通信

# なにがやりたいのか

簡単にいうと
**Arduino Unoで室内の温度と湿度を測ってそれをブラウザ上で表示させたい。**

とりあえずブラウザ上で表示させるのは`Node.js`かなんかに任せるとして、
今回は**[DHTセンサー](https://learn.adafruit.com/dht)**なるものを使って**Arduinoで回路を組み、計測した温度と湿度をJSON形式で送ろうとおもう。**

DHTセンサーにはメジャーなものとして`DHT11`と`DHT22`があるようですが、
今回は精度が`DHT11`より高い**`DHT22`**を使用します。Amazonで750円ぐらいで売っているのでよかったら試してみてください。

**JSONデータを送る方法はシリアル通信にします。**
ほんとうはシリアル通信じゃなくてHTTPあたりで通信したいけど**Ethernetシールド付きのArduino**の購入は
現在諸事情によりお金がなく、お値段的に憚れるので今回はシリアル通信で我慢する。
というより純正品に関しては日本のスイッチサイエンス社はもう販売していない？
HTTP通信についてはお金に余裕ができたら試してみようとおもいます。

## Arduinoで使用するライブラリ

- [DHT-sensor-library](https://github.com/adafruit/DHT-sensor-library)
    - 簡単にDHTから値を読み取ることができるようになる。
    - [Adafruit Unified Sensor Library](https://github.com/adafruit/Adafruit_Sensor)をインクルードしないとダメとあるが多分今回使う部分の程度なら必要なさそう。
- [ArduinoJson](https://github.com/bblanchon/ArduinoJson)
    - JSONのシリアライズに使用する。
    - **2019/01/21の時点ではバージョン6はベータ版なので注意。**

## 回路を組む

以下の画像がわかりやすいと思います。
![回路図](http://static.cactus.io/img/hookups/arduino/hookup-arduino-to-dht22-sensor.jpg)
ブレッドボード図はこちら
![board付き回路図](http://static.cactus.io/img/hookups/breadboards/hookup-arduino-dht22-breadboard.jpg)
[画像元のURL](http://cactus.io/hookups/sensors/temperature-humidity/dht22/hookup-arduino-to-dht22-temp-humidity-sensor)

## Arduinoのコードを書く

```c++:コード例
#include <ArduinoJson.h>
#include <DHT.h>

constexpr uint8_t dhtPin = 2;       // DHTセンサーが刺さっているピン
constexpr uint8_t dhtType = DHT22;  // 今回使うのはDHT22
DHT dht{dhtPin, dhtType};

void setup() {
  Serial.begin(9600);
  dht.begin();
}

void loop() {
  // 10秒待機(DHT22は最低でも2秒は遅延させたい)
  delay(10000);

  // JsonObjectの生成。
  StaticJsonBuffer<200> jsonBuffer;
  auto& root = jsonBuffer.createObject();

  // 読み取った温度をJsonObjectにセットする。
  const auto temperature = dht.readTemperature();
  if (isnan(temperature)) {
    setError(root, "Error reading temperature!");
  }
  else {
    root["temperature"] = temperature;
  }
  
  // 読み取った湿度をJsonObjectにセットする。
  const auto humidity = dht.readHumidity();
  if (isnan(humidity)) {
    setError(root, "Error reading humidity!");
  }
  else {
    root["humidity"] = humidity;
  }

  // Jsonのシリアライズ。
  root.printTo(Serial);
  Serial.print("\n");  // 改行文字をデリミタとして使う。
}
```

```text:シリアルモニタを覗くとこんな感じのJSONが確認できる
{"temperature":20.6,"humidity":54.4}
```

### 簡単な解説

```c++:DHTの初期化
#include <ArduinoJson.h>
#include <DHT.h>

constexpr uint8_t dhtPin = 2;       // DHTセンサーが刺さっているピン
constexpr uint8_t dhtType = DHT22;  // 今回使うのはDHT22
DHT dht{dhtPin, dhtType};
```

`constexpr uint8_t dhtType = DHT22;`とありますが、`DHT22`は`DHT.h`に定義されています。

C++11以降のC++にあまり馴染みがない方なら**`constexpr`**ってなんやねん！って思ったかもしれません。
**この部分に関しては**`const`とあまり変わらないので気にする必要はなさそうです。
**`constexpr`**に対してまともに解説するとそれだけで長い記事になるほど奥が深いので気になった方はぜひ調べてみてください。

`DHT dht{dhtPin, dhtType}`ではDHTのコンストラクタを呼び出しています。
ArduinoでC++11以降の記述ができるんだと知り、ビビってしまったためここでもC++11の記法を使っています。:sweat:

```C++:setup部分
void setup() {
  Serial.begin(9600);
  dht.begin();
}
```
シリアル通信のデータ転送レートの指定とDHTのセットアップをしています。
`DHT::begin()`の中身はメンバ変数とピンモードの初期化となっているようです。**(2019/1/21時点)**
ちなみにArduinoでは`Setup()`は電源を入れたときやリセットしたときに、一度だけ呼ばれる関数となっています。

```c++:loop部分はじめ
void loop() {
  // 10秒待機(DHT22は最低でも2秒は遅延させたい)
  delay(10000);
```
Arduinoでは`loop()`は`Setup()`のあとに繰り返し呼ばれます。
とりあえず10秒間隔でシリアル通信を行うことにしました。

```c++:JsonObjectの生成
  // JsonObjectの生成。
  StaticJsonBuffer<200> jsonBuffer;
  auto& root = jsonBuffer.createObject();
```
`StaticJsonBuffer`はメモリプールで、`StaticJsonBuffer<200> jsonBuffer`では200bytesのメモリを確保しています。
ちなみにおもいっきりStackにのっているのでHeapから確保したい場合は`DynamicJsonBuffer`を使えばいいそうです。

また、`auto& root = jsonBuffer.createObject();`でさきほどの`jsonBuffer`から`JsonObject`への参照を得ていますが、この参照の寿命はjsonBufferがスコープから抜けるまでのようです。
なので今回で言えば`loop()`内では参照可能ということになります。

```c++:温度の読み取り
  // 読み取った温度をJsonObjectにセットする。
  const auto temperature = dht.readTemperature();
  if (isnan(temperature)) {
    setError(root, "Error reading temperature!");
  }
  else {
    root["temperature"] = temperature;
  }
```
`DHT::readTemperature()`でセンサーが計測した温度を受け取ることができます。
失敗した場合は`NAN`が返ってきます。
ここでは`NAN`を捉えたら`setError()`で`root`にerror関連の値をセットしています。error値のセット方法は人によって実装方法がいろいろあると思うので実装は載せていません。

返り値が`NAN`ではなかった場合、`JsonObject`の`root`に`key=temperature`,`value=読み取った温度`を追加しています。
例えば、読み取った温度が25.5°Cだった場合は`{"temperature"=25.5}`のような感じになります。

湿度も同じことやっているので省略。

```c++:Jsonデータを送る
  // Jsonのシリアライズ。
  root.printTo(Serial);
  Serial.print("\n");  // 改行文字をデリミタとして使う。
}
```
`JsonObject::printTo()`の引数はJsonの書き込み先を指定します。今回はシリアル通信を行いたいので`Serial`を指定します。
データを受信する際に改行文字あったほうが楽そうだったので今回は改行文字もシリアルポートへ出力しています。

# つぎにやること

**`TypeScript` + `Node.js`でJSONデータを受け取ってブラウザで表示させるところまでは記事にしたい。**
本当はそれも含めた記事にしたかったのですが、そろそろ真面目に就職活動を行おうと思うので、ちょっと後回しになってしまう形に。
キチンと書き終えるのがいつになるのかわからなかったのでとりあえず一区切りとしてここでいったん投稿することにします。:fist: