こんばんは。
そろそろ就寝時間なのですが。。。スイッチが入ってしまいました。
主な機能の要約:以下前作との違いをボールドで強調しています。
①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ファイル次第で切り替えが可能となります。
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();
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);
}
}
<!--
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">°C</sup>
</p>
<p>
<span class="sensor-labels">Humidity</span>
<span id="humidity">%HUMIDITY%</span>
<sup class="units">%</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>
<!-- 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>
Under construction TBC
【参照記事】