Azure + ESP32 で つまずきました 工場系IoTでありがちなケース

ESP32

ESP32でMicrosoft Azureにデータを送信しようと思ったときに、その作法を知らなくてつまずいたので記事にします。自分にはこういう事を聞ける人が周りにいないため、色々な方のブログやサイトを訪問し、悩みながらなんとか解決できました。似たような状況の人がなるべく無駄に時間費やさないようになれば良いと思います。

いろいろ調べて以下のことができるようになりました。

  • Azureに送信する
  • 送信した後にESP32をDeep Sleepさせる
  • 割り込みタイマーでLED点滅させる
  • ESP32がWiFiにつながりにくい時の対処例
  • Deep Sleepしても消えないメモリに変数を格納する
  • シグナルタワーのように点滅するランプをCDSセンサーで取得する

    Deep SleepはESP32の消費電力や発熱低減に大きく寄与しますのでお勧めです。

はじめに

Arduino IDEを使ったスケッチになりますが、コードについてもっと良い方法や、間違いがあるかもしれませんが、その時はご指摘いただければ幸いです。
以下の記事の続きになりますので、そちらも合わせてごらんください。

回路図

今回、回路図は準備していませんので画像を参考にしてください。

32 33 34 CDSセンサー(抵抗を入れてください)
     CDSセンサーはアナログ出力です。アナログ入力できるピンにアサインしてください。
16 目覚めたときの確認用LED(抵抗をいれてください)
17 送信状態確認用LED(抵抗をいれてください)

ESP32、ブレッドボード、CDSセンサー、LED、ジャンパーワイヤー、抵抗などは、秋月電子で購入できると思います。

CDSセンサーを固定するのは、部屋の明かりを拾わないように出来るだけ覆ってください。
最初は粘土のような物でもいいと思います。始末が悪いかもですが。。。

Azure送信

Azureに送信するコードです。
合わせて全体スケッチもご確認ください。

#define MESSAGE_MAX_LEN 256
#define DEVICE_ID "NP5"
const char *messageData = "{\"deviceId\":\"%s\", \"messageId\":%d, \"sensorValueY\":%d, \"sensorValueG\":%d, \"sensorValueR\":%d}";
snprintf(messagePayload,MESSAGE_MAX_LEN, messageData, DEVICE_ID, messageCount++, sensorValueY, sensorValueG, sensorValueR_max);

以下はAzure側のクエリの例です。

SELECT 
    deviceId as device, 
    DATEADD(hour, 9, EventEnqueuedUtcTime) as time, 
    sensorValueR as sensorValueR,
    sensorValueG as sensorValueG,
    sensorValueY as sensorValueY
INTO 
    outputpowerbi01  //それぞれの環境に合わせてください
FROM 
    inputiothub01 TIMESTAMP BY EventEnqueuedUtcTime  //それぞれの環境に合わせてください

Deep Sleep

Deep Sleepするコードです。
マイクロ秒で指定するので1,000,000で1秒です。
60秒で復帰させるには60*1000000となります。

  esp_sleep_enable_timer_wakeup(60 * 1000000);
  esp_deep_sleep_start();

割り込みタイマーでLED点滅

#include <uTimerLib.h>
TimerLib.setInterval_us(flash,500000); //割り込みタイマー LED点滅開始 0.5秒間隔
TimerLib.clearTimer();                //割り込みタイマー LED点滅停止
void flash(){
  led = ~led;                   //LEDの状態を反転する
  digitalWrite(ledPin, led);    //LEDの点灯・消灯を行う
}

WiFiがつながらない時の対処法

countAが30を超えたら一旦WiFi.disconect(true)を入れます。
このループが3回(countB)超えたら、ESP32を再起動させます。

int countA =0;
int countB =1;
WiFi.begin(ssid, password);  //  Wi-Fi APに接続
  while (WiFi.status() != WL_CONNECTED) {  //  Wi-Fi AP接続待ち
    delay(400);
    Serial.print(".");
    if (++countA > 30){                  //カウント30でWiFi接続やり直し
      WiFi.disconnect(true);
      WiFi.begin(ssid, password);
      Serial.println("");
      Serial.print("reconnecting");
      countA = 0;
  
      if (++countB > 3){                 //カウント30を3回繰り返したら再起動
        Serial.println("...REBOOT...");
        Serial.println("");
        
        ESP.restart();
        }
      }
   }

Deep Sleepしても消えないメモリに変数を格納する

RTC_DATA_ATTRを付けるとスローメモリ領域にカウントを記憶します。

RTC_DATA_ATTR int bootCount = 0;

今回はESP32を再起動するカウントに使用しました。

シグナルタワーのように点滅するランプをCDSセンサーで取得する

アナログセンサーで明かりを測定するタイミングで、ランプが点滅して消えていると正しい数値が測れません。
そんな時、1秒間で10回測定して一番大きな数値を取得するコードにしました。
今回は赤ランプのみ点滅するので、sensorValueRのみに適応しています。

sensorValueR_max = -1;            //初期値はマイナスにすれば間違いない
  for (i = 0; i < 10; i++){                  //10回ループ
    sensorValueR = analogRead(sensorPinR);   //sensorPinR = 32
    if (sensorValueR > sensorValueR_max){
      sensorValueR_max = sensorValueR;
    }
    delay(100);  //0.1秒
  }

全体スケッチ

