LoginSignup
1
1

More than 3 years have passed since last update.

初めてのIoTをESP32とMQTTで。2020年度版

Last updated at Posted at 2021-02-27

1. はじめに

「職場改善活動しろと言われた。みんなで話し合ったら、IoTをやってみようという話になった。ESP32は購入してみたけど、どうやってIoTをしたら良いの?」という方向けです。
厳密な意味でのIoTは置いておいて、「とにかく、ESP32で、Wi-Fiを使って、情報を転送し、蓄積したい」人向けです。

2. 参考図書

今回の内容を説明すると、よく「これらの内容がまとまった本、ありますか?」と聞かれます。私が把握している限り、同じ内容のものは無いと思います、今のところ。

ただ、参考になる本は多いです。それぞれ、プログラム例が載っており、すぐに取りかかるのに丁度良い内容です。これらの本では、クラウドに上げるというところまでを扱うため、利用するサービスは、AmbientやAWS,Azureなどを挙げています。個人で行うには、とても便利なサービスですが、職場で行うには極めてハードルが高くなります。それは、データを外部に送信するため決裁が必要になるためです。

今回のような「なんちゃってIoT」について、全てを網羅する本がまだ無いという状況です。そもそも、IoTじゃないという点で、本にしづらいのかもしれません。

  • 下島 健彦、IoT開発スタートブック ── ESP32でクラウドにつなげる電子工作をはじめよう!、技術評論社、2019/8/13
  • 藤本 壱、ESP32&Arduino 電子工作 プログラミング入門、技術評論社、2020.4/15

3. 準備するもの

  • ESP32
  • ESP32のプログラム開発環境
  • ラズパイ(or MQTTブローカ)
  • Wi-FiのSSID、パスフレーズ(パスワード)
  • サーバのIPアドレス

ものが準備されてない場合は、まずは、 https://qiita.com/dzonesasaki/items/3e2bedfd03e435b52834 などを見て、購入などを済ませましょう。

4. サーバとWi-Fiの準備

サーバはラズパイを前提に記載します。それ以外の場合はそれぞれに合わせて読み替えて下さい。ラズパイを社内のLANに接続します。有線か無線どちらかで接続します。Wi-Fi のSSIDとパスフレーズは、管理者に聞きましょう。

ラズパイにmosquittoとnode-redとinfluxdb v1などセッティングしてください。https://qiita.com/dzonesasaki/items/ab904d70fa4b3d4ce7ce などを参考にしてください。

次にラズパイのIPアドレスの固定化については、 https://qiita.com/dzonesasaki/items/7942b13ab9a799f652ff などを参考にしてください。 ここまでで、次の情報が整っているはずです。

  • Wi-FiのSSID
  • Wi-Fiのパスフレーズ(パスワード)
  • サーバ(ラズパイ)のIPアドレス

あとは、MQTTのトピック名を決めましょう。
仮置きするなら、myTopicなどで良いと思いますが、何の情報を集取するのかを決めてその名称にします。

例えば、NCの1番の電源ON/OFFを情報として取得する場合は、ncno1onoff のようにです。
次の情報も整いました。

  • MQTTのトピック名

5. ESP32のプログラムをしましょう

ここでは、Arduino-IDEでESP32のコードを開発することを前提にします。
次のプログラムをコピー&ペースト使います。ただし、次の情報を自分で書き換えて下さい。ここでは仮のものを入力しています。

  • Wi-FiのSSID : SSID
  • Wi-Fiのパスフレーズ(パスワード):PASSWORD
  • サーバ(ラズパイ)のIPアドレス : 192.168.1.1
  • MQTTのトピック名 : myTopic
  • MQTTクライアントID : myMqttSub
  • 入力ピン : 26番
  • 測定周期 : 2秒 (=2000ミリ秒)
  • ウオッチドッグタイマー : 6x10秒

esp32_mqtt_pub.ino
#include <WiFi.h>
#include <PubSubClient.h> // https://github.com/knolleary/pubsubclient

char *ssidC="SSID";//ここを書き換える
char *passC="PASSWORD";//ここを書き換える
char* servC="192.168.1.1";//ここを書き換える

const char* topic = "myTopic";//ここを書き換える
const String clientId = "myMqttPub" ;//ここを書き換える

#define PIN_IN 26 //ここを書き換える

WiFiClient myWifiClient;
const int mqttPort = 1883;
PubSubClient mqttClient(myWifiClient);

