ESP32ではじめようIoT開発ハンズオン　実装用資料

#### 初めに
この資料は2022/04/29に開催されるESP32ではじめようIoT開発ハンズオンの内容を実装するときに使用する資料です。
[こちら](https://speakerdeck.com/shinrinakamura/esp32deshi-meyouiotkai-fa-hanzuon)のハンズオン資料と対応しています。

#### 使用するもの
ESP32
CdS感光センサー
水分センサー
マイクロUSBケーブル
開発用のPC

#### 開発環境
Arduino IDE
Node-RED

#### 開発環境の準備
##### Arduino IDEのインストール
[公式ページ](https://www.arduino.cc/)にアクセスします。
ヘッダーメニューからsoftwareを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/4022a86f-6f0a-6cd1-769e-c6ce26b98c2c.png)
Win7 and newerを選択してダウンロードします。
寄付をするか聞かれますので、寄付をしない場合はJUST DOWNLOADを押してください。
もちろん寄付をしてもらっても結構です。

ダウンロードしたファイルを実行すると、ウィザード形式でインストールすることができます。
特に指定したいことがなければそのままインストールします。追加ドライバについても承認します。

続いてArduino IDEでESP32が開発できるようにしていきます。


ボードマネージャを登録していきます。
ArduinoIDEのファイルメニューから環境設定を開きます。
続いてボードマネージャのURLを取得します。
[こちら](https://docs.espressif.com/projects/arduino-esp32/en/latest/installing.html)のページを開きます。
stableのボードマネージャのURLをコピーします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/32add95b-3e18-92e5-e748-945cfbae96a2.png)
コピーしたURLをArduinoIDEの環境設定の、追加のボードマネージャのURLにペーストします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/703954c9-27a5-1900-7cfe-7ff985a8fde2.png)
okを押して環境設定のウィンドウを閉じます。
次にツールメニューからボード、ボードマネージャの順に開いていきます。
検索窓にesp32と入力してエンターします。
出てきたespressifのesp32ボーマネージャをインストールしてください。
インストールが終わったらesp32のボードマネージャが開けるようになってますので開いていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/6ef1df7c-55dd-66e2-9954-57ca6acc981d.png)

ツールからボード、ESP32Arduino、ESP32 Dev Moduleを選択します。
これで、ArduinoIDEでESP32を使用する準備ができました。


