Skip to content

Commit e9d555f

Browse files
authored
Merge pull request #269 from milesburton/feature/introduce-esp-webserver-example-and-update-version
feat: Add web server example for ESP devices, update version
2 parents 176087a + 5f779e5 commit e9d555f

File tree

5 files changed

+439
-3
lines changed

5 files changed

+439
-3
lines changed

DallasTemperature.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#ifndef DallasTemperature_h
22
#define DallasTemperature_h
33

4-
#define DALLASTEMPLIBVERSION "4.0.2"
4+
#define DALLASTEMPLIBVERSION "4.0.3"
55

66
// Configuration
77
#ifndef REQUIRESNEW
+375
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,375 @@
1+
#include <ESP8266WiFi.h>
2+
#include <ESP8266WebServer.h>
3+
#include <OneWire.h>
4+
#include <DallasTemperature.h>
5+
6+
/*
7+
SETUP INSTRUCTIONS
8+
9+
1) Change WiFi SSID and Password:
10+
const char* ssid = "YourSSID";
11+
const char* password = "YourPassword";
12+
13+
2) Polling Interval (milliseconds):
14+
const unsigned long READ_INTERVAL = 10000; // 10 seconds
15+
16+
3) Number of Readings (History Length):
17+
const int HISTORY_LENGTH = 360; // 1 hour at 10-second intervals
18+
*/
19+
20+
const char* ssid = "YourSSID";
21+
const char* password = "YourPassword";
22+
23+
const int oneWireBus = 4;
24+
const int MAX_SENSORS = 8;
25+
const int HISTORY_LENGTH = 360;
26+
const unsigned long READ_INTERVAL = 10000;
27+
28+
DeviceAddress sensorAddresses[MAX_SENSORS];
29+
float tempHistory[MAX_SENSORS][HISTORY_LENGTH];
30+
int historyIndex = 0;
31+
int numberOfDevices = 0;
32+
unsigned long lastReadTime = 0;
33+
34+
OneWire oneWire(oneWireBus);
35+
DallasTemperature sensors(&oneWire);
36+
ESP8266WebServer server(80);
37+
38+
String getAddressString(DeviceAddress deviceAddress);
39+
void handleRoot();
40+
void handleSensorList();
41+
void handleTemperature();
42+
void handleHistory();
43+
void updateHistory();
44+
45+
const char MAIN_page[] PROGMEM = R"=====(
46+
<!DOCTYPE html>
47+
<html lang="en">
48+
<head>
49+
<meta charset="UTF-8">
50+
<meta name="viewport"
51+
content="width=device-width, initial-scale=1.0">
52+
<title>Arduino Temperature Control Library - Sensor Data Graph</title>
53+
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
54+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css"
55+
rel="stylesheet">
56+
</head>
57+
<body class="bg-gray-100 font-sans min-h-screen flex flex-col">
58+
<div class="container mx-auto p-6 flex-grow">
59+
<h1 class="text-2xl font-semibold text-gray-800 mb-4">
60+
Arduino Temperature Control Library - Sensor Data
61+
</h1>
62+
63+
<div class="flex mb-6">
64+
<div class="cursor-pointer px-4 py-2 bg-blue-500 text-white rounded-lg shadow
65+
hover:bg-blue-400 active:scale-95"
66+
onclick="showTab('dashboard')">
67+
Dashboard
68+
</div>
69+
<div class="cursor-pointer px-4 py-2 bg-gray-200 rounded-lg shadow
70+
hover:bg-gray-300 active:scale-95 ml-4"
71+
onclick="showTab('api')">
72+
API Docs
73+
</div>
74+
<div class="cursor-pointer px-4 py-2 bg-gray-200 rounded-lg shadow
75+
hover:bg-gray-300 active:scale-95 ml-4"
76+
onclick="showTab('setup')">
77+
Setup
78+
</div>
79+
</div>
80+
81+
<div id="dashboard" class="tab-content">
82+
<button class="px-6 py-2 bg-green-500 text-white rounded-lg shadow
83+
hover:bg-green-400 active:scale-95"
84+
onclick="refreshData()">
85+
Refresh Data
86+
</button>
87+
<div id="sensors" class="mt-6">
88+
<div class="text-gray-600">Loading sensor data...</div>
89+
</div>
90+
</div>
91+
92+
<div id="api" class="tab-content hidden mt-8">
93+
<h2 class="text-xl font-semibold text-gray-800 mb-4">API</h2>
94+
<div class="bg-white p-6 rounded-lg shadow">
95+
<div class="mb-4">
96+
<span class="font-semibold text-blue-500">GET</span>
97+
<a href="/temperature" class="text-blue-500 hover:underline">/temperature</a>
98+
<pre class="bg-gray-100 p-4 rounded mt-2">
99+
{
100+
"sensors": [
101+
{
102+
"id": 0,
103+
"address": "28FF457D1234AB12",
104+
"celsius": 23.45,
105+
"fahrenheit": 74.21
106+
}
107+
]
108+
}
109+
</pre>
110+
</div>
111+
<div class="mb-4">
112+
<span class="font-semibold text-blue-500">GET</span>
113+
<a href="/sensors" class="text-blue-500 hover:underline">/sensors</a>
114+
<pre class="bg-gray-100 p-4 rounded mt-2">
115+
{
116+
"sensors": [
117+
{
118+
"id": 0,
119+
"address": "28FF457D1234AB12"
120+
}
121+
]
122+
}
123+
</pre>
124+
</div>
125+
<div>
126+
<span class="font-semibold text-blue-500">GET</span>
127+
<a href="/history" class="text-blue-500 hover:underline">/history</a>
128+
<pre class="bg-gray-100 p-4 rounded mt-2">
129+
{
130+
"interval_ms": 10000,
131+
"sensors": [
132+
{
133+
"id": 0,
134+
"address": "28FF457D1234AB12",
135+
"history": [23.45, 23.50, 23.48]
136+
}
137+
]
138+
}
139+
</pre>
140+
</div>
141+
</div>
142+
</div>
143+
144+
<div id="setup" class="tab-content hidden mt-8">
145+
<h2 class="text-xl font-semibold text-gray-800 mb-4">Setup Instructions</h2>
146+
<div class="bg-white p-6 rounded-lg shadow leading-relaxed">
147+
<p class="mb-4">
148+
Edit the .ino code to change SSID, password, read interval, and number
149+
of stored readings (HISTORY_LENGTH).
150+
</p>
151+
</div>
152+
</div>
153+
</div>
154+
155+
<footer class="bg-gray-800 text-white p-4 mt-auto text-center">
156+
<p>&copy; 2025 Miles Burton. All Rights Reserved.</p>
157+
<p>
158+
Licensed under the
159+
<a href="https://opensource.org/licenses/MIT" class="text-blue-400 hover:underline">
160+
MIT License
161+
</a>.
162+
</p>
163+
</footer>
164+
165+
<script>
166+
const showTab = (name) => {
167+
document.querySelectorAll('.tab-content').forEach(c => c.classList.add('hidden'));
168+
document.getElementById(name).classList.remove('hidden');
169+
};
170+
const buildSensorsHTML = (sensors) => {
171+
return sensors.map(s => {
172+
const chartId = "chart-" + s.id;
173+
return `
174+
<div class="bg-white p-6 rounded-lg shadow mb-6">
175+
<div class="text-lg font-semibold text-blue-500">
176+
${s.celsius.toFixed(2)}°C / ${s.fahrenheit.toFixed(2)}°F
177+
</div>
178+
<div class="text-sm text-gray-600 mt-2">
179+
Sensor ID: ${s.id} (${s.address})
180+
</div>
181+
<div class="mt-4" style="height: 300px;">
182+
<canvas id="${chartId}"></canvas>
183+
</div>
184+
</div>
185+
`;
186+
}).join('');
187+
};
188+
const drawChart = (chartId, dataPoints) => {
189+
const ctx = document.getElementById(chartId);
190+
if (!ctx) return;
191+
new Chart(ctx, {
192+
type: 'line',
193+
data: {
194+
labels: dataPoints.map(p => p.x),
195+
datasets: [{
196+
label: 'Temp (°C)',
197+
data: dataPoints,
198+
borderColor: 'red',
199+
backgroundColor: 'rgba(255,0,0,0.1)',
200+
borderWidth: 2,
201+
pointRadius: 3,
202+
lineTension: 0.1,
203+
fill: true
204+
}]
205+
},
206+
options: {
207+
responsive: true,
208+
maintainAspectRatio: false,
209+
plugins: {
210+
tooltip: {
211+
mode: 'index',
212+
intersect: false,
213+
callbacks: {
214+
label: (ctx) => {
215+
const t = ctx.parsed.x;
216+
const d = new Date(t);
217+
return `${ctx.dataset.label}: ${ctx.parsed.y.toFixed(2)}°C at ${d.toLocaleTimeString()}`;
218+
}
219+
}
220+
}
221+
},
222+
interaction: { mode: 'nearest', intersect: true },
223+
scales: {
224+
x: {
225+
type: 'linear',
226+
position: 'bottom',
227+
ticks: {
228+
autoSkip: true,
229+
maxTicksLimit: 10,
230+
callback: (v) => new Date(v).toLocaleTimeString()
231+
}
232+
},
233+
y: {
234+
grid: { color: 'rgba(0,0,0,0.05)' }
235+
}
236+
}
237+
}
238+
});
239+
};
240+
const refreshData = async () => {
241+
try {
242+
const sensorsDiv = document.getElementById('sensors');
243+
sensorsDiv.innerHTML = '<div class="text-gray-600">Loading sensor data...</div>';
244+
const td = await fetch('/temperature');
245+
const tempData = await td.json();
246+
const hd = await fetch('/history');
247+
const historyData = await hd.json();
248+
sensorsDiv.innerHTML = buildSensorsHTML(tempData.sensors);
249+
tempData.sensors.forEach(s => {
250+
const chartId = "chart-" + s.id;
251+
const sensorHist = historyData.sensors.find(h => h.id === s.id);
252+
if (!sensorHist) return;
253+
const total = sensorHist.history.length;
254+
const arr = sensorHist.history.map((v, i) => {
255+
return { x: Date.now() - (total - 1 - i)*10000, y: v };
256+
});
257+
drawChart(chartId, arr);
258+
});
259+
} catch(e) {
260+
console.error(e);
261+
document.getElementById('sensors').innerHTML =
262+
'<div class="text-gray-600">Error loading data.</div>';
263+
}
264+
};
265+
refreshData();
266+
setInterval(refreshData, 30000);
267+
</script>
268+
</body>
269+
</html>
270+
)=====";
271+
272+
void setup() {
273+
Serial.begin(115200);
274+
sensors.begin();
275+
276+
for (int i = 0; i < MAX_SENSORS; i++) {
277+
for (int j = 0; j < HISTORY_LENGTH; j++) {
278+
tempHistory[i][j] = 0;
279+
}
280+
}
281+
282+
numberOfDevices = sensors.getDeviceCount();
283+
if (numberOfDevices > MAX_SENSORS) {
284+
numberOfDevices = MAX_SENSORS;
285+
}
286+
287+
for (int i = 0; i < numberOfDevices; i++) {
288+
sensors.getAddress(sensorAddresses[i], i);
289+
}
290+
291+
sensors.setResolution(12);
292+
293+
WiFi.begin(ssid, password);
294+
while (WiFi.status() != WL_CONNECTED) {
295+
delay(500);
296+
}
297+
298+
server.on("/", HTTP_GET, handleRoot);
299+
server.on("/temperature", HTTP_GET, handleTemperature);
300+
server.on("/sensors", HTTP_GET, handleSensorList);
301+
server.on("/history", HTTP_GET, handleHistory);
302+
303+
server.begin();
304+
}
305+
306+
void loop() {
307+
server.handleClient();
308+
unsigned long t = millis();
309+
if (t - lastReadTime >= READ_INTERVAL) {
310+
updateHistory();
311+
lastReadTime = t;
312+
}
313+
}
314+
315+
void updateHistory() {
316+
sensors.requestTemperatures();
317+
for (int i = 0; i < numberOfDevices; i++) {
318+
float tempC = sensors.getTempC(sensorAddresses[i]);
319+
tempHistory[i][historyIndex] = tempC;
320+
}
321+
historyIndex = (historyIndex + 1) % HISTORY_LENGTH;
322+
}
323+
324+
void handleRoot() {
325+
server.send(200, "text/html", MAIN_page);
326+
}
327+
328+
void handleSensorList() {
329+
String json = "{\"sensors\":[";
330+
for (int i = 0; i < numberOfDevices; i++) {
331+
if (i > 0) json += ",";
332+
json += "{\"id\":" + String(i) + ",\"address\":\"" + getAddressString(sensorAddresses[i]) + "\"}";
333+
}
334+
json += "]}";
335+
server.send(200, "application/json", json);
336+
}
337+
338+
void handleTemperature() {
339+
sensors.requestTemperatures();
340+
String json = "{\"sensors\":[";
341+
for (int i = 0; i < numberOfDevices; i++) {
342+
if (i > 0) json += ",";
343+
float c = sensors.getTempC(sensorAddresses[i]);
344+
float f = sensors.toFahrenheit(c);
345+
json += "{\"id\":" + String(i) + ",\"address\":\"" + getAddressString(sensorAddresses[i]) + "\",";
346+
json += "\"celsius\":" + String(c) + ",\"fahrenheit\":" + String(f) + "}";
347+
}
348+
json += "]}";
349+
server.send(200, "application/json", json);
350+
}
351+
352+
void handleHistory() {
353+
String json = "{\"interval_ms\":" + String(READ_INTERVAL) + ",\"sensors\":[";
354+
for (int i = 0; i < numberOfDevices; i++) {
355+
if (i > 0) json += ",";
356+
json += "{\"id\":" + String(i) + ",\"address\":\"" + getAddressString(sensorAddresses[i]) + "\",\"history\":[";
357+
for (int j = 0; j < HISTORY_LENGTH; j++) {
358+
int idx = (historyIndex - j + HISTORY_LENGTH) % HISTORY_LENGTH;
359+
if (j > 0) json += ",";
360+
json += String(tempHistory[i][idx]);
361+
}
362+
json += "]}";
363+
}
364+
json += "]}";
365+
server.send(200, "application/json", json);
366+
}
367+
368+
String getAddressString(DeviceAddress deviceAddress) {
369+
String addr;
370+
for (uint8_t i = 0; i < 8; i++) {
371+
if (deviceAddress[i] < 16) addr += "0";
372+
addr += String(deviceAddress[i], HEX);
373+
}
374+
return addr;
375+
}

0 commit comments

Comments
 (0)