ソースコードの解説

目的

「温湿度センサー(DHT11) のアウトプットをグラフで表示」で使用したコードが理解できるようになる。

目次

プログラムの全体図

ソースコードの説明

#include <Ambient.h>      // Ambient.h をインクルード
#include <DHT.h>          // DHT.h をインクルード
#include <WiFi.h>         // WiFi.h をインクルード
#define DHTPIN 22         // DHT センサーアウトプットピンの指定
#define DHTTYPE DHT11     // DHT型の指定
#define PERIOD 30         // delay の値を指定 (例:30 -> 30秒間間隔でデーターをAmbientに送信)
DHT dht(DHTPIN, DHTTYPE); // DHTセンサーライブラリーを使うための設定(インスタンス生成)
WiFiClient client;        // WiFiClientを使うための設定(インスタンス生成)
Ambient ambient;          // Ambientを使うための設定(インスタンス生成)

/***************************************/
/*** ここから 環境によって書き換える ***/
/***************************************/

//WiFi接続情報
const char* ssid     =  "<YOUR_SSID>";
const char* password =  "<YOUR_PASSWORD>";
//Ambient接続情報
unsigned int channelId =  <YOUR_CHANNEL_ID>;
const char* writeKey =  "<YOUR_WRITEKEY>";

void setup() {
    Serial.begin(115200);  // シリアル通信の速度を指定
    
    // Wi-Fiの初期化
    WiFi.begin(ssid, password);
    Serial.print("WiFi connecting");

    // Wi-Fiアクセスポイントへの接続待ち
    while (WiFi.status() != WL_CONNECTED) {
        Serial.print(".");
        delay(500);
    }
    
    Serial.println(" connected"); // Wi-Fi に接続したらコンソール画面に connected を表示

    // チャネルIDとライトキーを指定してAmbientの初期化
    ambient.begin(channelId, writeKey, &client);
    
    dht.begin(); //  DHT11の初期化
}

void loop() {
  float temperature = dht.readTemperature(); // 温度読み取り
  float humidity = dht.readHumidity();   // 湿度読み取り

  // 読み取りに失敗しているかチェック
  // 失敗している場合はもう一度、温湿度の読み取りをする
  if (isnan(temperature) || isnan(humidity)) {
     Serial.println("Failed to read from DHT sensor!");
     return;
  }
    
  //  シリアルモニターに表示
  Serial.print("温度: ");
  Serial.print(temperature);
  Serial.print(" *C\t ");
  Serial.print("湿度: ");
  Serial.print(humidity);
  Serial.println(" %");

  // データーがint型かfloat型であれば、直接セットすることができます。
  // 1番目のデータに温度を指定。
  ambient.set(1, temperature); 
  
  // 2番目のデータに湿度を指定。
  ambient.set(2, humidity);
  
  // Ambientにデータを送信         
  ambient.send();                   
  
  // 30秒間待つ
  delay(PERIOD * 1000);             
}

コメント

コメントを書く目的は、プログラムの働きを自分が理解したり、思い出したりするのを助けるためです。 また、他の人に、それを伝えるためでもあります。 コメントはコンパイラから無視され、コンピュータに出力されることはないので、チップ上のメモリを消費しません。

参照 - コメント 公式(英語)

参照 - コメント 非公式(日本語)

要するに、コメントを付けてプログラムの挙動を説明しています。

本資料では以下の部分で コメントを使用しています。

float temperature = dht.readTemperature(); // 温度読み取り
float humidity = dht.readHumidity(); // 湿度読み取り

プリプロセッサ

「プリプロセッサ」とはコンパイル前にソースプログラムに対して行われる前処理のことです。 一般にある処理を行うソフトウェアに対して、データ入力やデータ整形などの準備的な処理を行うソフトウェアのことである。

参照-プリプロセッサ

本資料では「#define」と「#include」がプリプロセッサです。

#define

#include

本資料では以下の部分で使用しています。

#include <Ambient.h>      // Ambient.h をインクルード
#include <DHT.h>          // DHT.h をインクルード
#include <WiFi.h>         // WiFi.h をインクルード
#define DHTPIN 22         // DHT センサーアウトプットピンの指定
#define DHTTYPE DHT11     // DHT型の指定
#define PERIOD 30         // delay の値を指定 (例:30 -> 30秒間間隔でデーターをAmbientに送信)

参照-第18章 プリプロセッサ

