Skip to content

Fix Client Console page memory exhaustion on large HTML files#211

Merged
dorkmo merged 3 commits intomasterfrom
copilot/fix-client-config-loading-issue
Feb 6, 2026
Merged

Fix Client Console page memory exhaustion on large HTML files#211
dorkmo merged 3 commits intomasterfrom
copilot/fix-client-config-loading-issue

Conversation

Copy link
Contributor

Copilot AI commented Feb 5, 2026

Client Console page (/client-console) displays blank page due to memory exhaustion. The 69KB CLIENT_CONSOLE_HTML constant exceeds Arduino String capacity when serveFile() builds it in memory twice: once from PROGMEM, then again in respondHtml() for loading overlay injection.

Changes

  • Added size-based serving strategy in serveFile():

    • Files >32KB: Direct chunked transfer from PROGMEM (512-byte static buffer)
    • Files ≤32KB: Existing String-based serving with overlay injection preserved
  • Prevents stack overflow by using static buffer instead of stack allocation

const size_t LARGE_FILE_THRESHOLD = 32768;
if (htmlLen > LARGE_FILE_THRESHOLD) {
  // Send HTML in 512-byte chunks directly from PROGMEM
  static char buffer[512];
  // ... chunked transfer without building String
  return;
}
// Existing String-based path for smaller files
respondHtml(client, body);

Trade-off: Large files skip loading overlay injection to avoid memory exhaustion. Only CLIENT_CONSOLE_HTML (69KB) currently exceeds threshold; all other pages retain overlay.

Original prompt

This section details on the original issue you should resolve

<issue_title>Client Config webpage not loading</issue_title>
<issue_description>after clicking the client config button in the header, the browser shows a blank page.

this is the html that is shown

<title>Tank Alarm Client Console</title>
<script> window.addEventListener('load', () => { const ov = document.getElementById('loading-overlay'); if (ov) ov.classList.add('hidden'); } ); </script>

:root {
--primary: #0066cc;
--primary-hover: #004c99;
--bg: #f2f2f2;
--card: #ffffff;
--text: SenaxInc/ArduinoSMSTankAlarm#333333;
--muted: SenaxInc/ArduinoSMSTankAlarm#666666;
--border: #cccccc;
--danger: #cc0000;
--success: #28a745;
--warning: #ffc107;
--chip: #e0e0e0;
--radius: 0;
--space-1: 0.4rem;
--space-2: 0.75rem;
--space-3: 1rem;
--space-4: 1.5rem;
--space-5: 1.75rem;
--space-6: 2.25rem;
--card-border: #d7d7d7;
--text-secondary: #6b7280;
--focus: #1d4ed8
}

body {
font-family: system-ui,-apple-system,sans-serif;
margin: 0;
background: var(--bg);
color: var(--text);
line-height: 1.5
}

  • {
    box-sizing: border-box
    }

header {
background: var(--card);
border-bottom: 1px solid var(--border);
position: sticky;
top: 0;
z-index: 100
}

.bar {
max-width: 1200px;
margin: 0 auto;
padding: var(--space-2) var(--space-3);
display: flex;
align-items: center;
gap: var(--space-5)
}

.brand {
font-weight: 700;
font-size: 1.1rem;
white-space: nowrap;
color: SenaxInc/ArduinoSMSTankAlarm#201442
}

.login-title {
margin: 0 0 0.75rem;
font-size: 1.6rem
}

.login-title .brand {
font-size: inherit
}

.login-form {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.6rem;
margin-top: 0.5rem
}

.login-form .pin-input {
max-width: 180px;
width: 100%
}

.login-form .login-button {
min-width: 120px
}

.error {
color: var(--danger);
font-size: 0.9rem;
display: none
}

.header-actions {
display: flex;
flex-wrap: wrap;
gap: var(--space-2);
row-gap: var(--space-1)
}

