こんばんは。

そろそろ就寝時間なのですが。。。スイッチが入ってしまいました。てへぺろ

の続編になります。前回は、バイナリーのオブジェクト(F/W)を2種類準備して、それぞれ用に準備したコスメティック(HTML)を切り替えました。
今回は、オブジェクトは一つで、参照するHTMLファイルを差し替えることで、そのコスメティックを切り替える様にしてみました。

主な機能の要約:以下前作との違いをボールドで強調しています。

 

①IoTデバイス(ESP12-F)のF/Wを、Pull型AsyncElegantOTAでアップデートする仕組み

  デバイス側でPullするので、認証機能はありません。

②①のF/Wでは、パソコン上のブラウザを使ってIoTデバイス上のLED1を遠隔でON/OFFできます。

③①のF/Wでは、IoTデバイス上のセンサー(BME280)で取得した、温度、湿度、気圧の情報が、パソコン上のブラウザにリアルタイム表示できます。

オブジェクトが参照するHTMLを差し替えることで、パソコンのブラウザに表示される画面(HTMLファイル)を切り替えることができます。

  2つの画面を準備しました。

 ②および③を実現する画面 右差し その1

 ③で取得出来たデータを統計的なグラフで表示する画面 右差し その2

⑤④の各画面データ(HTMLファイル)は、マイコンチップ上のフラッシュメモリエリアに事前にダウンロードします。

 この時のフラッシュメモリ上のファイルシステムは、LittleFSを採用

 サンプルコードでは、間接的にそれらのファイルを参照(ポイントする:C言語のポインターと同じ考え方)しています。

 ブラウザに表示される画面は、参照先のHTMLファイル次第で切り替えが可能となります。

 faviconにも対応しています。左差し  

⑥おまけの機能
 温度センサーで検知する値が一定値以上の場合に、Prototype Shiledのプッシュボタンを押下するとLED2が点灯する。
 
ターゲットデバイスの回路図下差し
早速ソースコードを上げます。
/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
 ----------------------------------------------------------------------------
  https://randomnerdtutorials.com/esp8266-web-server-spiffs-nodemcu/
  https://randomnerdtutorials.com/install-esp8266-nodemcu-littlefs-arduino/
  https://randomnerdtutorials.com/esp8266-nodemcu-ota-over-the-air-arduino/
  本ソフトウェアおよび関連文書のファイルの複製を取得するすべての人に対して、無償で許可します。
  上記の著作権表示および本許諾表示を、ソフトウェアのすべての複製または重要な部分に記載するものとします。

  AsyncElegantOTA Version
  Modified for ESP-12F w/Prototype Shield
  Last update 2021/5/12
  MITライセンスに準拠する形でご利用願います。 by BRK
*/

// Import required libraries
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Wire.h>

#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

// https://github.com/esp8266/Arduino/tree/master/libraries/LittleFS
#include "LittleFS.h"

#include <AsyncElegantOTA.h>

#define TH 20

// For ESP8266 ESP-12F
// BME280 I2C        ESP-12F
// SCL ( I2c) 3    SCL    GPIO 5
// SDA ( I2c) 4    SDA    GPIO 4
// CSB (SPI)  5    Open
// SDO (SPI)  6 → 3.3V I2C address 0x77 or G I2C address 0x76 → Open
//
// Create a sensor object
Adafruit_BME280 bme;            // BME280 connect to ESP-12F I2C (GPIO 4 = SDA, GPIO 5 = SCL)

// Replace with your network credentials
const char* ssid = "SSID";
const char* password = "PASSWORD";

// Set LED1 GPIO
const int led1Pin = 3;          // LED1 on Prototype Shield
// Stores LED1 state
String led1State;

// constants won't change. They're used here to set pin numbers:
const int buttonPin = 0;        // the number of the pushbutton pin
const int led2Pin =  16;        // the number of the LED2 pin
// variables will change:
int buttonState = HIGH;         // variable for reading the pushbutton status

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