参照-プリプロセッサの働きとその使い方

ライブラリー

ライブラリ(英: library)は、汎用性の高い複数のプログラムを再利用可能な形でひとまとまりにしたものである。ライブラリと呼ぶ時は、それ単体ではプログラムとして作動できない、つまり実行ファイルではない場合がある。ライブラリは他のプログラムに何らかの機能を提供するコードの集まりと言える。

参照-ライブラリ

要するに、ライブラリを活用することで、自分で新しくプログラムを書く必要がなくなり手間を省くことができます。

本資料では以下の部分で使用しています。

#include <DHT.h>          // DHT.h をインクルード
...
float temperature = dht.readTemperature(); // 温度読み取り
float humidity = dht.readHumidity();   // 湿度読み取り

関数

関数とは、コンピュータプログラム上で定義されるサブルーチンの一種で、数学の関数のように与えられた値(引数)を元に何らかの計算や処理を行い、結果を呼び出し元に返すもののこと。

参照 - 関数 【 function 】 ファンクション

サブルーチン

サブルーチンとは、コンピュータプログラムの中で特定の機能や処理をひとまとまりの集合として定義し、他の箇所から呼び出して実行できるようにしたもの。単に「ルーチン」とも呼ばれる。

参照 - サブルーチン 【 subroutine 】 サブルーティン

要するに、一度プログラムしたコードを再利用するために関数を用います。

本資料では以下の部分で関数を定義しています。

void setup(){
    ...
}
void loop(){
    ...
}

本資料では以下の部分で関数を利用しています(呼び出しています)。

Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.print("WiFi connecting");
WiFi.status()
Serial.print(".");
delay(500);
Serial.println(" connected"); 
ambient.begin(channelId, writeKey, &client);
dht.begin();
dht.readTemperature();
dht.readHumidity();    
isnan(temperature);
isnan(humidity)
ambient.set(1, temperature); 
ambient.set(2, humidity);
ambient.send();                   
delay(PERIOD * 1000);  

return

関数の実行を中止して、呼び出し元の関数に処理を戻します。

参照 - コメント 公式(英語)

参照 - コメント 非公式(日本語)

要するに、return の後のプログラムは実行されなくなります。

本資料では以下の部分で return を使用しています。

void loop() {
    float temperature = dht.readTemperature(); // 温度読み取り
    float humidity = dht.readHumidity(); // 湿度読み取り 

    // 読み取りに失敗しているかチェック
    // 失敗している場合はもう一度、温湿度の読み取りをする
    if (isnan(temperature) || isnan(humidity)) {
       Serial.println("Failed to read from DHT sensor!");
       return;
    }
    
    //  シリアルモニターに表示
    Serial.print("温度: ");
    Serial.print(temperature);
    Serial.print(" *C\t ");
    Serial.print("湿度: ");
    Serial.print(humidity);
    Serial.println(" %");

    // データーがint型かfloat型であれば、直接セットすることができます。
    // 1番目のデータに温度を指定。
    ambient.set(1, temperature); 
  
    // 2番目のデータに湿度を指定。
    ambient.set(2, humidity);
  
    // Ambientにデータを送信         
    ambient.send();                   
  
    // 30秒間待つ
    delay(PERIOD * 1000);             
}

setup() 関数

setup()はArduinoボードの電源を入れたときやリセットしたときに、一度だけ実行されます。setup()は省略できません。

参照 - setup()-公式(英語)

参照 - setup()-非公式(日本語)

参照 - Arduinoチュートリアル 基礎編

本資料では以下の部分で setup() 関数を定義しています。

void setup() {
    Serial.begin(115200);  // シリアル通信の速度を指定
    
    // Wi-Fiの初期化
    WiFi.begin(ssid, password);
    Serial.print("WiFi connecting");

    // Wi-Fiアクセスポイントへの接続待ち
    while (WiFi.status() != WL_CONNECTED) {
        Serial.print(".");
        delay(500);
    }
    
    Serial.println(" connected"); // Wi-Fi に接続したらコンソール画面に connected を表示

    // チャネルIDとライトキーを指定してAmbientの初期化
    ambient.begin(channelId, writeKey, &client);
    
    dht.begin(); //  DHT11の初期化
}

loop() 関数

loop()関数はスケッチの心臓部であり、繰り返し実行されます。loop()は省略できません。

参照 - loop()

参照 - Arduinoチュートリアル 基礎編

