Skip to content

Commit 1b29b12

Browse files
committed
Embed website, add LED breathing, tweak thresholds
Embed generated website assets into firmware and prefer PROGMEM pages over SPIFFS; add helpers to serve PROGMEM strings and update root handler. Implement LED breathing brightness (sinusoidal pulse) and adjust LED behavior per status. Lower alignment/intensity thresholds in the Justage page and JS logic (Optimal: 50000->4000, Gut: 20000->1500). Add extra startup/version logging, handle captive-portal URI variants (/generate_204*), and update build/version metadata. Minor README esptool port adjustment and regenerated HTML header files.
1 parent 83740a3 commit 1b29b12

8 files changed

Lines changed: 104 additions & 38 deletions

File tree

Production_Files/Software/ODMR_Server/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,9 +400,10 @@ cd /Users/bene/Dropbox/Dokumente/Promotion/PROJECTS/TechnicalDocs-openUC2-QBox/P
400400
# Output: build/fw-images/seeed_xiao_esp32c3.bin → direkt an 0x0 flashen
401401
/Users/bene/.platformio/penv/bin/pio run -e seeed_xiao_esp32c3 -t upload_merged --upload-port /dev/cu.usbmodem101
402402

403+
# Rather use this: (same as webserial)
403404
/Users/bene/.platformio/penv/bin/python -m esptool \
404405
--chip esp32c3 \
405-
-p /dev/cu.usbmodem101 \
406+
-p /dev/cu.usbmodem1101 \
406407
-b 460800 \
407408
write-flash \
408409
--flash-mode keep \
Binary file not shown.

Production_Files/Software/ODMR_Server/data/justage.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ <h5>Messbereich</h5>
104104
<div class="card-body">
105105
<p><strong>Minimal:</strong> 0</p>
106106
<p><strong>Maximal:</strong> 65535</p>
107-
<p><strong>Optimal:</strong> > 50000</p>
107+
<p><strong>Optimal:</strong> > 4000</p>
108108
</div>
109109
</div>
110110

@@ -238,11 +238,11 @@ <h5>Sensor-Einstellungen</h5>
238238
intensityBar.setAttribute('aria-valuenow', percentage);
239239