String getTemperature() {
  float temperature = bme.readTemperature();
  // Read temperature as Fahrenheit (isFahrenheit = true)
  //float t = dht.readTemperature(true);
  Serial.printf("%.2f ºC

", temperature);
  return String(temperature);
}
  
String getHumidity() {
  float humidity = bme.readHumidity();
  Serial.printf("%.2f %%

", humidity);
  return String(humidity);
}

String getPressure() {
  float pressure = bme.readPressure()/ 100.0F;
  Serial.printf("%.2f hPa

", pressure);
  return String(pressure);
}

// Replaces placeholder with LED state value
String processor(const String& var){
  Serial.println(var);
  if(var == "STATE"){
    if(digitalRead(led1Pin)){
      led1State = "ON";
    }
    else{
      led1State = "OFF";
    }
    Serial.print(led1State);
    Serial.println();
    return led1State;
  }
  else if (var == "TEMPERATURE"){
    return getTemperature();
  }
  else if (var == "HUMIDITY"){
    return getHumidity();
  }
  else if (var == "PRESSURE"){
    return getPressure();
  }  
}

// Initialize WiFi
void initWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println(WiFi.localIP());
}

// Initialize LittleFS
void initLittleFS() {
  if (!LittleFS.begin()) {
    Serial.println("

An error has occurred while mounting LittleFS");
  }
  Serial.println("

LittleFS mounted successfully");
}

// Init BME280 for I2C
void initBME(){
  Wire.begin();
  if (!bme.begin(0x76, &Wire)) {        // SDO:L for I2C
    Serial.println("Could not find a valid BME280 sensor through I2C, check wiring or power off/on!");
    while (1);
  }
}

// Set number of outputs
#define NUM_OUTPUTS  4                                          //

// Assign each GPIO to an output
int outputGPIOs[NUM_OUTPUTS] = {2, 14, led1Pin, led2Pin};       //

void initGPIO() {
  digitalWrite(2, HIGH);
  digitalWrite(14, LOW);
  digitalWrite(led1Pin, LOW);
  digitalWrite(led2Pin, LOW);
}

void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);

  // Set GPIOs as outputs
  for (int i =0; i<NUM_OUTPUTS; i++)
    pinMode(outputGPIOs[i], OUTPUT);
  initGPIO();
  
  // Initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);

  // Initialize LittleFS
  initLittleFS();

  // Connect to Wi-Fi
  initWiFi();

  // Initialize the sensor
  initBME();
  
  server.serveStatic("/", LittleFS, "/");

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(LittleFS, "/index.html", String(), false, processor);
  });
  
  // Route to load style.css file
  server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(LittleFS, "/style.css", "text/css");
  });

  // Route to set GPIO to HIGH
  server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request){
    digitalWrite(led1Pin, HIGH);    
    request->send(LittleFS, "/index.html", String(), false, processor);
  });
  
  // Route to set GPIO to LOW
  server.on("/off", HTTP_GET, [](AsyncWebServerRequest *request){
    digitalWrite(led1Pin, LOW);    
    request->send(LittleFS, "/index.html", String(), false, processor);
  });

  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.on("/pressure", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", getPressure().c_str());
  });

  AsyncElegantOTA.begin(&server);    // Start ElegantOTA
  
  // Start server
  server.begin();
}
 
void loop(){
  AsyncElegantOTA.loop();

  if (bme.readTemperature() >= TH) {
    // read the state of the pushbutton value:
    buttonState = digitalRead(buttonPin);
    // check if the pushbutton is pressed. If it is, the buttonState is LOW:
    if (buttonState == LOW) {
      // turn LED2 on:
      digitalWrite(led2Pin, HIGH);
      Serial.printf("Temperature is equal to %.2f ºC.

", bme.readTemperature());
    } else
      // turn LED2 off:
      digitalWrite(led2Pin, LOW);
  }
}
先ずは、ここまで更に、
 
上記②のindex.html
<!DOCTYPE html>
<!-- 
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com  
-->
<html>
<head>
  <title>ESP8266 Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon"  type="image/png" href="favicon.png">
  <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
  <h1>ESP8266 Web Server</h1>
  <p>GPIO 3 state<strong> %STATE%</strong></p>
  <p>
    <a href="/on"><button class="button">ON</button></a>
    <a href="/off"><button class="button button2">OFF</button></a>
  </p>
  <p>
    <span class="sensor-labels">Temperature</span>
    <span id="temperature">%TEMPERATURE%</span>
    <sup class="units">&deg;C</sup>
  </p>
  <p>
    <span class="sensor-labels">Humidity</span>
    <span id="humidity">%HUMIDITY%</span>
    <sup class="units">&#37;</sup>
  </p>
  <p>
    <span class="sensor-labels">Pressure</span>
    <span id="pressure">%PRESSURE%</span>
    <sup class="units">hPa</sup>
  </p>
</body>
<script>
  setInterval(function ( ) {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
        document.getElementById("temperature").innerHTML = this.responseText;
      }
    };
    xhttp.open("GET", "/temperature", true);
    xhttp.send();
  }, 10000 ) ;

  setInterval(function ( ) {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
        document.getElementById("humidity").innerHTML = this.responseText;
      }
    };
    xhttp.open("GET", "/humidity", true);
    xhttp.send();
  }, 10000 ) ;

  setInterval(function ( ) {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
        document.getElementById("pressure").innerHTML = this.responseText;
      }
    };
    xhttp.open("GET", "/pressure", true);
    xhttp.send();
  }, 10000 ) ;