本資料では以下の部分で loop() 関数を定義しています。

void loop() {
    float temperature = dht.readTemperature(); // 温度読み取り
    float humidity = dht.readHumidity();   // 湿度読み取り

    // 読み取りに失敗しているかチェック
    // 失敗している場合はもう一度、温湿度の読み取りをする
    if (isnan(temperature) || isnan(humidity)) {
       Serial.println("Failed to read from DHT sensor!");
       return;
    }
    
    //  シリアルモニターに表示
    Serial.print("温度: ");
    Serial.print(temperature);
    Serial.print(" *C\t ");
    Serial.print("湿度: ");
    Serial.print(humidity);
    Serial.println(" %");

    // データーがint型かfloat型であれば、直接セットすることができます。
    // 1番目のデータに温度を指定。
    ambient.set(1, temperature); 
  
    // 2番目のデータに湿度を指定。
    ambient.set(2, humidity);
  
    // Ambientにデータを送信         
    ambient.send();                   
  
    // 30秒間待つ
    delay(PERIOD * 1000);             
}

変数

変数を用いることで、データを一定期間記憶し必要なときに利用することができます。

参照 - 変数 (プログラミング)

要するに、一度変数を用意すると、次回からは変数名だけでその値を再利用できます。

本資料では以下の部分で使用しています。

float temperature = dht.readTemperature(); // 温度読み取り
float humidity = dht.readHumidity();   // 湿度読み取り
...
Serial.print(temperature);
Serial.print(humidity);
...
ambient.set(1, temperature);
ambient.set(2, humidity);

データ型

データ型(データがた、data type)とは、コンピュータにおける データ(値)の種類に関する分類である。

参照 - データ型

浮動小数点数-float

整数よりも分解能が高いアナログ的な値が必要なときに使います。使用可能な値の範囲は3.4028235E+38から-3.4028235E+38までで、32ビット(4バイト)のサイズです。

参照 - float (浮動小数点型) 公式(英語)

参照 - float (浮動小数点型) 非公式(英語)

要するに、温度/湿度 を測って小数点以下も扱いたいような場合に、浮動小数点数を使います。

本資料では以下の部分で使用しています。

float temperature = dht.readTemperature(); // 温度読み取り
float humidity = dht.readHumidity();   // 湿度読み取り

int-整数型

int型(整数型)は、数値の記憶にもっともよく使われる型です。値の範囲は-32768から32767までです。

参照 - int(整数型) 公式(英語)

参照 - int(整数型) 非公式(日本語)

要するに、プログラムの中で値を整数で扱いたい場合に int 型を使用します。

本資料では後続資料の「演習で参考にするプログラム」で使用されています。

//ESP32にはhallRead()関数がデフォルトで定義されています。hallValue というint型の変数に、hallRead()関数を使用して結果を hallValue に代入します。
int hallValue = hallRead(); 

制御文

プログラムの実行(流れ)の制御を行う文

参照 - 制御文

if 文

if文は与えられた条件が満たされているかどうかをテストします。

参照 - if 公式(英語)

参照 - if 非公式(日本語)

要するに、条件を満たすか判定し、ある処理を実行したり実行しなかったり制御するために使用します。

本資料では以下の部分で使用しています。

// 読み取りに失敗しているかチェック
// 失敗している場合はもう一度、温湿度の読み取りをする
// "||" は「または」を表す。
if (isnan(temperature) || isnan(humidity)) {
    Serial.println("Failed to read from DHT sensor!");
    // 関数の処理を終了して、呼び出し元に戻る。
    return; 
}

if else 文

if else文を使うと複数のテストをまとめることができ、単体のifより高度な制御が可能となります。

参照 - if else 公式(英語)

参照 - if else 非公式(日本語)

要するに、条件が満たされるかの判定を複数作りたい場合に使用します。

本資料では後続資料の「演習で参考にするプログラム」で使用されています。

// hallValue が10以下の場合(N極)
// hallValue が23以上の場合(S極)
if (hallValue < 10 || hallValue > 25) {
    // LEDを点灯させます
    digitalWrite(LED_BUILTIN, HIGH);
} else {
    // LEDを消灯させます。
    digitalWrite(LED_BUILTIN, LOW);
}

while 文

whileは繰り返しの処理に使います。カッコ内の式がfalseになるまで、処理は無限に繰り返されます。

参照 - while 公式(英語)

参照 - while 非公式(日本語)