240240
// Update status based on intensity
241-
if (intensity > 50000) {
241+
if (intensity > 4000) {
242242
alignmentStatus.textContent = 'Optimal';
243243
alignmentStatus.className = 'badge bg-success';
244244
intensityBar.className = 'progress-bar bg-success';
245-
} else if (intensity > 20000) {
245+
} else if (intensity > 1500) {
246246
alignmentStatus.textContent = 'Gut';
247247
alignmentStatus.className = 'badge bg-warning';
248248
intensityBar.className = 'progress-bar bg-warning';

Production_Files/Software/ODMR_Server/src/main.cpp

Lines changed: 89 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@
1212
// Version info (auto-generated)
1313
#include "version_info.h"
1414

15+
// Embedded website files (auto-generated by build_website.py)
16+
#include "website/index_html.h"
17+
#include "website/messung_html.h"
18+
#include "website/justage_html.h"
19+
#include "website/ratio_html.h"
20+
#include "website/infos_html.h"
21+
#include "website/style_css.h"
22+
1523
#define ADF_FREQ_MIN 2200.0f // Min frequency for ADF4351 (2.2 GHz)
1624
#define ADF_FREQ_MAX 4400.0f // Max frequency for ADF4351 (4.4 GHz)
1725

@@ -125,20 +133,25 @@ void updateLEDs()
125133
return;
126134
lastLEDUpdate = millis();
127135

128-
strip.setBrightness(100); // Set to 50% brightness
136+
// Breathing brightness: sinusoidal pulse between ~20 and 100 over ~3 seconds
137+
float breathPhase = (float)(millis() % 3000) / 3000.0f; // 0..1 over 3 s
138+
float breathVal = (sin(breathPhase * 2.0f * PI) + 1.0f) / 2.0f; // 0..1
139+
uint8_t breathBri = (uint8_t)(20 + breathVal * 80); // range 20..100
129140

130141
switch (currentLEDStatus)
131142
{
132143
case LED_NO_CLIENT:
133-
// White color for no client connected
144+
// White breathing for no client connected
145+
strip.setBrightness(breathBri);
134146
for (int i = 0; i < strip.numPixels(); i++)
135147
{
136-
strip.setPixelColor(i, strip.Color(100, 100, 100)); // TODO: Make it glowing?
148+
strip.setPixelColor(i, strip.Color(100, 100, 100));
137149
}
138150
break;
139151

140152
case LED_CONNECTED:
141153
// Rainbow effect when client connected but idle
154+
strip.setBrightness(100);
142155
for (int i = 0; i < strip.numPixels(); i++)
143156
{
144157
int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
@@ -150,25 +163,69 @@ void updateLEDs()
150163
break;
151164

152165
case LED_MEASURING:
153-
// Red color when measuring frequency sweep
166+
// Red breathing when measuring frequency sweep
167+
strip.setBrightness(breathBri);
154168
for (int i = 0; i < strip.numPixels(); i++)
155169
{
156-
strip.setPixelColor(i, strip.Color(100, 0, 0)); // TODO: Make it glowing?
170+
strip.setPixelColor(i, strip.Color(100, 0, 0));
157171
}
158172
break;
159173

160174
case LED_INTENSITY:
161-
// Blue color when monitoring intensity for alignment
175+
// Blue breathing when monitoring intensity for alignment
176+
strip.setBrightness(breathBri);
162177
for (int i = 0; i < strip.numPixels(); i++)
163178
{
164-
strip.setPixelColor(i, strip.Color(0, 0, 100)); // TODO: Make it glowing?
179+
strip.setPixelColor(i, strip.Color(0, 0, 100));
165180
}
166181
break;
167182
}
168183

169184
strip.show();
170185
}
171186

187+
// Utility to serve a PROGMEM string with the given content type
188+
void servePROGMEM(const char *content, const char *contentType)
189+
{
190+
server.send_P(200, contentType, content);
191+
}
192+
193+
// Try embedded PROGMEM pages first; return true if handled.
194+
bool serveEmbeddedPage(const String &path)
195+
{
196+
if (path == "/index.html" || path == "/")
197+
{
198+
servePROGMEM(INDEX_HTML, "text/html");
199+
return true;
200+
}
201+
if (path == "/messung.html")
202+
{
203+
servePROGMEM(MESSUNG_HTML, "text/html");
204+
return true;
205+
}
206+
if (path == "/justage.html")
207+
{
208+
servePROGMEM(JUSTAGE_HTML, "text/html");
209+
return true;
210+
}
211+
if (path == "/ratio.html")
212+
{
213+
servePROGMEM(RATIO_HTML, "text/html");
214+
return true;
215+
}
216+
if (path == "/infos.html")
217+
{
218+
servePROGMEM(INFOS_HTML, "text/html");
219+
return true;
220+
}
221+
if (path == "/style.css")
222+
{
223+
servePROGMEM(STYLE_CSS, "text/css");
224+
return true;
225+
}
226+
return false;
227+
}
228+
172229
// Utility to serve static files from header files
173230
// Determine MIME content type from file extension
174231
String getContentType(const String &path)
@@ -183,7 +240,7 @@ String getContentType(const String &path)
183240
return "text/plain";
184241
}
185242

186-
// Serve files from SPIFFS, fall back to index.html for SPA navigation
243+
// Serve files: try embedded PROGMEM first, then SPIFFS fallback
187244
void handleFileRequest(const String &path)
188245
{
189246
String actualPath = path;
@@ -197,7 +254,11 @@ void handleFileRequest(const String &path)
197254
actualPath = "/" + actualPath;
198255
}
199256

200-
// Try to serve from SPIFFS
257+
// 1) Try serving from embedded PROGMEM (fast, no flash read)
258+
if (serveEmbeddedPage(actualPath))
259+
return;
260+
261+
// 2) Try to serve from SPIFFS (images, bootstrap, etc.)
201262
if (SPIFFS.exists(actualPath))
202263
{
203264
File file = SPIFFS.open(actualPath, "r");
@@ -210,22 +271,13 @@ void handleFileRequest(const String &path)
210271
}
211272
}
212273

