ESP-32S NodeMCU-32S Arduino WIFI接続とWebServer化とデータベースへのデータ保管 「外部からのエアコン制御」への道 その4

さ、徐々に進んで来てますよ、Iotもしくはスマート家電化。

自分の場合、どうやって温度と湿度のデータを、Synology ServerのMariaDB(Mysql)へ渡すかというところでかなり悩みました。

セキュリティーも考えないとダメだし、余り難しくてもなぁ。。ということで、温度と湿度のデータを WebServerとして表示させて、表示データをPythonで作ったプログラムをSynologyのタスクスケジューラーで実行してデータ取りに行かせよう。と思います。

他にもやり方が色々あると思いますが、自分の場合はPythonに少し慣れたところがあるのでこの手法にしました。

では、初めにWifi接続とWebServer化です。

ESP-32SのWifiでの接続を試してみました。

現在使用しているESP32と温度センサー↓

参考にさせて頂いたサイト↓

なるほど、こんな感じでWebServerとしていけるのかぁ、とこれ簡単でいいなぁ。と思います。

そして、調べていると面白いサイトがありました。↓

このサイトの仕様が自分のやり方とマッチしてしまったので、これを利用させて頂くことにしました。

元のサイトの表の形式をちょっと変更したのと、1時間で自動更新するように変更しました。

表を変更したのはPythonのスクレイピングでデータを値だけ欲しかったからです。

分かりやすく言うと、「30℃」の℃が要らないので、表示を別にしたということです。

大したことでは無いのですが、Pythonで楽が出来るならそれに越したことは無いもので。

ESP32で1時間更新にしたのは、

Synology側からデータ取得するPythonアプリはSynologyのタスクスケジューラーでの設定で15分毎にデータ取得の設定にしました。

そうすると、ESP32側は、アクセスされたその時にその温度と湿度を表示することになっているので、結局のところ、ESP32での時間の設定はどうでも良く、問い合わせが来たら応えるということです。

で、一応、データが取得できました。

下図が結果です。(R_Tempというデータベースのarduinoテーブルに保管されています。

Synology側からデータ取得するPythonのコード

Pythonのコードは以下の通りです。

# coding: utf-8
from urllib.request import urlopen
from bs4 import BeautifulSoup
import pymysql.cursors
import datetime
import time

#日付と時間のフォーマット
today0 = datetime.date.today()
today01 = today0.strftime('%Y%m%d')
Time0 = datetime.datetime.now()
Time01 = Time0.strftime('%H:%M:%S')

# Mysqlへ接続
database = pymysql.connect(user='ユーザー名',passwd='パスワード',host='ホスト名、Ipアドレスなど',port = ポート番号,db='データベース名',charset='utf8mb4',cursorclass=pymysql.cursors.DictCursor)

# arduinoサイトを開く
html = urlopen("http://192.168.1.56/")
bsObj = BeautifulSoup(html, "html.parser")

#urlオープンでの安定化時間
time.sleep(3)

# arduinoの表から値をもらう
Temp = bsObj.select('.value')[0].contents[0]
Humi = bsObj.select('.value')[1].contents[0]

# temp,humiの値が--だったら、測定値が出なかったら。
while Temp == "--":

    html.close()

    # arduinoサイトを開く
    html = urlopen("http://192.168.1.56/")
    bsObj = BeautifulSoup(html, "html.parser")
    #urlオープンでの安定化時間
    time.sleep(3)

    # arduinoの表から値をもらう
    Temp = bsObj.select('.value')[0].contents[0]
    Humi = bsObj.select('.value')[1].contents[0]


Temp01 = float(Temp)
Humi01 = float(Humi)

# INSERT INTO SQLクエリを作成する
query = "INSERT INTO arduino (Time, Date, S1_Temp, S1_Humi) VALUES (%s, %s, %s, %s)"

# データベースを1行ずつ走査するために使用されるカーソルを取得します。
cursor = database.cursor()

# 各行から値を割り当てる
values = (Time01, today01, Temp01, Humi01)

# SQLクエリを実行する
cursor.execute(query, values)

# カーソルを閉じる
cursor.close()

# トランザクションをコミットします
database.commit()

# データベース接続を閉じます
database.close()

# サイトクローズ
html.close()

Pythonのコードはホント、いつも短くて簡単で良かったです。

Synologyのタスクスケジューラーの内容も書いておきます。

export PATH="$PATH:/volume1/@appstore/py3k/usr/local/bin"
cd ./Document/R_Temp/01_Python
python3 R_Temp.py

これでPythonによって、データベースへのデータ保管が始まりました。

—————————————————————————————————–

追記です。

少々、困った問題を抱えております。

ESP32側なんですが、長時間計測していると、フリーズする(ハングアップする、固まる)。と言った症状に悩まされております。

で、色々と試した結果、Wifiが固まる(フリーズ、ハングアップ)ようです。

なので、Wifiが固まったら、一旦、Wifiを切って、再度、接続するように変更しました。

ESP32のコード

なので、そのコードを残しておきます。

#include <Arduino.h>
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <DHT.h>                    // DHTセンサー用

/* Function Prototype */
String getTemperature();
String getHumidity();
String processor(const String&);
void doInitialize();
void connectToWifi();

// ルーター接続情報
#define WIFI_SSID "Jinsinnolan"
#define WIFI_PASSWORD "asuka672"

/* 基本属性定義  */
#define SPI_SPEED   115200          // SPI通信速度
#define DHTTYPE     DHT11           // DHTセンサーの型式

// Webサーバーオブジェクト
#define HTTP_PORT 80
AsyncWebServer server(HTTP_PORT);

/* DHTセンサー*/
const int DHTPin = 16;                  // DHTセンサーの接続ピン
DHT   dht(DHTPin, DHTTYPE);             // DHTクラスの生成

/* HTMLページ */
const char* strHtml = R"rawliteral(
<!DOCTYPE HTML>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
      html { font-family: Helvetica; display: inline-block; margin: 0px auto;text-align: center;} 
      h1 {font-size:28px;}
      body {text-align: center;} 
      table { border-collapse: collapse; margin-left:auto; margin-right:auto;}
      th { padding: 12px; background-color: #0000cd; color: white; border: solid 2px #c0c0c0;}
      tr { border: solid 2px #c0c0c0; padding: 12px;}
      td { border: solid 2px #c0c0c0; padding: 12px;}
      .value { color:blue; font-weight: bold; padding: 1px;}
    </style>
  </head>
  <body>
    <h1>温度測定中</h1>
    <p style='color:brown; font-weight: bold'>計測値はアクセスが無いときは1時間ごとに自動更新</p>
    <p><table>
      <tr><th>要素</th><th>値</th><th>単位</th></tr>
      <tr><td>温度</td><td><span id="temperature" class="value">%TEMPERATURE%</span></td><td>℃</td>
      <tr><td>湿度</td><td><span id="humidity" class="value">%HUMIDITY%</span></td><td>%</td>
      </td></tr>
    </table></p>
  </body>
  <script>
    var getTemperature = function () {
      var xhr = new XMLHttpRequest();
      xhr.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
          document.getElementById("temperature").innerHTML = this.responseText;
        }
      };
      xhr.open("GET", "/temperature", true);
      xhr.send(null);
    }
    var getHumidity = function () {
      var xhr = new XMLHttpRequest();
      xhr.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
          document.getElementById("humidity").innerHTML = this.responseText;
        }
      };
      xhr.open("GET", "/humidity", true);
      xhr.send(null);
    }

    setInterval(getTemperature, 3600000);
    setInterval(getHumidity, 3600000);
  </script>
