Upload files to "/"

This commit is contained in:
2026-08-09 08:56:34 +00:00
parent 84913cc8a3
commit 19c65aa151
+290
View File
@@ -0,0 +1,290 @@
#include <WiFi.h>
#include <WebServer.h>
#include <DHT.h>
// ----------- WiFi -----------
static const char* WIFI_SSID = "ssid";
static const char* WIFI_PASS = "password";
// ----------- DHT -----------
static const int DHT_PIN = 10; // GPIO for DHT DATA
static const int DHT_TYPE = DHT11;
DHT dht(DHT_PIN, DHT_TYPE);
// ----------- Server -----------
WebServer server(80);
// ----------- Reading interval -----------
static const unsigned long READ_MS = 2000;
unsigned long lastRead = 0;
// ----------- Latest values -----------
float latestH = NAN;
float latestT_F = NAN;
String htmlPage() {
return R"HTML(
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>ESP32-C3 DHT11</title>
<style>
body { font-family: system-ui, Arial; margin: 2rem; }
.card { padding: 1rem 1.2rem; border: 1px solid #ddd; border-radius: 12px; max-width: 720px; }
.val { font-size: 2rem; margin: 0.2rem 0; }
.small { color: #555; font-size: 0.9rem; }
.row { display: flex; gap: 2rem; flex-wrap: wrap; }
.box { flex: 1; min-width: 240px; }
canvas { width: 100%; height: 260px; background: #fafafa; border: 1px solid #eee; border-radius: 10px; }
.legend { display: flex; gap: 1rem; align-items: center; margin-top: 0.5rem; }
.dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; margin-right: 0.35rem; }
.btnbar { margin-top: 0.8rem; display: flex; gap: 0.6rem; flex-wrap: wrap; }
button { padding: 0.5rem 0.8rem; border-radius: 10px; border: 1px solid #ddd; background: #fff; cursor: pointer; }
</style>
</head>
<body>
<div class="card">
<h2>ESP32 DHT</h2>
<div class="small">Updates every 2 seconds • Last ~60 samples</div>
<div class="row">
<div class="box">
<p><b>Temperature:</b></p>
<p class="val" id="temp">--</p>
</div>
<div class="box">
<p><b>Humidity:</b></p>
<p class="val" id="hum">--</p>
</div>
</div>
<canvas id="chart" width="680" height="260"></canvas>
<div class="legend">
<div><span class="dot" style="background:#e53935"></span>Temp (°F)</div>
<div><span class="dot" style="background:#1e88e5"></span>Humidity (%)</div>
</div>
<div class="btnbar">
<button onclick="clearData()">Clear chart</button>
</div>
</div>
<script>
const canvas = document.getElementById('chart');
const ctx = canvas.getContext('2d');
// Keep separate series; chart shares the x-axis (time)
const MAX_POINTS = 60;
let tempSeries = []; // {x, v}
let humSeries = []; // {x, v}
function resizeForDPR() {
const dpr = window.devicePixelRatio || 1;
const rect = canvas.getBoundingClientRect();
canvas.width = Math.round(rect.width * dpr);
canvas.height = Math.round(rect.height * dpr);
ctx.setTransform(dpr, 0, 0, dpr, 0, 0); // draw in CSS pixels
}
window.addEventListener('resize', resizeForDPR);
resizeForDPR();
function draw() {
const w = canvas.getBoundingClientRect().width;
const h = canvas.getBoundingClientRect().height;
ctx.clearRect(0, 0, w, h);
// Background grid
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, w, h);
ctx.strokeStyle = '#eee';
ctx.lineWidth = 1;
const gridLines = 5;
for (let i = 1; i < gridLines; i++) {
const y = (h * i) / gridLines;
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(w, y);
ctx.stroke();
}
// If no data, stop
if (tempSeries.length === 0 && humSeries.length === 0) return;
// Compute ranges (ignore missing nulls)
const allTemp = tempSeries.map(p => p.v).filter(v => Number.isFinite(v));
const allHum = humSeries.map(p => p.v).filter(v => Number.isFinite(v));
const tempMin = allTemp.length ? Math.min(...allTemp) : 0;
const tempMax = allTemp.length ? Math.max(...allTemp) : 1;
const humMin = allHum.length ? Math.min(...allHum) : 0;
const humMax = allHum.length ? Math.max(...allHum) : 1;
// Avoid flat-line divisions
const tempSpan = (tempMax - tempMin) || 1;
const humSpan = (humMax - humMin) || 1;
// x scale: index across MAX_POINTS window
const count = Math.max(tempSeries.length, humSeries.length);
const xForIndex = (i) => {
if (count <= 1) return 0;
return (w * i) / (count - 1);
};
function plot(series, color, min, span) {
ctx.strokeStyle = color;
ctx.lineWidth = 2;
ctx.beginPath();
for (let i = 0; i < series.length; i++) {
const v = series[i].v;
if (!Number.isFinite(v)) continue;
const x = xForIndex(i);
const y = h - ((v - min) / span) * (h - 20) - 10; // padding
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.stroke();
}
// Draw both series
// (We scale each series to its own min/max so you always get a visible line.)
plot(tempSeries, '#e53935', tempMin, tempSpan);
plot(humSeries, '#1e88e5', humMin, humSpan);
// Labels (min/max)
ctx.fillStyle = '#333';
ctx.font = '12px system-ui, Arial';
ctx.fillText(`Temp range: ${tempMin.toFixed(1)}${tempMax.toFixed(1)} °F`, 10, 16);
ctx.fillText(`Hum range: ${humMin.toFixed(1)}${humMax.toFixed(1)} %`, 10, 32);
}
function pushPoint(series, value) {
if (Number.isFinite(value)) {
series.push(value);
while (series.length > MAX_POINTS) series.shift();
} else {
// Keep time alignment by pushing NaN placeholder only if you want strict alignment.
// Here we just skip.
}
}
function clearData() {
tempSeries = [];
humSeries = [];
draw();
}
async function refresh() {
try {
const res = await fetch('/data?nocache=' + Date.now());
const j = await res.json();
// Update numbers
if (j.t_f === null) document.getElementById('temp').textContent = '--';
else document.getElementById('temp').textContent = j.t_f.toFixed(1) + ' °F';
if (j.h === null) document.getElementById('hum').textContent = '--';
else document.getElementById('hum').textContent = j.h.toFixed(1) + ' %';
// Update series + chart
if (j.t_f !== null) tempSeries.push({x: Date.now(), v: j.t_f});
if (j.h !== null) humSeries.push({x: Date.now(), v: j.h});
while (tempSeries.length > MAX_POINTS) tempSeries.shift();
while (humSeries.length > MAX_POINTS) humSeries.shift();
draw();
} catch (e) {
// ignore fetch errors
}
}
refresh();
setInterval(refresh, 2000);
</script>
</body>
</html>
)HTML";
}
void handleRoot() {
server.send(200, "text/html", htmlPage());
}
void handleData() {
// Convert NaN to null for easy JSON handling
float h = latestH;
float t = latestT_F;
String json = "{";
json += "\"h\": " + (isnan(h) ? String("null") : String(h, 1)) + ",";
json += "\"t_f\": " + (isnan(t) ? String("null") : String(t, 1));
json += "}";
server.send(200, "application/json", json);
}
void setup() {
Serial.begin(115200);
delay(200);
Serial.println("ESP32-C3 DHT11 starting...");
dht.begin();
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(300);
Serial.print(".");
}
Serial.println();
Serial.print("Connected. IP address: ");
Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.on("/data", handleData);
server.begin();
Serial.println("HTTP server started.");
}
void loop() {
// keep server responsive
server.handleClient();
unsigned long now = millis();
if (now - lastRead < READ_MS) return;
lastRead = now;
float h = dht.readHumidity();
float tC = dht.readTemperature(); // Celsius
if (isnan(h) || isnan(tC)) {
Serial.println("DHT read failed (check wiring/pull-up).");
latestH = NAN;
latestT_F = NAN;
return;
}
latestH = h;
latestT_F = dht.convertCtoF(tC);
Serial.print("Humidity: ");
Serial.print(latestH, 1);
Serial.print(" %\tTemp: ");
Serial.print(latestT_F, 1);
Serial.println(" F");
Serial.print("Connected. IP address: ");
Serial.println(WiFi.localIP());
}