uint32_t gu32Count=0;

#define NUM_1SEC_MICROSEC (1000000)
#define NUM_SEC_TIMER (10)
#define NUM_TIMER_MICROSEC (NUM_SEC_TIMER*NUM_1SEC_MICROSEC)
hw_timer_t * handleTimer = NULL; 

portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; 

volatile int gviCountWatchDog=0;
#define NUM_MAX_WATCHDOG (6) // 60sec //ここを書き換える


void IRAM_ATTR irq_Timer01()
{
  portENTER_CRITICAL_ISR(&timerMux);
  gviCountWatchDog++;
  if (gviCountWatchDog > NUM_MAX_WATCHDOG)
  {
    selfRestartProcess();
  }

  portEXIT_CRITICAL_ISR(&timerMux);
}//irq_Timer01()

void selfRestartProcess(void)
{
  //memory copy to flash
  ESP.restart(); // reboot
}//selfRestartProcess()

void init_TimerInterrupt()
{
  gviCountWatchDog =0;
  handleTimer = timerBegin(0, 80, true); // Num_timer ,  counter per clock 
  timerAttachInterrupt(handleTimer, &irq_Timer01, true);
  timerAlarmWrite(handleTimer, NUM_TIMER_MICROSEC, true); //[us] per 80 clock @ 80MHz
  timerAlarmEnable(handleTimer);
}//init_TimerInterrupt()