</html>)rawliteral";

void setup(){
    doInitialize();             // 初期化処理をして
    connectToWifi();            // Wi-Fiルーターに接続する

    // GETリクエストに対するハンドラーを登録して
    server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
        request->send_P(200, "text/html", strHtml, editPlaceHolder);
    });
    server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
        request->send_P(200, "text/plain", getTemperature().c_str());
    });
    server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest *request){
        request->send_P(200, "text/plain", getHumidity().c_str());
    });
    // サーバーを開始する
    server.begin();
}
 
/*
  * void loop(void)
  */
void loop(void) {
  // WiFi Lan の監視
  wifi_lan_check();
}

/* 計測処理ロジック */
String getTemperature() {
    float t = dht.readTemperature();
    if (isnan(t)) {    
        Serial.println("Failed to get temperature!");
        return "--";
    }
    else {
        Serial.println(t);
        return String(t) ;
    }
}

String getHumidity() {
    float h = dht.readHumidity();
    if (isnan(h)) {
        Serial.println("Failed to get humidity!");
        return "--";
    }
    else {
        Serial.println(h);
        return String(h) ;
    }
}

/* プレースホルダー処理 */
String editPlaceHolder(const String& var){
    if(var == "TEMPERATURE"){
        return getTemperature();
    }
    else if(var == "HUMIDITY"){
        return getHumidity();
    }
    return "??";
}

/* 初期化処理 */
void doInitialize() {
    Serial.begin(SPI_SPEED);
    dht.begin();                       // DHTセンサーを起動
}


/* Wi-Fiルーターに接続する */
void connectToWifi() {
    Serial.print("Connecting to Wi-Fi ");
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
    Serial.println("");
    // モニターにローカル IPアドレスを表示する
    Serial.println("WiFi connected.");
    Serial.print("  *IP address: ");
    Serial.println(WiFi.localIP());
    server.begin();
}

/*
 * void wifi_lan_check()
 */
void wifi_lan_check(){
  int i;
  if(WiFi.status() !=3){
  }
  if(WiFi.status() != WL_CONNECTED){
    WiFi.disconnect();
    delay(50);
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    i=50;
    while (WiFi.status() != WL_CONNECTED) {
      delay(250);
      i--;
      if(i<0){
        delay(250);
        ESP.restart();
      }
    }
  }
}

参考にさせて頂いたサイト↓

これでようやく連続でデータを取得できるようになってきたのかな。と思っています。

これを書いている時点では、稼働2日目でして、確証はありませんが、以前とは全然違ってしっかり動いている様子ではあります。

で、暑くなってきた(連日40℃越えの予報が出てきた)ので、グラフはとりあえず置いといて、次回からはエアコンのON、OFF編へ行きたいと思います。