Logo Pico-Framework A web-first embedded framework for C++
Loading...
Searching...
No Matches
dashboard.h
Go to the documentation of this file.
1const char* dashboard_html = R"!(
2<!DOCTYPE html>
3<html lang="en">
4
5<head>
6 <meta charset="UTF-8">
7 <title>Pico GPIO Dashboard</title>
8 <meta name="viewport" content="width=device-width, initial-scale=1.0">
9 <style>
10 body {
11 font-family: 'Raleway', sans-serif;
12 background: #e0e0e0;
13 margin: 0;
14 padding: 2rem 1rem;
15 display: flex;
16 flex-direction: column;
17 align-items: center;
18 }
19
20 h1 {
21 color: #333;
22 margin-bottom: 1.5rem;
23 }
24
25 .gpio-banks {
26 display: flex;
27 flex-wrap: wrap;
28 justify-content: center;
29 gap: 2rem;
30 margin-bottom: 2.5rem;
31 width: 100%;
32 max-width: 900px;
33 }
34
35 .bank {
36 display: grid;
37 grid-template-columns: repeat(2, 1fr);
38 gap: 1rem;
39 background: #e0e0e0;
40 padding: 1rem;
41 border-radius: 20px;
42 box-shadow: 9px 9px 16px #bebebe,
43 -9px -9px 16px #ffffff;
44 width: 220px;
45 }
46
47 .gpio-card {
48 background: #e0e0e0;
49 border-radius: 16px;
50 padding: 1rem;
51 text-align: center;
52 box-shadow: inset 2px 2px 6px #bebebe,
53 inset -2px -2px 6px #ffffff;
54 }
55
56 .gpio-card button {
57 margin-top: 0.5rem;
58 padding: 0.5rem 1rem;
59 border: none;
60 border-radius: 12px;
61 font-weight: bold;
62 background: #f0f0f0;
63 box-shadow: 5px 5px 10px #bebebe,
64 -5px -5px 10px #ffffff;
65 cursor: pointer;
66 transition: background 0.2s;
67 }
68
69 .gpio-card button.active {
70 background-color: #a5d6a7;
71 }
72
73 .gauge-section {
74 display: flex;
75 flex-wrap: wrap;
76 justify-content: center;
77 gap: 2rem;
78 align-items: center;
79 }
80
81 .gauge-shell {
82 width: 220px;
83 height: 220px;
84 border-radius: 50%;
85 background: #e0e0e0;
86 box-shadow: 9px 9px 16px #bebebe,
87 -9px -9px 16px #ffffff,
88 inset 5px 5px 15px #bebebe,
89 inset -5px -5px 15px #ffffff;
90 display: flex;
91 align-items: center;
92 justify-content: center;
93 position: relative;
94 }
95
96 svg.gauge {
97 width: 160px;
98 height: 160px;
99 transform: rotate(-90deg);
100 }
101
102 .gauge-text {
103 position: absolute;
104 font-size: 1.6rem;
105 font-weight: bold;
106 color: #333;
107 }
108
109 .led-toggle {
110 background: #e0e0e0;
111 border-radius: 20px;
112 box-shadow: 9px 9px 16px #bebebe,
113 -9px -9px 16px #ffffff;
114 padding: 1rem 2rem;
115 text-align: center;
116 min-width: 160px;
117 }
118
119 .led-toggle button {
120 padding: 0.5rem 1rem;
121 border: none;
122 border-radius: 12px;
123 background: #f0f0f0;
124 font-weight: bold;
125 cursor: pointer;
126 margin-top: 1.5rem;
127 box-shadow: 5px 5px 10px #bebebe,
128 -5px -5px 10px #ffffff;
129 }
130
131 .led-toggle button.active {
132 background-color: #ffca28;
133 }
134
135 @media (max-width: 768px) {
136 .gpio-banks {
137 flex-direction: column;
138 align-items: center;
139 }
140
141 .gauge-section {
142 flex-direction: column;
143 }
144 }
145 </style>
146</head>
147
148<body>
149 <h1>Pico GPIO Dashboard</h1>
150
151 <div class="gpio-banks" id="gpioBanks">
152 <!-- JS adds GPIOs -->
153 </div>
154
155 <div class="gauge-section">
156 <div class="gauge-shell">
157 <svg class="gauge" viewBox="0 0 100 100">
158 <circle cx="50" cy="50" r="45" stroke="#ddd" stroke-width="10" fill="none" />
159 <circle id="gaugeArc" cx="50" cy="50" r="45" stroke="#81c784" stroke-width="10" fill="none"
160 stroke-linecap="round" stroke-dasharray="283" stroke-dashoffset="283" />
161 </svg>
162 <div class="gauge-text" id="tempValue">--°C</div>
163 </div>
164
165 <div class="led-toggle">
166 <div>Wi-Fi LED</div>
167 <button id="ledBtn" onclick="toggleLED()">OFF</button>
168 </div>
169 </div>
170
171 <script>
172 const gpioBanks = [
173 [2, 3, 4, 5],
174 [6, 7, 8, 9],
175 [16, 17, 18, 19]
176 ];
177
178 const gpioButtons = {}; // pin -> button
179
180 function createGpioCards() {
181 const container = document.getElementById("gpioBanks");
182
183 gpioBanks.forEach(bank => {
184 const bankDiv = document.createElement("div");
185 bankDiv.className = "bank";
186
187 bank.forEach(pin => {
188 const card = document.createElement("div");
189 card.className = "gpio-card";
190
191 const label = document.createElement("div");
192 label.textContent = `GPIO ${pin}`;
193
194 const btn = document.createElement("button");
195 btn.textContent = "OFF";
196 btn.onclick = () => toggleGPIO(pin, btn);
197
198 card.appendChild(label);
199 card.appendChild(btn);
200 bankDiv.appendChild(card);
201
202 gpioButtons[pin] = btn;
203 });
204
205 container.appendChild(bankDiv);
206 });
207 }
208
209 function toggleGPIO(pin, btn) {
210 const isActive = btn.classList.toggle("active");
211 const state = isActive ? 1 : 0;
212 btn.textContent = state ? "ON" : "OFF";
213 fetch(`/api/v1/gpio/${pin}/${state}`, { method: "POST" })
214 .catch(err => console.warn(`GPIO ${pin} toggle failed`, err));
215 }
216
217 function syncAllGpios() {
218 const pins = Object.keys(gpioButtons);
219
220 if (pins.length === 0) {
221 console.warn("No GPIO pins to sync");
222 return;
223 }
224
225 const params = new URLSearchParams();
226 pins.forEach(pin => params.append("pin", pin));
227
228 fetch(`/api/v1/gpios?${params.toString()}`)
229 .then(res => res.json())
230 .then(data => {
231 data.forEach(pinData => {
232 const pin = pinData.pin;
233 const isOn = pinData.state === 1;
234 const btn = gpioButtons[pin];
235 if (btn) {
236 btn.classList.toggle("active", isOn);
237 btn.textContent = isOn ? "ON" : "OFF";
238 }
239 });
240 })
241 .catch(err => console.warn("Failed to sync GPIOs", err));
242 }
243
244 function setupLedButton(ledBtn) {
245 function applyLedState(isOn) {
246 ledBtn.classList.toggle("active", isOn);
247 ledBtn.textContent = isOn ? "ON" : "OFF";
248 }
249
250 ledBtn.onclick = () => {
251 const isOn = !ledBtn.classList.contains("active");
252 applyLedState(isOn);
253 fetch(`/api/v1/led/${isOn ? 1 : 0}`, { method: "POST" })
254 .catch(err => console.warn("LED toggle failed", err));
255 };
256
257 fetch('/api/v1/led')
258 .then(res => res.json())
259 .then(data => {
260 const isOn = data.state === 1 || data.state === "on";
261 applyLedState(isOn);
262 })
263 .catch(err => console.warn("LED sync failed", err));
264 }
265
266 function setupTemperatureGauge(arc, tempValue) {
267 function update(temp) {
268 tempValue.textContent = `${temp.toFixed(1)}°C`;
269 const percent = Math.min(Math.max(temp / 60, 0), 1);
270 arc.style.strokeDashoffset = 283 * (1 - percent);
271 arc.style.stroke =
272 temp < 50 ? "#81c784" :
273 temp < 65 ? "#ffb74d" :
274 "#e57373";
275 }
276
277 function refresh() {
278 fetch('/api/v1/temperature')
279 .then(res => res.json())
280 .then(data => update(data.temperature))
281 .catch(err => {
282 console.warn("Temp fetch failed", err);
283 tempValue.textContent = "--°C";
284 arc.style.strokeDashoffset = 283;
285 arc.style.stroke = "#ccc";
286 });
287 }
288
289 refresh(); // initial
290 setInterval(refresh, 10000); // every 10s
291 }
292
293 // ----- Full load: all layout and rendering done -----
294 window.addEventListener("load", () => {
295 createGpioCards();
296 syncAllGpios();
297
298 const ledBtn = document.getElementById("ledBtn");
299 setupLedButton(ledBtn);
300
301 const arc = document.getElementById("gaugeArc");
302 const tempValue = document.getElementById("tempValue");
303 setupTemperatureGauge(arc, tempValue);
304 });
305 </script>
306</body>
307</html>
308)!";
const char * dashboard_html
Definition dashboard.h:1