自宅IoT 第一弾 – 4.1ソフトウェア (IoT)

投稿者: | 2020-02-16

開発環境構築

今回IoT デバイス側のコード実装には、Arduino IDE をMacBook Proにインストールして使用。

環境構築手順

1. Install Arduino IDE

2. Setup IDE

省略!

3. Add additional libraries

ESP32 ESP-32S
BME280
Adafruit_SSD1306

4. Install USB- UART bridge driver

5. Install phython3 (Option)

2台あるうちのIDEをインストールしたMacBook Pro には何故かphyhon3 がインストールされていなくてエラーとなったので別途インストールを行った。

コード記述

機能設計

機能1 – WEB サーバー

Wifi 接続を前提として、WEB 機能を実装。

環境により要下記情報変更
IP アドレス – 192.168.1.49 に固定
ポート番号 – 8090 (将来的なNAT用)

機能2 – センサ情報読み出し

BME280 センサから、温度・湿度・気圧情報を読み出す

機能3 – OLED表示

ブレッドボード上のOLEDディスプレーに、現在時刻と計測値を交互に表示

コード記述

Arduino IDE で下記コードをコンパイル&書き込みを行う。

– 要変更箇所
Wifi のSSIDとパスワード情報は環境に合わせて変更
固定IP アドレスを設定するようになっているが、環境に応じて変更
DHCP 管理で良ければ、WiFi.config行をコメントとすれば良い
(IP アドレスはシリアル画面に表示される)

/*
// WiFi Network
*/
#include <WiFi.h>
#include <WiFiClient.h>

const char* ssid = "xxxxxxx";     // 要変更
const char* password = "yyyyyyy"; // 要変更

IPAddress ip(192, 168, 1, 49);       // 固定IPアドレス設定関係
IPAddress gateway(192,168, 1, 1);    //
IPAddress subnet(255, 255, 255, 0);  //
IPAddress DNS(192, 168, 1, 1);       //

/*
// Web Server
*/
#include <WebServer.h>
WebServer server(8090);              // ポート番号

/*
// BME280 Sensor (Temperature, Humidity, Pressure)
*/
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

//#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME280 bme; // I2C
//Adafruit_BME280 bme(BME_CS); // hardware SPI
//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI

/*
//  OLED SSD1306
*/
#include <Wire.h>
#include "SSD1306.h"            // ディスプレイ用ライブラリを読み込み
SSD1306  display(0x3c, 21, 22); // SSD1306インスタンスの作成(I2Cアドレス,SDA,SCL)

/*
//
// Routines
//
*/
void handleData() {
  char dt_msg[20];  // 日時文字列格納用
  char wb_msg[400]; // WEB 返信文字列格納用
  float room_temp,room_humi,room_prss; // 温度データ、湿度データ、気圧データ
  struct tm timeInfo; // 時刻を格納する構造体

  // BME280センサより温度データ、湿度データ、気圧データの取得
  room_temp = bme.readTemperature();
  room_humi = bme.readHumidity();
  room_prss = bme.readPressure() / 100.0F;

  // 日時情報の取得
  getLocalTime(&timeInfo);
  sprintf(dt_msg, "%04d/%02d/%02d %02d:%02d:%02d",
          timeInfo.tm_year + 1900, timeInfo.tm_mon + 1, timeInfo.tm_mday,
          timeInfo.tm_hour, timeInfo.tm_min, timeInfo.tm_sec);
  // データ返送文字列作成
  sprintf(wb_msg, "%s\nRoom Temrerature=%2.2f *C,Room Humidity=%2.2f %%,Atmospheric Pressure=%4.2f hPa\n%2.2f\n%2.2f\n%4.2f\n",
          dt_msg, room_temp, room_humi, room_prss, room_temp, room_humi, room_prss);

  server.send(200, "text/plain", wb_msg); // WEB TEXT 返送
  // 日時調整
  configTime(9 * 3600L, 0, "ntp.nict.jp", "time.google.com", "ntp.jst.mfeed.ad.jp");
}