.pill {
text-decoration: none;
color: var(--text);
padding: var(--space-1) var(--space-2);
border-radius: var(--radius);
font-size: 0.875rem;
font-weight: 500;
background: var(--card);
border: 1px solid var(--border);
white-space: nowrap;
transition: background 0.2s
}

.pill:hover {
background: #f0f0f0
}

.pill.secondary {
background: var(--card);
color: var(--muted)
}

.pill.secondary:hover {
background: #f7f7f7;
color: var(--text)
}

.pill[href^="http"] {
color: var(--primary)
}

main {
max-width: 1200px;
margin: var(--space-6) auto;
padding: 0 var(--space-3);
display: grid;
gap: var(--space-5)
}

.card {
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: var(--space-4);
box-shadow: none
}

h2 {
margin: 0 0 1.5rem;
font-size: 1.25rem;
font-weight: 600
}

h3 {
margin: var(--space-4) 0 var(--space-3);
font-size: 1rem;
font-weight: 600;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.05em
}

.form-grid {
display: grid;
grid-template-columns: repeat(auto-fit,minmax(250px,1fr));
gap: var(--space-4);
margin-bottom: var(--space-5)
}

.field {
display: flex;
flex-direction: column;
gap: var(--space-1)
}

.field span {
font-size: 0.875rem;
font-weight: 500;
color: var(--text)
}

input,select,textarea {
padding: 0.6rem var(--space-2);
border: 1px solid var(--border);
border-radius: var(--radius);
font-size: 0.95rem;
background: var(--card);
transition: border-color 0.15s;
width: 100%
}

input:focus,select:focus,textarea:focus {
outline: none;
border-color: var(--primary);
box-shadow: none
}

button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible,a:focus-visible {
outline: 2px solid var(--focus);
outline-offset: 2px
}

button {
background: var(--primary);
color: white;
border: 1px solid var(--primary);
padding: 0.55rem 1.05rem;
border-radius: var(--radius);
font-weight: 500;
cursor: pointer;
font-size: 0.95rem;
transition: background 0.15s
}

button:hover {
background: var(--primary-hover)
}

#loading-overlay {
position: fixed;
inset: 0;
bac...


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits February 5, 2026 22:55
The CLIENT_CONSOLE_HTML constant is 69KB, which was too large to be assembled into an Arduino String object. The serveFile() function was attempting to:
1. Read the entire 69KB from PROGMEM into a String
2. Pass it to respondHtml() which creates another String to inject loading overlay
This double-String creation exhausted available memory.

Solution: Modified serveFile() to detect large HTML files (>32KB) and serve them directly from PROGMEM in 512-byte chunks without building a String, bypassing the loading overlay injection for these large files.

Co-authored-by: dorkmo <1923070+dorkmo@users.noreply.github.com>
- Changed buffer from stack-allocated to static to prevent stack overflow
- Defined LARGE_FILE_THRESHOLD constant instead of magic number 32768
- Added comment explaining static buffer usage

Co-authored-by: dorkmo <1923070+dorkmo@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix client config webpage not loading issue Fix Client Console page memory exhaustion on large HTML files Feb 5, 2026
Copilot AI requested a review from dorkmo February 5, 2026 22:59
@dorkmo dorkmo marked this pull request as ready for review February 6, 2026 01:05
Copilot AI review requested due to automatic review settings February 6, 2026 01:05
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a critical memory exhaustion issue that prevented the Client Console page (/client-console) from loading. The 69KB CLIENT_CONSOLE_HTML constant exceeded Arduino String capacity when the existing serveFile() function attempted to build it in memory twice: once from PROGMEM and again in respondHtml() for loading overlay injection.

Changes:

  • Added size-based serving strategy that uses chunked transfer for files >32KB to avoid memory exhaustion
  • Preserves existing overlay injection for smaller files (≤32KB) while serving large files directly from PROGMEM
  • Uses a static 512-byte buffer for chunked transfers to avoid excessive stack usage

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@dorkmo dorkmo merged commit 87511bf into master Feb 6, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Client Config webpage not loading

2 participants