##### Node-RED環境の準備
今回はenebularというSaaSを使用します。
[enebular](https://www.enebular.com/ja/)の公式ページに移動します。
始めて使用する方はアカウントを作成してください。
右上のサインインボタンを押すとログインページに入ります。
そこにアカウントの作成ボタンがありますので、必要事項を入力して登録してください。
メールアドレスについてはGmailでの登録もできるようになっています。
登録ができたら準備は完了です。
使い方については、流れの中で説明していきます。
enebularのフローに関しては、開いてから1時間使用できます。1時間を経過したら再度開くことが必要になりますので注意してください。

#### ハンズオン
##### Lチカ
まずはLチカから試していきます。
これはweb開発でいうhello worldのようなもので、開発環境とデバイスが想定通りに動いているか確認するために行います。

LEDのアノード側(足が長い方)をESP32開発ボードのGPIO４番に接続します。
LEDのカソード側に抵抗を接続します。さらに抵抗からGNDに接続します。
接続するときはジャンパピンを使用して配線してください。

Lチカコード例
```cpp
#define LED_PIN 12   //ピンの番号を指定する
void setup() {
  Serial.begin(115200);               //シリアル通信のスタート
  Serial.println("LED Brynk start");  //コンソールに表示する
  pinMode(LED_PIN, OUTPUT);           //ピンを出力モードにする
}

void loop() {     //この中に書いた指示が繰り返される
  
  digitalWrite(LED_PIN, HIGH);    //点灯
  Serial.println("light on");     //表示して確認
  delay(1000);                    //指定した時間待つ
  
  digitalWrite(LED_PIN, LOW);     //消灯
  Serial.println("light off");
  delay(1000);                      
}
```
ESP32開発ボードと開発用のPCをマイクロUSBケーブルで接続します。
Arduino IDEのツールメニューからボードはESP32Dev Moduleを選択します。
ポートはESP32を接続しているポートを選択します。
ポートがわからないときはデバイスマネージャで確認してください。(windows)

ビルドをして、書き込みを行います。
➡ボタンを押して書き込みを行います。
オートで書き込みが終わらないときは、書き込みを行っている最中にENボタンとbootボタンを同時押しして、ENボタンの方を先に離します。
うまく書き込みができない場合は教えてください。

うまく書き込みができると指定した時間でLEDが点滅します。
シリアルコンソールを見るために通信速度(ボーレート)を115200に設定します。
LEDが点滅し始めたらIDEの虫メガネボタンを押してシリアルコンソールを開きます。
確認用のメッセージが表示されることができます。

##### 感光センサー
感光センサーに3.3Vと10kΩ抵抗を接続します。抵抗の反対側はGNDに接続します。
感光センサーと抵抗が接続されているところからD32ピンに接続します。

感光センサー読み取りコード例
```cpp
#define LIGHT_PIN 35    //ピン番号を指定

void setup() {
  Serial.begin(115200);
  Serial.println("CdS start");
  pinMode(LIGHT_PIN, INPUT);    //センサーピンのセット
}

void loop() {
  int Measurevalue;   //センサーの値を読み取るための変数を用意
  Measurevalue = analogRead(LIGHT_PIN);   //測定値を読みとる
  
  Serial.print("Mesure value is : ");   //表示して確認
  Serial.println(Measurevalue);
  
  delay(500);                           //遅延を入れて読み取り頻度を調整
}
```

感光センサーの値が変化したらLEDが点灯するプログラムをif文を使用して作成してみてください。


##### 水分センサー
水分センサーとESP32開発ボードを接続します。
VCCは3.3V、GNDはGNDに接続します。信号線はD32に接続します。

ソースコードに関しては、感光センサーと同じようにanalogRead関数になりますので、感光センサーのコードを参考に書いてみてください。
ピンの名前に関しては「MOIST_PIN」にすればよいと思います。

##### mqtt通信
mqtt通信用のライブラリを使用することになります。
ライブラリのインクルードからPubSubClientをインストールしてください。
[こちら](https://pubsubclient.knolleary.net/)から自分でダウンロードしてインストールすることもできます。
興味がある人は挑戦してみてください。

mqtt publish用のサンプルコードです。少し長いですので、基本的にはコピペして使用するようにしてください。
興味があればコードの内容についてもわかる範囲で答えますので、質問してください。
```cpp
/*ESP32でMQTTを送信するシンプルなプログラム
 * @author S.Nakamura 2022-04-23
*/


//送信関係のライブラリの読み込み
#include <WiFi.h>
#include <PubSubClient.h>

//接続するアクセスポイントの情報
const char *ssid = "your_ssid";
const char *password = "your_pass";

// Pub/Subの設定
// Pub/Sub
const char* mqttHost = "your_server_domain";                  //ipアドレスかドメインで指定する
const int mqttPort = 1883;                                    //通常は1883か8883

//mqttのユーザー認証を使用する際に使用する ハンズオンでは使用しない
//const char* mqtt_username = "your name";                //ユーザーネーム
//const char* mqtt_password = "your publish password";    //mqttのパスワード

const char* topic = "your_topic_name";                         // 送信するトピック名（変更）

WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
//ここまで通信関係の設定--------------------------------------------------------------


//プロトタイプ宣言
void connectWiFi();
void connectMqtt();
void MqttPublish(const char *payload);


void setup() {

  Serial.begin(115200);   //シリアル通信の開始
  connectWiFi();    //Ｗｉ-Ｆｉの接続確認 
  connectMqtt();    //mQTTサーバーへの接続確認
  
  MqttPublish("start");   //MQTT送信確認
}

//1秒ごとにカウントアップするためのカウンター
int timer;
int old_time =0;
int timeCounter = 0;  

void loop() {
  
    //テストとして実行しておく処理
    //1秒ごとにカウントアップする
    timer = millis();
    if((timer - old_time) >= 1000){
      timeCounter = timeCounter +1;
      Serial.print("count is ");
      Serial.println(timeCounter);
      
      //計測時間の基準作成
      old_time = timer; 
    }
    
    //フラグを確認してMQTTの送信 
    if (timeCounter > 9) {
      timeCounter = 0;
      
      MqttPublish("test");    //mqtを送信する
     
    }
}


//通信関係の処理
//--------------------------------------------------------
//Wi-Fiを接続する
void connectWiFi(){

  //Wi-Fiのアクセスポイントに接続
  //第一引数：ssid
  //第二引数：パスワード      
  WiFi.begin(ssid, password);
  Serial.print("WiFi connecting...");

  int i = 0 ;   //接続確認の時間
  //Wi-Fiの接続確認
  while(WiFi.status() != WL_CONNECTED) {
  i += 1;
  Serial.print(".");
  delay(1000);
  //10秒間Wi-Fiが接続できないときは接続をやり直す
  if (i == 10){
    Serial.println("WiFi reset");
    connectWiFi();
      }
  }
  //Wi-Fiの接続が確認出来たらコンソールに表示して確認する      
  Serial.print(" connected. ");
  Serial.println(WiFi.localIP());

}

//MQTTの接続
//mqttに接続する関数
void connectMqtt(){
  
  //Wi-Fiの接続確認
  while(WiFi.status() != WL_CONNECTED) {
    Serial.print("WiFi is not connect");
    connectWiFi();
  }
  
  //brokerサーバーに接続する
  //第一引数：ブローカーサーバーのドメインもしくはipアドレス
  //第二引数：接続するポート（通常は1883か8883）
  mqttClient.setServer(mqttHost, mqttPort);

  //clientIDを作成してサーバーに接続する
  while( ! mqttClient.connected() ) {

    Serial.println("Connecting to MQTT...");
    
    //MacアドレスからクライアントIDを作成する
    String clientId = "ESP32-" +  getMacAddr();
    //確認用の表示
    Serial.print("clientID : "); 
    Serial.println(clientId.c_str()); 
    
    //接続の確認
    //if ( mqttClient.connect(clientId.c_str(),mqtt_username, mqtt_password) ) {  //ユーザー認証を行う時はこちらを利用する
    if ( mqttClient.connect(clientId.c_str())) { 
      Serial.println("connected"); 

    }
  }
}

//Mqttの送信
void MqttPublish(const char *payload){

  //mqttの接続を確認
  while( ! mqttClient.connected() ) {
    Serial.println("Mqtt is not connect");
    connectMqtt();
  }
 
  //mqttの送信
  //第一引数：トピック名
  //第二引数：送信するデータ
  mqttClient.publish(topic, payload);
  //コンソールに送信するデータを表示して確認
  Serial.print("published ");
  Serial.println(payload);

}

// Macアドレスを文字列で取得する mqttのクライアントIDに利用する
String getMacAddr()
{
    byte mac[6];
    char buf[50];
    WiFi.macAddress(mac);
    sprintf(buf, "%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    return String(buf);
}
```
アクセスポイントの情報、サーバーとtopicの情報については当日会場で指定されたものを使用してください。

##### 実際の測定データを送信する
今回は測定したデータをJSONという形でpayloadに詰めて送信するようにします。
まずは送信用のpayloadを作成します。
sprintf関数を使用して文字列としてJSONを作成します。
```cpp
char payload[50];

sprintf(payload, "{\"valueID\":\"01\",\"moist\":%d,\"light\":%d}", moist, light)
```
何をやっているのかを説明します。
まずは文字列の配列を用意して、作成した文字列を置いておく領域を確保します。
50Byte用意しているのですが、溢れないように適当に設定しています。
次にsprintf関数ですが、第一引数が出力の格納先、第二引数が
書式指定文字列、第三引数からは文字列に挿入する値となります。

%dはフォーマット指定子と呼ばれ、符号あり整数を意味します。
またJSONは文字列なので「"」で囲みます。またJSONのプロパティも「"」で囲みますが、そのまま使用するとどこまでが文字列なのかコンピュータには判断ができませんので、「\"」という書き方をします。「\」をエスケープシーケンスと言って特別な意味を持ちます。
（例）
|  エスケープシーケンス  |  意味  |
| ---- | ---- |
|  \n |  改行  |
|  \\"  |  ダブルクォーテーション  |
|  \0  |  null文字  |

では早速実際のデータを送信するコード例を書いていきます。
```cpp
/*cdsセンサーの動きと湿度センサーで測定した値をmqttで送信するシンプルなプログラムです
 * 
 * @author S.Nakamura 2022-04-23
 */

//使用するライブラリの読み込み
#include <WiFi.h>
#include <PubSubClient.h>

//Wi-Fiのアクセスポイントの接続情報
const char ssid[] = "your_ssid";
const char passwd[] = "your_pass";

//brokerサーバーの接続情報
const char* mqttHost = "######"; // MQTTのIPかホスト名
const int mqttPort = 1883;       // MQTTのポート

WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

const char* topic_pub = "handson0429";                         // 送信するトピック名
//const char* topic_sub = "今回は使用しません";                      // 受信するトピック名
char* payload;                   // 送信するデータ

//センサーで使用するピンを指定
#define MOIST_PIN 32
#define CDS_PIN 35

//プロトタイプ宣言
bool connectWiFi();
//espのMacアドレスを取得する
String getMacAddr();
//サーバーとの接続の確認をする
void connectMqtt();
//Mqttを送信する
//第一引数；水分センサーの測定値
//第二引数：cdsセンサーの測定値
void MqttPublish(int moist, int cds);


void setup() {
  Serial.begin(115200);
  Serial.println("CdS Moist sensor moving check start");
  pinMode(MOIST_PIN, INPUT);
  pinMode(CDS_PIN, INPUT);
  connectWiFi();      //接続確認
  connectMqtt();
}

void loop() {
  
  int MeasureMoist;                     //読み取った値を入れておく領域を確保
  int MeasureCds;
  MeasureMoist = analogRead(MOIST_PIN); //水分センサーの値を読み取る
  MeasureCds = analogRead(CDS_PIN);     //cdsセンサーの値を読み取る
  
  Serial.print("Mesure Moist is : ");   //表示して確認
  Serial.println(MeasureMoist);
  Serial.print("Mesure Cds is : ");
  Serial.println(MeasureCds);

  MqttPublish(MeasureMoist, MeasureCds);  //mqttを送信
  delay(1000);      //送信頻度を調整するために遅延を入れる
}


//Wi-Fiの接続確認
bool connectWiFi(){
        
  WiFi.begin(ssid, passwd);
  Serial.print("WiFi connecting...");
  //10秒たっても接続されていないときはリセットする
  int i = 0 ;   //接続確認のリセット用
  while(WiFi.status() != WL_CONNECTED) {
  i += 1;
  Serial.print("...");
  delay(1000);
  if (i == 10){
    Serial.println("wi-fi reset");
    connectWiFi();
      }
  }
        
  Serial.print(" connected. ");
  Serial.println(WiFi.localIP());

  return true;
}


//mqttサーバーに接続
void connectMqtt(){
  
  //Wi-Fiの接続確認
  while(WiFi.status() != WL_CONNECTED) {
    
  Serial.print("WiFi is not connect");
  connectWiFi();

  }
  
  //brokerサーバーに接続する
  //第一引数：ブローカーサーバーのドメインもしくはipアドレス
  //第二引数：接続するポート（通常は1883か8883）
  mqttClient.setServer(mqttHost, mqttPort);
  

  //clientIDを作成してサーバーに接続する
  while( ! mqttClient.connected() ) {

    Serial.println("Connecting to MQTT...");
    
    //MacアドレスからクライアントIDを作成する
    String clientId = "ESP32-" +  getMacAddr();
    //確認用の表示
    Serial.print("clientID : "); 
    Serial.println(clientId.c_str()); 
    
    //接続の確認
    //if ( mqttClient.connect(clientId.c_str(),mqtt_username, mqtt_password) ) {  //ユーザー認証を行う時はこちらを利用する
    if ( mqttClient.connect(clientId.c_str())) { 
      Serial.println("connected"); 

    }
  }
}





//mqttを送信する関数
//引数：なし
//ステータスを返すようにしたい
void MqttPublish(int moist, int cds){
  

  //第一引数：トピック名
  //第二引数：送信するデータ
  char payload[125];    //大きめに確保
  sprintf(payload,"{\"deviceId\":\"01\",\"moist\":%d,\"light\":%d}", moist, cds);
  mqttClient.publish(topic_pub, payload);
  Serial.println("published Message is ");
  Serial.println(payload);

}

// Macアドレスを文字列で取得する
String getMacAddr()
{
  byte mac[6];
  char buf[50];
  WiFi.macAddress(mac);
  sprintf(buf, "%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  return String(buf);
}
```
のようになります。
ここまでできたら書き込んで、データが送信できているか確認します。

##### クラウド側での受信
ここからはクラウド側での操作になります。
登録しておいたenebularのフローを開いてください。

mqttの受信ノードとデバッグノードをフローに配置します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/8da854f6-f6b4-f81c-d3d3-cbf3e949639f.png)
左側から使用するノードをドラッグドロップしてフローに配置します。
配置したら、線で結んでください。
mqttのノードをダブルクリックして必要な情報を入力していきます。
入力が済んだらデプロイボタンを押してデプロイします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/a8271194-80b7-d9e1-b2d3-3d00e1a22071.png)
デバッグボタンを押して、動作を確認していきます。
データ受信に成功していると、右側の表示領域に受信データが表示されていきます。

##### Kafkaノードのインストール
Node-REDのハンバーガーメニューからパレットの管理を選択します。
ノードの追加を選択して、検索窓にkafkaと入力して検索します。
2番目に出てきたnode-red-contrib-kafka-managerを選択して追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244155/3864ed1d-065c-7e39-fdba-91601ae4ea8f.png)
kafkaの接続情報については接続先のサーバーのipアドレスとtpic名、ポート番号が必要になります。
接続先の情報はサーバー側のハンズオンチームから共有してもらえるようになっていますので、当日に共有します。


##### 参考情報
[関連github shinrinakamura/esp32handson
](https://github.com/shinrinakamura/esp32handson/tree/main)
[ESP32で始めようIoT開発ハンズオン](https://speakerdeck.com/shinrinakamura/esp32deshi-meyouiotkai-fa-hanzuon)
[ESP32で始めようIoT開発ハンズオン　サーバーサイド](https://www.slideshare.net/ssuserb158e8/esp32iotpdf)