LoginSignup
3
2

More than 3 years have passed since last update.

家の環境(温度・湿度・大気圧)をAWSに蓄積して分析してみるの巻 その2

Last updated at Posted at 2019-12-09

前回まで

今回の内容

今回は、AWS IoT向けにデバイスからpublishするところまで作業します。

先にデバイス側のコードを紹介

aws-iot-publish.ino
#include <time.h>
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include "BME280SPI.h"

#define JST     3600*9

const String wifiSSID = "<yourSSID>";      // お使いのWiFiのSSIDを入力してください
const String wifiPass = "<yourPass>";  // お使いのWiFiのパスワードを入力してください

const uint8_t pinLED = 14;       // 緑LEDピン番号
const uint8_t pinSW = 0;         // タクトスイッチSW1ピン番号
const uint8_t pinBME280CS = 15;  // BME280センサーCSピン番号
const uint8_t pinSDCS = 2;       // SDカードCSピン番号

const char* server = "<AWS IoTのエンドポイント>";
const int port = 8883;
const char* pubTopic =  "<トピック名>";

const char* Root_CA = \
"-----BEGIN CERTIFICATE----\n" \
"~\n" \
"~\n" \
"中略\n" \
"-----END CERTIFICATE-----\n";

const char* Client_cert = \
"-----BEGIN CERTIFICATE-----\n" \
"~\n" \
"~\n" \
"中略\n" \
"-----END CERTIFICATE-----\n";

const char* Client_private = \
"-----BEGIN RSA PRIVATE KEY-----\n" \
"~\n" \
"~\n" \
"中略\n" \
"-----END RSA PRIVATE KEY-----\n";

WiFiClientSecure espClient;
PubSubClient mqttClient(espClient);

//---------------------------------
// 変数

BME280SPI bme280(pinBME280CS);       // BME280センサーを制御するクラス
bool statWiFiConnected = true;  // WiFiに接続されているかを記憶しておく
uint32_t lastStat = 0;          // 最後にstat()を実行した時間を記憶しておく
char currentTimeStr[19];

//---------------------------------
// 関数
void connectAWSIoT() {
    while (!mqttClient.connected()) {
        if (mqttClient.connect("ESP8266")) {
            Serial.println("AWSIoT Connected.");
        } else {
            Serial.print("Failed. Error state=");
            Serial.print(mqttClient.state());
            // Wait 5 seconds before retrying
            delay(5000);
        }
    }
}

void mqttCallback (char* topic, byte* payload, unsigned int length) {
    Serial.print("Received. topic=");
    Serial.println(topic);
    for (int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
    }
    Serial.print("\n");
}

void mqttLoop() {
    if (!mqttClient.connected()) {
        connectAWSIoT();
    }
    mqttClient.loop();
}

void getCurrentDateTimeStr() {

  time_t t;
  struct tm *tm;

  t = time(NULL);
  tm = localtime(&t);
  sprintf(currentTimeStr,"%04d/%02d/%02d %02d:%02d:%02d",
        tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
        tm->tm_hour, tm->tm_min, tm->tm_sec);
}