</script>
</html>
 
上記③のindex.html
<!DOCTYPE HTML><html>
<!-- Rui Santos - Complete project details at https://RandomNerdTutorials.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. -->
<head>
  <title>ESP Weather Station</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon"  type="image/png" href="favicon.png">
  <link rel="stylesheet" type="text/css" href="style.css">
  <script src="https://code.highcharts.com/highcharts.js"></script>
  <style>
    body {
      min-width: 310px;
        max-width: 800px;
        height: 400px;
      margin: 0 auto;
    }
    h2 {
      font-family: Arial;
      font-size: 2.5rem;
      text-align: center;
    }
  </style>
</head>
<body>
  <h2>ESP Weather Station</h2>
  <div id="chart-temperature" class="container"></div>
  <div id="chart-humidity" class="container"></div>
  <div id="chart-pressure" class="container"></div>
</body>
<script>
var chartT = new Highcharts.Chart({
  chart:{ renderTo : 'chart-temperature' },
  title: { text: 'BME280 Temperature' },
  series: [{
    showInLegend: false,
    data: []
  }],
  plotOptions: {
    line: { animation: false,
      dataLabels: { enabled: true }
    },
    series: { color: '#059e8a' }
  },
  xAxis: { type: 'datetime',
    dateTimeLabelFormats: { second: '%H:%M:%S' }
  },
  yAxis: {
    title: { text: 'Temperature (Celsius)' }
    //title: { text: 'Temperature (Fahrenheit)' }
  },
  credits: { enabled: false }
});
setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var x = (new Date()).getTime(),
          y = parseFloat(this.responseText);
      //console.log(this.responseText);
      if(chartT.series[0].data.length > 40) {
        chartT.series[0].addPoint([x, y], true, true, true);
      } else {
        chartT.series[0].addPoint([x, y], true, false, true);
      }
    }
  };
  xhttp.open("GET", "/temperature", true);
  xhttp.send();
}, 30000 ) ;

var chartH = new Highcharts.Chart({
  chart:{ renderTo:'chart-humidity' },
  title: { text: 'BME280 Humidity' },
  series: [{
    showInLegend: false,
    data: []
  }],
  plotOptions: {
    line: { animation: false,
      dataLabels: { enabled: true }
    }
  },
  xAxis: {
    type: 'datetime',
    dateTimeLabelFormats: { second: '%H:%M:%S' }
  },
  yAxis: {
    title: { text: 'Humidity (Percent)' }
  },
  credits: { enabled: false }
});
setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var x = (new Date()).getTime(),
          y = parseFloat(this.responseText);
      //console.log(this.responseText);
      if(chartH.series[0].data.length > 40) {
        chartH.series[0].addPoint([x, y], true, true, true);
      } else {
        chartH.series[0].addPoint([x, y], true, false, true);
      }
    }
  };
  xhttp.open("GET", "/humidity", true);
  xhttp.send();
}, 30000 ) ;

var chartP = new Highcharts.Chart({
  chart:{ renderTo:'chart-pressure' },
  title: { text: 'BME280 Pressure' },
  series: [{
    showInLegend: false,
    data: []
  }],
  plotOptions: {
    line: { animation: false,
      dataLabels: { enabled: true }
    },
    series: { color: '#18009c' }
  },
  xAxis: {
    type: 'datetime',
    dateTimeLabelFormats: { second: '%H:%M:%S' }
  },
  yAxis: {
    title: { text: 'Pressure (hPa)' }
  },
  credits: { enabled: false }
});
setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var x = (new Date()).getTime(),
          y = parseFloat(this.responseText);
      //console.log(this.responseText);
      if(chartP.series[0].data.length > 40) {
        chartP.series[0].addPoint([x, y], true, true, true);
      } else {
        chartP.series[0].addPoint([x, y], true, false, true);
      }
    }
  };
  xhttp.open("GET", "/pressure", true);
  xhttp.send();
}, 30000 ) ;
</script>
</html>
 

OK Under construction TBC

【参照記事】