void initWifiClient(void){
  Serial.print("Connecting to ");
  Serial.println(ssidC);
  uint16_t tmpCount =0;

  WiFi.begin( ssidC, passC);
  while (WiFi.status() != WL_CONNECTED) {
  delay(500);
  Serial.print(".");
  tmpCount++;
  if(tmpCount>128)
  {
    //gFlagWiFiConnectFailure = true;
    Serial.println("failed  ");
    return;
  }
}
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void connectMqtt()
{
  mqttClient.setServer(servC, mqttPort);
  while( ! mqttClient.connected() )
  {
    Serial.println("Connecting to MQTT Broker...");
    //String clientId = "myMqttPub" ;
    if ( mqttClient.connect(clientId.c_str()) )
    {
      Serial.println("connected"); 
    }
  }
}

void doPubMqtt(char * cValue)
{
  if(!mqttClient.connected())
  { 
    if (mqttClient.connect(clientId.c_str()) )
    {
      Serial.println("reconnected to MQTT broker"); 
    }
    else
    {
      Serial.println("failure to reconnect to MQTT broker");
      return;
    }
  }
  //mqttClient.loop();
  boolean bFlagSucceed=mqttClient.publish(topic,cValue);
  //free(cValue);
  if(bFlagSucceed)
   {
    Serial.print("done: ");
    Serial.println(cValue);
  }
  else
  {
    Serial.println("failure");
  // run to check: mosquitto_sub -t myTopic
  }
}

void setup() {
  Serial.begin(115200);
  gviCountWatchDog=0; 
  init_TimerInterrupt();
  initWifiClient();
  connectMqtt();
  pinMode(PIN_IN,INPUT);

}

void loop(){
 Serial.println("start loop");
  portENTER_CRITICAL(&timerMux);
    gviCountWatchDog=0; //clear Counter
  portEXIT_CRITICAL(&timerMux);

  float fSensValue=analogRead(PIN_IN);

  //char cValue[11];
  char *cValue;
  cValue = (char *)malloc(12);
  sprintf(cValue, "%1.5e", fSensValue);

  doPubMqtt(cValue);
  free(cValue);

  delay(2000);//ここを書き換える

}

Arduino-IDEを起動して、ファイル → 新規作成をクリックします。
初めに出てくる内容を全て削除して、上のコードをコピーし、Arduino-IDE上にペースト します。
そして、自分の状態に合わせて、上の書き換える部分を修正しましょう。

測定周期の変更は、最後のdelay(2000);にある2000を書き換えます。

聞き慣れないウオッチドッグという単語があります。
これは、プログラムが途中で予期しない停止があった場合に、ESP32自身が自分をリセットするための機能(プログラム)です。
たとえば、測定周期を10秒にした場合は、ウオッチドッグタイマーは60秒のままで良いと思いますが、測定周期が60秒の場合は、ウオッチドッグタイマーは3分などにした方が良いでしょう。そうしないと、測定せずに自己リセットされてしまいます。

書き換えたらESP32に書き込みます。
その前に、何に書き込むのかを設定します。

使っているのがESP32開発ボードの場合は、

ツール → ボード:" @@@@@@ " → ESP32 Arduino → ESP32 Dev Module

にします。@@@@は、前回の内容が入ってます。初期の場合は、Arduino-Unoになっていると思います。

使っているのがM5StickCやM5StickC-plusの場合は、

ツール → ボード:" @@@@@@ " → ESP32 Arduino → M5Stick-C

にします。
あとは、

ツール → シリアルポート → COM数字

にします。使っている状況で、COMの後ろの数字が異なります。書き込みでエラーが出たら、別の数字のものにします。

数字を確認する場合、ESP32とPCをUSBケーブルで接続する前に、ツール → シリアルポートで内容を確認し、USBケーブルを接続した時に、ツール → シリアルポートで増えたものが、ESP32であることが判ります。

これらが終わったら、 スケッチ → マイコンボードに書き込む をクリックします。

6. サーバ側のプログラムをしましょう

ここでは、サーバをラズパイとします。ラズベリーのスタートボタンから、プログラム → node-redをクリックすると、node-redが起動します。

node-redの右上に 三 のようなボタンを押して、「読み込み」をクリックすると、jsonをペーストするウインドウが出ますので、そこに次のコードをペーストし、貼り付けます。最後にデプロイボタンを押します。変更したらその都度デプロイボタンを押します。

node-red_read.png

右上に、テントウ虫のようなアイコンがあるのでクリックします。
そこに、測定周期ごとに数字が現れるはずです。これで、ESP32からの数値を受け取っていることが判ります。

node-red_mqtt_sub.json
[
    {
        "id": "9b3687a0.66a128",
        "type": "mqtt in",
        "z": "f61cf7aa.e627d",
        "name": "",
        "topic": "myTopic",
        "qos": "2",
        "datatype": "auto",
        "broker": "4e38b730.53a58",
        "x": 140,
        "y": 100,
        "wires": [
            [
                "53dd59d2.d3dad8",
                "9b3f417c.243948"
            ]
        ]
    },
    {
        "id": "212a6d2.4d54f92",
        "type": "debug",
        "z": "f61cf7aa.e627d",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 670,
        "y": 100,
        "wires": []
    },
    {
        "id": "53dd59d2.d3dad8",
        "type": "csv",
        "z": "f61cf7aa.e627d",
        "name": "",
        "sep": ",",
        "hdrin": "",
        "hdrout": "none",
        "multi": "one",
        "ret": "\\n",
        "temp": "",
        "skip": "0",
        "strings": true,
        "include_empty_strings": "",
        "include_null_values": "",
        "x": 310,
        "y": 100,
        "wires": [
            [
                "391eec55.13d354"
            ]
        ]
    },
    {
        "id": "391eec55.13d354",
        "type": "function",
        "z": "f61cf7aa.e627d",
        "name": "",
        "func": "var Sens=msg.payload.col1;\nvar myValues = {Sens:Sens}\nmsg.payload=myValues\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 480,
        "y": 100,
        "wires": [
            []
        ]
    },
    {
        "id": "9b3f417c.243948",
        "type": "debug",
        "z": "f61cf7aa.e627d",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 340,
        "y": 160,
        "wires": []
    },
    {
        "id": "4e38b730.53a58",
        "type": "mqtt-broker",
        "name": "",
        "broker": "localhost",
        "port": "1883",
        "clientid": "",
        "usetls": false,
        "compatmode": false,
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "closeTopic": "",
        "closeQos": "0",
        "closePayload": "",
        "willTopic": "",
        "willQos": "0",
        "willPayload": ""
    }
]

この先、データ蓄積を行いたい場合は、 https://qiita.com/dzonesasaki/items/319173274f0af448b2dc などを参考にしてください。

7. おわりに

細かい説明抜きに、まずはIoTを試してみる、という内容を記載しました。判らなければgoogleるという習慣をつけてください。そして、15分悩んで解決しなければ、誰かに聞くのが良いでしょう。1時間かけて、何も進展が無ければ、それは何かが間違っています。それ以上時間をかけるのは望ましくないかもしれません。
なお、今回は同一のLAN上にあるラズパイに接続する前提でした。Node-redを使っていますので、クラウドにあるNode-redでも同じことができます。そういった意味で、IoTの入り口となる内容だと思います。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1