mirror of
https://github.com/cesanta/mongoose.git
synced 2026-11-08 16:11:18 +00:00
Added dev notes
This commit is contained in:
1 parent
b43988ad20
commit
cab8ccddea
4 files changed
+160
-99
No files matched your search
@@ -23,6 +23,9 @@ dashboard.html.c: dashboard.html
|
|||||||
node $(MONGOOSE_DIR)/resources/pack.js dashboard.html::gzip > $@
|
node $(MONGOOSE_DIR)/resources/pack.js dashboard.html::gzip > $@
|
||||||
@echo "On-flash size: `grep -oE '[0-9]+' $@ | wc -l`"
|
@echo "On-flash size: `grep -oE '[0-9]+' $@ | wc -l`"
|
||||||
|
|
||||||
|
purge:
|
||||||
|
curl -s https://purge.jsdelivr.net/gh/cesanta/mongoose@master/resources/dashboard.js
|
||||||
|
|
||||||
# For development: rebuild and restart if any of the sources change
|
# For development: rebuild and restart if any of the sources change
|
||||||
watch:
|
watch:
|
||||||
watchexec -r -w main.c -w mongoose_dashboard.c -w $(MONGOOSE_DIR)/mongoose.h -w web_root -e c,h,html,css,js,svg,png -- make
|
watchexec -r -w main.c -w mongoose_dashboard.c -w $(MONGOOSE_DIR)/mongoose.h -w web_root -e c,h,html,css,js,svg,png -- make
|
||||||
|
|||||||
@@ -32,13 +32,12 @@ static void sync_leds(bool is_write) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read-only device Metrics
|
// Read-only device Metrics
|
||||||
static int s_ram = 32, s_cpu = 7, s_humidity = 59;
|
static int s_ram = 32, s_cpu = 7;
|
||||||
static double s_temperature = 24.8;
|
static double s_temperature = 24.8;
|
||||||
|
|
||||||
static struct mg_field fields_metrics[] = {
|
static struct mg_field fields_metrics[] = {
|
||||||
{"ram", MG_VAL_INT, &s_ram, sizeof(s_ram)},
|
{"ram", MG_VAL_INT, &s_ram, sizeof(s_ram)},
|
||||||
{"cpu", MG_VAL_INT, &s_cpu, sizeof(s_cpu)},
|
{"cpu", MG_VAL_INT, &s_cpu, sizeof(s_cpu)},
|
||||||
{"humidity", MG_VAL_INT, &s_humidity, sizeof(s_humidity)},
|
|
||||||
{"temperature", MG_VAL_DBL, &s_temperature, sizeof(s_temperature)},
|
{"temperature", MG_VAL_DBL, &s_temperature, sizeof(s_temperature)},
|
||||||
{NULL, 0, NULL, 0},
|
{NULL, 0, NULL, 0},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
chart1: {data: []},
|
chart1: {data: []},
|
||||||
leds: { led1: true, led2: false, led3: false },
|
leds: { led1: true, led2: false, led3: false },
|
||||||
settings: { volume: 12, log_level: 3, name: 'Galway' },
|
settings: { volume: 12, log_level: 3, name: 'Galway' },
|
||||||
metrics: { temperature: 1.2, humidity: 3, cpu: 4, ram: 5 },
|
metrics: { temperature: 1.2, cpu: 4, ram: 5 },
|
||||||
files: {data: [{name: 'device-log-2026-04-25.txt', size: 8625563}]},
|
files: {data: [{name: 'device-log-2026-04-25.txt', size: 8625563}]},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -101,16 +101,6 @@
|
|||||||
<span class="metric-status metric-status-good">ok</span>
|
<span class="metric-status metric-status-good">ok</span>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
<article class="metric-tile">
|
|
||||||
<div class="metric-label">Humidity</div>
|
|
||||||
<div class="metric-value-row">
|
|
||||||
<div class="metric-value">
|
|
||||||
<span class="metric-number" data-bind="metrics.humidity">51</span>
|
|
||||||
<span class="metric-unit">%</span>
|
|
||||||
</div>
|
|
||||||
<span class="metric-status metric-status-good">ok</span>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
<article class="metric-tile metric-tile-warning">
|
<article class="metric-tile metric-tile-warning">
|
||||||
<div class="metric-label">CPU Load</div>
|
<div class="metric-label">CPU Load</div>
|
||||||
<div class="metric-value-row">
|
<div class="metric-value-row">
|
||||||
@@ -142,6 +132,12 @@
|
|||||||
<span class="metric-status metric-status-good">ok</span>
|
<span class="metric-status metric-status-good">ok</span>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
<article class="metric-tile note">
|
||||||
|
<div>
|
||||||
|
To tailor this dashboard to your firmware, follow
|
||||||
|
<a target="_blank" href="https://mongoose.ws/docs/guides/web-dashboard-guide/#dashboard-example">this guide</a>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="detail-row">
|
<div class="detail-row">
|
||||||
@@ -161,7 +157,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Controls Panel -->
|
<!-- Controls Panel -->
|
||||||
<div class="panel controls-panel">
|
<div class="panel">
|
||||||
<div class="panel-title">LED Control</div>
|
<div class="panel-title">LED Control</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
@@ -176,107 +172,150 @@
|
|||||||
<div class="field-label">LED3</div>
|
<div class="field-label">LED3</div>
|
||||||
<input type="checkbox" data-bind="leds.led3" data-autosave="1" class="toggle" />
|
<input type="checkbox" data-bind="leds.led3" data-autosave="1" class="toggle" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="note">
|
||||||
|
To attach these LED controls to your hardware,
|
||||||
|
modify mongoose_dashboard.c :: sync_leds() function.
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
<section id="settings" class="tab-panel" role="tabpanel" aria-labelledby="tab-settings" hidden>
|
<section id="settings" class="tab-panel" role="tabpanel" aria-labelledby="tab-settings" hidden>
|
||||||
<div class="panel" style="width: 22rem;">
|
<div class="row">
|
||||||
<div class="panel-title">Device settings</div>
|
|
||||||
<div class="panel-body">
|
<div class="panel" style="width: 22rem;">
|
||||||
<div class="field">
|
<div class="panel-title">Device settings</div>
|
||||||
<label class="field-label">Name</label>
|
<div class="panel-body">
|
||||||
<input class="field-input"type="text" data-bind="settings.name" data-autosave="1" />
|
<div class="field">
|
||||||
|
<label class="field-label">Name</label>
|
||||||
|
<input class="field-input"type="text" data-bind="settings.name" data-autosave="1" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="field-label">Volume</label>
|
||||||
|
<input class="field-input" type="text" data-bind="settings.volume" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="field-label">Log Level</label>
|
||||||
|
<select class="field-input" data-bind="settings.log_level">
|
||||||
|
<option value="0">Disable logs</option>
|
||||||
|
<option value="1">Error</option>
|
||||||
|
<option value="2">Info</option>
|
||||||
|
<option value="3">Debug</option>
|
||||||
|
<option value="4">Verbose (very noisy)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="panel-footer">
|
||||||
<label class="field-label">Volume</label>
|
<button class="btn btn-secondary" type="button" data-cancel="settings">
|
||||||
<input class="field-input" type="text" data-bind="settings.volume" />
|
<svg viewBox="0 0 24 24" class="nav-icon" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
</div>
|
<path d="M18 6 6 18" />
|
||||||
<div class="field">
|
<path d="m6 6 12 12" />
|
||||||
<label class="field-label">Log Level</label>
|
</svg>
|
||||||
<select class="field-input" data-bind="settings.log_level">
|
Cancel
|
||||||
<option value="0">Disable logs</option>
|
</button>
|
||||||
<option value="1">Error</option>
|
<button class="btn btn-primary" type="button" data-save="settings">
|
||||||
<option value="2">Info</option>
|
<svg viewBox="0 0 24 24" class="nav-icon" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<option value="3">Debug</option>
|
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2Z" />
|
||||||
<option value="4">Verbose (very noisy)</option>
|
<path d="M17 21v-8H7v8" />
|
||||||
</select>
|
<path d="M7 3v5h8" />
|
||||||
|
</svg>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-footer">
|
|
||||||
<button class="btn btn-secondary" type="button" data-cancel="settings">
|
<div class="note" style="width: 44rem;">
|
||||||
<svg viewBox="0 0 24 24" class="nav-icon" fill="none" stroke="currentColor" stroke-width="2">
|
<p>This settings panel is backed by the "settings" dashboard object
|
||||||
<path d="M18 6 6 18" />
|
see Dashboard.init call at the top of index.html - and, consequently,
|
||||||
<path d="m6 6 12 12" />
|
by the "settings" fieldset in mongoose_dashboard.c. </p>
|
||||||
</svg>
|
<p>Fields are bound to the dashboard object via the data-bind attribute,
|
||||||
Cancel
|
e.g. data-bind="setttings.name"</p>
|
||||||
</button>
|
<p>When you save changed fields, the changes are saved to the device.</p>
|
||||||
<button class="btn btn-primary" type="button" data-save="settings">
|
<p> The "NAME" field
|
||||||
<svg viewBox="0 0 24 24" class="nav-icon" fill="none" stroke="currentColor" stroke-width="2">
|
has data-autosave="1", so it is autosaved when user types text.</p>
|
||||||
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2Z" />
|
<p>The other fields are not auto-saved, so when they are changed,
|
||||||
<path d="M17 21v-8H7v8" />
|
dashboard.js automatically applies CSS class "edited" to them to
|
||||||
<path d="M7 3v5h8" />
|
indicate they were changed but not saved.</p>
|
||||||
</svg>
|
<p>The cancel and save buttons are attached to the settings object
|
||||||
Save
|
via the data-cancel and data-save attributes. Those buttons
|
||||||
</button>
|
get automatically enabled or disabled based on editing status.</p>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section id="firmware" class="tab-panel" role="tabpanel" aria-labelledby="tab-firmware" hidden></section>
|
<section id="firmware" class="tab-panel" role="tabpanel" aria-labelledby="tab-firmware" hidden></section>
|
||||||
<section id="files" class="tab-panel" role="tabpanel" aria-labelledby="tab-files" hidden>
|
<section id="files" class="tab-panel" role="tabpanel" aria-labelledby="tab-files" hidden>
|
||||||
<div class="panel" style="max-width: 40rem;">
|
<div class="row">
|
||||||
<div class="panel-title">
|
<div class="panel" style="width: 40rem;">
|
||||||
<span>
|
<div class="panel-title">
|
||||||
<span>File Manager</span>
|
<span>
|
||||||
<span class="metric-status">${files.data.length} Files</span>
|
<span>File Manager</span>
|
||||||
</span>
|
<span class="metric-status">${files.data.length} Files</span>
|
||||||
<div class="header-actions">
|
</span>
|
||||||
<label class="btn btn-primary">
|
<div class="header-actions">
|
||||||
<svg viewBox="0 0 24 24" class="nav-icon" fill="none" stroke="currentColor" stroke-width="2">
|
<label class="btn btn-primary">
|
||||||
<path d="M12 3v12" />
|
<svg viewBox="0 0 24 24" class="nav-icon" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<path d="m7 8 5-5 5 5" />
|
<path d="M12 3v12" />
|
||||||
<path d="M5 21h14" />
|
<path d="m7 8 5-5 5 5" />
|
||||||
</svg>
|
<path d="M5 21h14" />
|
||||||
Upload
|
</svg>
|
||||||
<input type="file" hidden data-upload="1" />
|
Upload
|
||||||
</label>
|
<input type="file" hidden data-upload="1" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="panel-body">
|
||||||
<div class="panel-body">
|
<div class="file-list" data-repeat="files.data" data-iterator="f">
|
||||||
<div class="file-list" data-repeat="files.data" data-iterator="f">
|
<div class="file-row">
|
||||||
<div class="file-row">
|
<div class="file-info">
|
||||||
<div class="file-info">
|
<span class="file-icon" aria-hidden="true">
|
||||||
<span class="file-icon" aria-hidden="true">
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<path d="M14 2H7a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7Z" />
|
||||||
<path d="M14 2H7a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7Z" />
|
<path d="M14 2v5h5" />
|
||||||
<path d="M14 2v5h5" />
|
</svg>
|
||||||
</svg>
|
</span>
|
||||||
</span>
|
<div class="file-name">${f.name}</div>
|
||||||
<div class="file-name">${f.name}</div>
|
</div>
|
||||||
</div>
|
<div class="file-size">${f.size}</div>
|
||||||
<div class="file-size">${f.size}</div>
|
<div class="file-actions">
|
||||||
<div class="file-actions">
|
<a onclick="window.open('files/${encodeURIComponent(f.name)}', '_blank')" target="_blank" class="btn btn-secondary">
|
||||||
<a onclick="window.open('files/${encodeURIComponent(f.name)}', '_blank')" target="_blank" class="btn btn-secondary">
|
<svg viewBox="0 0 24 24" class="nav-icon" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<svg viewBox="0 0 24 24" class="nav-icon" fill="none" stroke="currentColor" stroke-width="2">
|
<path d="M12 3v12" />
|
||||||
<path d="M12 3v12" />
|
<path d="m7 10 5 5 5-5" />
|
||||||
<path d="m7 10 5 5 5-5" />
|
<path d="M5 21h14" />
|
||||||
<path d="M5 21h14" />
|
</svg>
|
||||||
</svg>
|
</a>
|
||||||
</a>
|
<button onclick="fetch('files/${encodeURIComponent(f.name)}?action=delete'); return false;" class="btn btn-secondary">
|
||||||
<button onclick="fetch('files/${encodeURIComponent(f.name)}?action=delete'); return false;" class="btn btn-secondary">
|
<svg viewBox="0 0 24 24" class="nav-icon" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<svg viewBox="0 0 24 24" class="nav-icon" fill="none" stroke="currentColor" stroke-width="2">
|
<path d="M3 6h18" />
|
||||||
<path d="M3 6h18" />
|
<path d="M8 6V4h8v2" />
|
||||||
<path d="M8 6V4h8v2" />
|
<path d="M19 6l-1 14H6L5 6" />
|
||||||
<path d="M19 6l-1 14H6L5 6" />
|
<path d="M10 11v5" />
|
||||||
<path d="M10 11v5" />
|
<path d="M14 11v5" />
|
||||||
<path d="M14 11v5" />
|
</svg>
|
||||||
</svg>
|
</button>
|
||||||
</button>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="note" style="max-width: 40em;">
|
||||||
|
<p>The two default files are fake - you can delete them</p>
|
||||||
|
<p>However the manager works - you can upload new files. They
|
||||||
|
are uploaded to /tmp/ directory on the device.
|
||||||
|
This requires stdio (fopen/fread/...) implemeneted.</p>
|
||||||
|
<p>The easiest way to implement it is to use a filesystem like
|
||||||
|
LittleFS on your internal or external flash
|
||||||
|
and retarget the standard library to use it.</p>
|
||||||
|
<p>See <a href="https://github.com/cpq/bare-metal-programming-guide/tree/main/templates/lfs/nucleo-h563zi">this example</a> on how LittleFS can be implemented on
|
||||||
|
Nucleo-H563ZI internal flash partition.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ body {
|
|||||||
color: var(--color-text-strong);
|
color: var(--color-text-strong);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a, a:visited, a:link { text-decoration: none; }
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
}
|
}
|
||||||
@@ -311,7 +313,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.field-input.edited, .select.edited, .slider.edited>* {
|
.field-input.edited, .select.edited, .slider.edited>* {
|
||||||
border-color: #00502040;
|
border-color: #00500040 !important;
|
||||||
background: #00ff0020;
|
background: #00ff0020;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,7 +407,25 @@ body {
|
|||||||
transform: none;
|
transform: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Detail row (chart + controls) ─────────────────────────────── */
|
.note {
|
||||||
|
color: #075985;
|
||||||
|
background: #e0f2fe;
|
||||||
|
border: 1px solid #7dd3fc;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 0.75rem 0.9rem;
|
||||||
|
font-size: 0.86rem;
|
||||||
|
line-height: 1.55;
|
||||||
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.75);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.detail-row {
|
.detail-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|||||||
Reference in new issue
Block a user