/*ESP32+CDS+WiFi+Azure
 * Deep Sleep
*/
#include <WiFi.h>
#include "Esp32MQTTClient.h"
#include "AzureIotHub.h"
#include <uTimerLib.h>
#define MESSAGE_MAX_LEN 256
#define DEVICE_ID "ABC1" 
const char* ssid = "WiFi SSID"; //WiFi SSID
const char* password = "Password"; //WiFi Password
/*String containing Hostname, Device Id & Device Key in the format:  */
static const char* connectionString = "Azure Key"; //プライマリ接続文字列
const int sensorPinR = 32;
const int sensorPinG = 33;
const int sensorPinY = 34;
RTC_DATA_ATTR int bootCount = 0; // RTCスローメモリに変数を確保
const char *messageData = "{\"deviceId\":\"%s\", \"messageId\":%d, \"sensorValueY\":%d, \"sensorValueG\":%d, \"sensorValueR\":%d}";
static bool hasIoTHub = false;
int messageCount = 1;
const int ledPinRun = 16;
const int ledPin = 17;
int led;
void setup() {
  Serial.begin(115200);
  pinMode(ledPinRun, OUTPUT);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPinRun, HIGH);    //起動確認 LEDの点灯を行う
  if( bootCount == 0 ){             // 初回起動だけシリアル初期化待ち
    delay(1000);
  } else {
    delay(10);
  }
  bootCount++;                      // 起動回数カウントアップ
  Serial.println("");
  Serial.printf("起動回数: %d ", bootCount);
  //DeepSleep60回目でReBoot再起動
  if( bootCount >= 60 ){
    Serial.println("REBOOT...");
    ESP.restart();
  } 
  Serial.println("Start");
  int countA =0;
  int countB =1;
  Serial.println("Starting connecting WiFi.");
  delay(10);
  
  WiFi.begin(ssid, password);  //  Wi-Fi APに接続
  while (WiFi.status() != WL_CONNECTED) {  //  Wi-Fi AP接続待ち
    delay(400);
    Serial.print(".");
    digitalWrite(ledPin, HIGH);            //接続状態確認用にLED点滅
    delay(100);
    digitalWrite(ledPin, LOW);
    if (++countA > 30){                  //カウント30でWiFi接続やり直し
      WiFi.disconnect(true);
      WiFi.begin(ssid, password);
      Serial.println("");
      Serial.print("reconnecting");
      countA = 0;
  
      if (++countB > 3){                 //カウント30を3回繰り返したら再起動
        Serial.println("...REBOOT...");
        Serial.println("");
        
        ESP.restart();
        }
      }
   }
   led = LOW;
   TimerLib.setInterval_us(flash,500000);  //割り込みタイマー LED点滅開始 Azure通信
   Serial.print("WiFi connected\r\nIP address: ");
   Serial.println(WiFi.localIP());
   Serial.print(" カウント:");Serial.print(countB);Serial.print("-");Serial.println(countA);
   if (!Esp32MQTTClient_Init((const uint8_t*)connectionString))
    {
      hasIoTHub = false;
      Serial.println("Initializing IoT hub failed.");
      ESP.restart();
      return;
    }
    hasIoTHub = true;
   TimerLib.clearTimer();                //割り込みタイマー LED点滅停止
   digitalWrite(ledPin, LOW);
   delay(500);
    // 光センサーの値を取得
  int sensorValueY = analogRead(sensorPinY);
  int sensorValueG = analogRead(sensorPinG);
  int i , sensorValueR, sensorValueR_max;
  sensorValueR_max = -1;
  for (i = 0; i < 10; i++){                  //最大値取得 1秒間に10回確認
    sensorValueR = analogRead(sensorPinR);
    Serial.print("sensorValueR:");
    Serial.println(sensorValueR);
    if (sensorValueR > sensorValueR_max){
      sensorValueR_max = sensorValueR;
      Serial.print("sensorValueR_max:");
      Serial.println(sensorValueR_max);
    }
    delay(100);
  }
  // シリアルモニターに光センサーからの値を表示
  Serial.print("sensor_Y = ");
  Serial.println(sensorValueY);
  Serial.print("sensor_G = ");
  Serial.println(sensorValueG);
  Serial.print("sensor_R = ");
  Serial.println(sensorValueR_max);
  
  digitalWrite(ledPin, HIGH);  //LED点灯
  if (hasIoTHub)
    {
      char messagePayload[MESSAGE_MAX_LEN];
      // replace the following line with your data sent to Azure IoTHub
      snprintf(messagePayload,MESSAGE_MAX_LEN, messageData, DEVICE_ID, messageCount++, sensorValueY, sensorValueG, sensorValueR_max);
      if (Esp32MQTTClient_SendEvent(messagePayload))
      {
        Serial.println("Sending data succeed");
      } else {
        Serial.println("Failure...");
      }
      delay(500);
    }
  Serial.println("WiFi Disconnected");
  WiFi.disconnect(true);
  digitalWrite(ledPin, LOW); // LED 消灯
 
  digitalWrite(ledPinRun, LOW);    //Running LEDの消灯を行う
  
  // 1000000us = 1sのタイマー設定
  Serial.println("...Deep Sleep");
  esp_sleep_enable_timer_wakeup(60 * 1000000);
  // ディープスリープ
  esp_deep_sleep_start();
}
void loop() {
}
void flash(){
  led = ~led;                   //LEDの状態を反転する
  digitalWrite(ledPin, led);    //LEDの点灯・消灯を行う
}

さいごに

みなさんの環境によって変更しながら参考にしていただけますと幸いです。
コードをコピーされる場合、全角スペースが混じっているとエラーになりますので、ご注意ください。
いつか、Azureで受信したデータをNode-Redで表示する方法についてまとめたいと思います。

更新
Node-Redで表示したときの方法を記事にしました。

コメント

タイトルとURLをコピーしました