void setup() {
  Serial.begin(115200);
  delay(1);
  Serial.printf("\n\n-----------------------\n");
  Serial.printf("Program Start\n");

  pinMode(pinLED, OUTPUT);   // LED
  pinMode(pinSW, INPUT);     // SW1

  // SPI CSピンをHighにする
  pinMode(pinBME280CS, OUTPUT);
  pinMode(pinSDCS, OUTPUT);
  digitalWrite(pinBME280CS, HIGH);
  digitalWrite(pinSDCS, HIGH);

  // BME280 Measure until success
  while(BME280SPI::statSuccess!=bme280.meas()){
    Serial.print("BME280 is not available\n");
    delay(5000);
  }

  // WiFi
  Serial.printf("\nWiFi");
  WiFi.mode(WIFI_STA);
  WiFi.begin(wifiSSID.c_str(), wifiPass.c_str());

  // 接続完了まで待つ
  while(WiFi.status()!=WL_CONNECTED){
    delay(1000);
  }
  Serial.printf(" Connected IP : ");
  Serial.println(WiFi.localIP());

  //時刻合わせ
  configTime( JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");

  delay(5000);

  getCurrentDateTimeStr();
  Serial.printf("Current date-time is ");
  Serial.println(currentTimeStr);

  //AWSへの接続処理
  //証明書、秘密鍵のオブジェクト作成
  BearSSL::X509List cert(Root_CA);
  BearSSL::X509List client_crt(Client_cert);
  BearSSL::PrivateKey key(Client_private);

  //証明書、秘密鍵の設定
  espClient.setTrustAnchors(&cert);
  espClient.setClientRSACert(&client_crt, &key);

  //接続処理
  mqttClient.setServer(server, port);  
  mqttClient.setCallback(mqttCallback);
  connectAWSIoT();
}

// 5秒おきに動作状況を確認する処理
void stat(){
  pinMode(pinLED, OUTPUT);

  if(WiFi.status()==WL_CONNECTED){
    // WiFi切断 -> 接続に変わった場合ログを残す
    if(!statWiFiConnected){
      statWiFiConnected = true;
      Serial.println(" WiFi Connected");
      Serial.println(WiFi.localIP());
    }

    // 正常状態 LEDを1回点滅
    digitalWrite(pinLED, 1);
    delay(100);
    digitalWrite(pinLED, 0);
  }else{
    // WiFi接続 -> 切断に変わった場合ログを残す
    if(statWiFiConnected){
      statWiFiConnected = false;
      Serial.printf(" WiFi Disconnected\n");
    }

    // WiFi切断状態 LEDを2回点滅
    digitalWrite(pinLED, 1);
    delay(50);
    digitalWrite(pinLED, 0);
    delay(450);
    digitalWrite(pinLED, 1);
    delay(50);
    digitalWrite(pinLED, 0);
  }
}

// センサーの気温、気圧、湿度をAWSにpublishする
void publishToAWS() {

  Serial.printf("Start publishing data to AWS\n");

  // 測定処理おこなう
  bme280.meas();

  // センサーからデータ取得に失敗
  if(!bme280.getResult()){
    Serial.printf(" BME280 Not Available\n");
    return;
  }

  getCurrentDateTimeStr();

  String json = "{";
    json +=  "\"time\":\"";
    json += currentTimeStr,
    json += "\",";
    json += "\"temperature\":\"";
    json += bme280.getMeasStr(BME280SPI::selectTemp).c_str();
    json += "\",";
    json += "\"humidity\":\"";
    json += bme280.getMeasStr(BME280SPI::selectHumidity).c_str();
    json += "\",";
    json += "\"temperature\":\"";
    json += bme280.getMeasStr(BME280SPI::selectPressure).c_str();
    json += "\"}";

  Serial.println(json);

  if(!mqttClient.publish(pubTopic, json.c_str())){
    Serial.println("Failed to publish\n");
  }else{
    Serial.println("Succeed to publish\n");
  }

}

void loop() {
  if(millis() > lastStat+5000){      // 5秒おきに実行
    lastStat = millis();
    stat();
    publishToAWS();
  }
  delay(1);   // 消費電力低減
  mqttClient.loop();
}

ポイント

全体的に

  • ループとかwifi接続とかの実装はこちらの公式サンプルを参考に実装。
  • とりあえずpublishすることを優先したので、実装的に変なとこありますがご容赦ください。

証明書・秘密鍵について

  • とりあえず今回はベタ書きしました。
  • 最初、ここのやり方を参考に実装したけどうまくいかず。
    • こんなエラーが出た
      error: no matching function for call to BearSSL::WiFiClientSecure::setCACert(const char*&)
  • 参考にしてた人のライブラリがESP32用で、WifiSecureClientの実装が同名ながら違っていた(今回使ったのはESP8266
  • 最終的にここを見て解決
    • この参考記事には時刻合わせも必要という記載があったのでここを参考に実装しました。

センサー情報の取得について

掲載コード以外でやったこと

  • 最初のほう、fork/exec /Users/yourusername/Library/Arduino15/packages/esp8266/tools/python3/3.7.2-post1/python3: no such file or directoryというエラーが出ました。
  • PubSubClient.hのパラメーターをいじりました。
    • MQTT_MAX_PACKET_SIZE256に。
      • publishに失敗する原因を切り分けてるうちに、publishするデータ長によって成否が分かれたので気づいた。
    • MQTT_KEEPALIVE30に。
      • ここに書いてたのでとりあえず。
  • とりあえず動作確認する際は、AWS IoTのテスト機能でpublishしたデータをsubscribeできるか確認しました。デバイスがpublishするトピックをテスト機能のほうでsubscribeして、うまくいけばpublishされたデータが見れるようになります。 image.png

次回やる予定

publishしたデータをDynamoDBにぶち込んで何しか可視化したいと思います。
と思ったけどS3にしました。

次回記事

家の環境(温度・湿度・大気圧)をAWSに蓄積して分析してみるの巻 その3

他、参考記事

https://blog.maripo.org/2017/07/esp32-aws-iot/
https://blog.maripo.org/2017/07/esp32-aws-iot-troubleshooting/

3
2
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
3
2