要するに、条件が満たされている間繰り返し実行されます。

本資料では以下の部分で使用しています。

// Wi-Fiアクセスポイントへの接続待ち
while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
}

Serial.print, Serial.println

Serial.print

人間の読めるASCII文字の形でデータをシリアルポートに出力します.

参照 - Serial.print() 公式(日本語)

要するに、例えば、計測した温度や湿度をシリアルポートに出力することで、その値を人間が確認することができます。

本資料では以下の部分で使用しています。

Serial.print("温度: ");
Serial.print(temperature);

Serial.println

Serial.print()と同じフォーマットですが、ニューライン(ASCIIコード10あるいは’’)を付けて送信します。

参照 - Serial.Println 公式(英語)

参照 - Serial.Println 非公式(日本語)

要するに、Serial.println()関数を使用すると、シリアルポートに文字列を出力し、かつ、改行してくれます。 Serial.print()関数の場合は改行は行われません。

本資料では以下の部分で使用しています。

Serial.println(" *C ");

Serial.begin

シリアル通信のデータレートをビット/秒(bps)で設定します

参照 - Serial.begin()公式(日本語)

要するに、シリアルモニターに値や文字列を表示させたい場合には Serial.begin(speed) を指定する必要があります。

本資料では以下の部分で使用しています。

 Serial.begin(115200);              //シリアル通信の速度を指定

delay

プログラムを指定した時間だけ止めます。単位はミリ秒です(1,000ミリ秒=1秒)。

参照 - delay()公式(英語)

参照 - delay()非公式(日本語)

要するに、プログラムを指定した時間だけ止めて、処理をゆっくり実行させたい場合に delay を使用します。例えば、LEDの点滅速度を調整する場合などに利用できます。

本資料では以下の部分で使用しています。

// 30秒間待つ
delay(PERIOD * 1000);

WiFiの利用

必要なライブラリをインクルード-WiFi

本資料では以下の部分で使用しています。

#include <WiFi.h>         // WiFi.h をインクルード

WiFi設定情報の定義

本資料では以下の部分で使用しています。

//WiFi接続情報
const char* ssid     =  "your-ssid" ; 
const char* password =  "your-password";

WiFiアクセスポイントへの接続

本資料では以下の部分で使用しています。

// Wi-Fiの初期化
WiFi.begin(ssid, password);

// Wi-Fiアクセスポイントへの接続待ち
while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
}   

Ambientの利用

必要なライブラリをインクルード-Ambient

本資料では以下の部分で使用しています。

#include <Ambient.h>      // Ambient.h をインクルード

Ambientの設定情報定義と初期化

Ambientではマイコンからデーターを送ってグラフ化するまでは非常に簡単です。 最低限必要なのはAmbientサイトで「チャネル」を作ることと、 マイコン側のプログラムでチャネルIDとライトキーを指定してデーターを送ることだけです。

参照 - Ambientを使ってみる

参照 - Arduinoライブラリー - Ambient::begin()

本資料では以下の部分で使用しています。

//Ambient設定
unsigned int channelId =  your-channelId;
const char* writeKey =  "your-writeKey";

// チャネルIDとライトキーを指定してAmbientの初期化
ambient.begin(channelId, writeKey, &client);

Ambientにデータをセット

Ambientに送信する時は、まずはambient.set()でデーターをパケットにセットします。Ambientでは8種類までのデーターを送信できます。set()は何種類目のデーターかを示す数値とそのデーターを指定します。

参照 - Arduino ESP8266で温度・湿度を測定し、Ambientに送ってグラフ化する - プログラム

参照 - Arduinoライブラリー - Ambient::set()

参照 - Ambientを使ってみる - 3.マイコン側プログラミング

本資料では以下の部分で使用しています。

// 1番目のデータに温度を指定。
ambient.set(1, temperature); 
  
// 2番目のデータに湿度を指定。
ambient.set(2, humidity);

Ambientにデータを送信

ambient.send()を呼び出すことでデーターが送信されます。 Ambientでは最短の送信間隔が5秒ですので、5秒以上間隔をあけてデーターを送信してください。 Ambientはデーターを受信すると受信時刻と合わせてデーターを保存します。

参照 - Arduinoライブラリー - Ambient::send()

参照 - Ambientを使ってみる - 4.データー送信

本資料では以下の部分で使用しています。

// Ambientにデータを送信         
ambient.send();