void handleRoot() {
  char dt_msg[20];  // 日時文字列格納用
  char wb_msg[400]; // WEB 返信文字列格納用
  struct tm timeInfo; // 時刻を格納する構造体
  float room_temp,room_humi,room_prss; // 温度データ、湿度データ、気圧データ

  // 日時情報の取得
  getLocalTime(&timeInfo);
  sprintf(dt_msg, "%04d/%02d/%02d %02d:%02d:%02d",
          timeInfo.tm_year + 1900, timeInfo.tm_mon + 1, timeInfo.tm_mday,
          timeInfo.tm_hour, timeInfo.tm_min, timeInfo.tm_sec);
          
  // BME280センサより温度データ、湿度データ、気圧データの取得
  room_temp = bme.readTemperature();
  room_humi = bme.readHumidity();
  room_prss = bme.readPressure() / 100.0F;

  // HTML 構文構築
  sprintf(wb_msg, "<html>\n<head><title>RoomStat WEB</title></head>\n<body>\n<h1>%s</h1>\n<h2>Temperature=%2.2f *C</h2>\n<h2>Humidity=%2.2f %%</h2>\n<h2>Pressure=%4.2f hPa</h2>\n<body>\n</html>\n",
  dt_msg, room_temp, room_humi, room_prss);
  Serial.println(wb_msg);
  server.send(200, "text/html", wb_msg); // WEB HTML 返送
}

void handleNotFound() {
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i = 0; i < server.args(); i++) {
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
}


void setup(void) {
  Serial.begin(115200); // シリアルコンソールの速度設定
  bool status;

  Serial.print(F("Checking BME280... ")); // BME280センサ確認開始メッセージ表示
  // default settings
  // (you can also pass in a Wire library object like &Wire2)
  status = bme.begin(0x76);  
  if (!status) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  } else {
    Serial.println("OK");
  }
  delay(10);

  Serial.print("Checking OLED...   "); // OLEDディスプレー初期化開始メッセージ表示
  status = display.init();             //ディスプレイを初期化
  if (!status) {
    Serial.println("Could not find a valid OLED display, check wiring!");
    while (1);
  } else {
    Serial.println("OK");
  }
  
  display.setFont(ArialMT_Plain_16);    // フォント設定
  display.drawString(0, 0, "Initializing...\n.........\n.........");
  display.display();                    //指定された情報を描画
  delay(10);

  Serial.println("Setting up WEB server"); // WEBサーバー開始メッセージ表示
  WiFi.config(ip, gateway, subnet, DNS);   // Set fixed IP address
  delay(10);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);              // WiFi接続開始
  
  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  
  // 接続情報表示
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  // WEB リクエストマッピング設定
  server.on("/", handleRoot);
  server.on("/data", handleData);
  server.on("/inline", []() {
    server.send(200, "text/plain", "this works as well");
  });
  server.onNotFound(handleNotFound);

  // WEB サービス開始
  server.begin();
  Serial.println("HTTP server started");

  // 時間調整
  configTime(9 * 3600L, 0, "ntp.nict.jp", "time.google.com", "ntp.jst.mfeed.ad.jp");
}


void loop(void) {
  struct tm timeInfo; // 時刻を格納する構造体
  char dt_msg[20];  // 日時文字列格納用
  char dp_msg[100]; // ディスプレー表示文字列格納用
  float room_temp,room_humi,room_prss; // 温度データ、湿度データ、気圧データ

  // 日時情報の取得
  getLocalTime(&timeInfo);
  sprintf(dt_msg, "%04d/%02d/%02d\n%02d:%02d:%02d",
          timeInfo.tm_year + 1900, timeInfo.tm_mon + 1, timeInfo.tm_mday,
          timeInfo.tm_hour, timeInfo.tm_min, timeInfo.tm_sec);

  server.handleClient();
  // Display Date/Time to OLED    
  display.clear();                  // 画面消去
  display.drawString(0, 0, dt_msg); // 画面描画
  display.display();                // 画面表示
  delay(1000);

  // OLED表示保持ディレイループ (3秒)
  // 単に server.handleI();delay(3000) とするとWEB不感時間が長すぎるので...
  for (uint8_t i = 0; i < 30; i++) {
    server.handleClient();
    delay(100);
  }
  
  // BME280センサより温度データ、湿度データ、気圧データの取得
  room_temp = bme.readTemperature();
  room_humi = bme.readHumidity();
  room_prss = bme.readPressure() / 100.0F;

  sprintf(dp_msg, "Temp:%2.2f\nHumd:%2.2f\nPres:%4.2f",
    room_temp, room_humi, room_prss);

  display.clear();                  // 画面消去
  display.drawString(0, 0, dp_msg); // 画面描画
  display.display();                // 画面表示
  delay(1000);

  // OLED表示保持ディレイループ (4秒)
  for (uint8_t i = 0; i < 40; i++) {
    server.handleClient();
    delay(100);
  }
}

WEB(&Network)、BME280、OLED のブロック毎に出来るだけ分けているので、例えばOLED が必要なければ削除は容易だと思う。

シリアルコンソールに初期化状況等表示されるようになっている。

対象IP アドレス接続可能なネットワーク内で、ブラウザから下記URLにアクセスしてセンサーからの情報が得られれば確認完了。

http://192.168.1.49:8090/

http://192.168.1.49:8090/data

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です