213-
// File not found — for HTML navigation requests fall back to index.html
274+
// File not found — for HTML navigation requests fall back to embedded index
214275
String accept = server.header("Accept");
215276
if (actualPath.endsWith(".html") || accept.indexOf("text/html") >= 0)
216277
{
217-
Serial.print("Unknown HTML path -> index: ");
278+
Serial.print("Unknown HTML path -> index (PROGMEM): ");
218279
Serial.println(actualPath);
219-
if (SPIFFS.exists("/index.html"))
220-
{
221-
File idx = SPIFFS.open("/index.html", "r");
222-
server.streamFile(idx, "text/html");
223-
idx.close();
224-
}
225-
else
226-
{
227-
server.send(200, "text/html", "<h1>ODMR Server</h1><p>SPIFFS not mounted or index.html missing.</p>");
228-
}
280+
servePROGMEM(INDEX_HTML, "text/html");
229281
}
230282
else
231283
{
@@ -743,6 +795,11 @@ void setup()
743795
Serial.begin(115200);
744796
delay(1500); // Allow time to connect
745797
Serial.println("Booting...");
798+
Serial.println("=========================================");
799+
Serial.printf(" openUC2 ODMR Server %s\n", VERSION_STRING);
800+
Serial.printf(" Build : %s %s\n", BUILD_DATE, BUILD_TIME);
801+
Serial.printf(" Git : %s (%s)\n", GIT_HASH, GIT_BRANCH);
802+
Serial.println("=========================================");
746803

747804
disableLoopWDT(); // Deactivate Watchdog for loop
748805
// Check WiFi capabilities
@@ -890,9 +947,9 @@ void setup()
890947
// adf.updateFrequency(2.2e9); // 2.2 GHz // 1.800 GHz ─ writes R5…R0
891948

892949
// Setup routes
893-
// Root handler — serve index.html from SPIFFS
950+
// Root handler — serve index.html from PROGMEM
894951
server.on("/", HTTP_GET, []()
895-
{ handleFileRequest("/index.html"); });
952+
{ servePROGMEM(INDEX_HTML, "text/html"); });
896953

897954
// Captive portal detection endpoints (P0 #1)
898955
// Returning a non-"Success" HTML page triggers the OS captive portal popup.
@@ -965,11 +1022,19 @@ void setup()
9651022
// Silently return 404 for known OS background requests (Windows Update, etc.)
9661023
// to avoid log spam from e.g. /msdownload/update/... certificate fetches
9671024
if (uri.startsWith("/msdownload/") || uri.endsWith(".cab") ||
968-
uri.startsWith("/GTSLT") || uri.startsWith("/ocsp"))
1025+
uri.startsWith("/GTSLT") || uri.startsWith("/ocsp") ||
1026+
uri.startsWith("/en-GB/") || uri.startsWith("/en-US/"))
9691027
{
9701028
server.send(404, "text/plain", "");
9711029
return;
9721030
}
1031+
// Android / Windows append a UUID to /generate_204, e.g. /generate_204_abc123
1032+
// Match any URI starting with /generate_204 to catch these variants.
1033+
if (uri.startsWith("/generate_204"))
1034+
{
1035+
server.send(200, "text/html", PORTAL_HTML);
1036+
return;
1037+
}
9731038
handleFileRequest(uri);
9741039
});
9751040
server.on("/odmr_act", HTTP_POST, handleOdmrAct);

Production_Files/Software/ODMR_Server/src/version_info.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
#define __VERSION_INFO_H__
66

77
#define FIRMWARE_VERSION "1.0.0"
8-
#define BUILD_DATE "2026-03-03"
9-
#define BUILD_TIME "12:58:56"
10-
#define BUILD_TIMESTAMP "20260303125856"
11-
#define GIT_HASH "7f03fb3"
12-
#define GIT_BRANCH "main"
8+
#define BUILD_DATE "2026-03-09"
9+
#define BUILD_TIME "14:54:53"
10+
#define BUILD_TIMESTAMP "20260309145453"
11+
#define GIT_HASH "83740a3"
12+
#define GIT_BRANCH "fix-listoferrors"
1313

1414
// Combined version string
1515
#define VERSION_STRING "v" FIRMWARE_VERSION " (" BUILD_DATE " " BUILD_TIME " - " GIT_HASH ")"

Production_Files/Software/ODMR_Server/src/website/justage_html.h

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

Production_Files/Software/ODMR_Server/src/website/messung_html.h

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

Production_Files/Software/ODMR_Server/src/website_html/justage.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ <h5>Messbereich</h5>
103103
<div class="card-body">
104104
<p><strong>Minimal:</strong> 0</p>
105105
<p><strong>Maximal:</strong> 65535</p>
106-
<p><strong>Optimal:</strong> > 50000</p>
106+
<p><strong>Optimal:</strong> > 4000</p>
107107
</div>
108108
</div>
109109

@@ -238,11 +238,11 @@ <h5>Sensor-Einstellungen</h5>
238238
intensityBar.setAttribute('aria-valuenow', percentage);
239239

240240
// Update status based on intensity
241-
if (intensity > 50000) {
241+
if (intensity > 4000) {
242242
alignmentStatus.textContent = 'Optimal';
243243
alignmentStatus.className = 'badge bg-success';
244244
intensityBar.className = 'progress-bar bg-success';
245-
} else if (intensity > 20000) {
245+
} else if (intensity > 1500) {
246246
alignmentStatus.textContent = 'Gut';
247247
alignmentStatus.className = 'badge bg-warning';
248248
intensityBar.className = 'progress-bar bg-warning';

0 commit comments

Comments
 (0)