Network: status reports mask/gw/dns; UI shows the DHCP lease in the static fields

With DHCP on, the greyed-out IP/netmask/gateway/DNS fields show the
addresses in use. Unticking DHCP keeps them as the starting point for a
static setup. (Static addressing itself is not applied yet.)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-25 23:42:35 +10:00
parent 122e46e862
commit c4468c4af0
3 changed files with 24 additions and 2 deletions
+13 -1
View File
@@ -138,10 +138,19 @@ static void net_apply(const cJSON *g)
static void net_status(cJSON *st)
{
char ip[16] = "", mac_s[18] = "";
char ip[16] = "", mask[16] = "", gw[16] = "", dns[16] = "", mac_s[18] = "";
esp_netif_ip_info_t info;
if (esp_netif_get_ip_info(s_netif, &info) == ESP_OK && info.ip.addr) {
snprintf(ip, sizeof(ip), IPSTR, IP2STR(&info.ip));
snprintf(mask, sizeof(mask), IPSTR, IP2STR(&info.netmask));
if (info.gw.addr) {
snprintf(gw, sizeof(gw), IPSTR, IP2STR(&info.gw));
}
}
esp_netif_dns_info_t d;
if (esp_netif_get_dns_info(s_netif, ESP_NETIF_DNS_MAIN, &d) == ESP_OK && d.ip.type == ESP_IPADDR_TYPE_V4 &&
d.ip.u_addr.ip4.addr) {
snprintf(dns, sizeof(dns), IPSTR, IP2STR(&d.ip.u_addr.ip4));
}
uint8_t mac[6];
if (esp_netif_get_mac(s_netif, mac) == ESP_OK) {
@@ -151,6 +160,9 @@ static void net_status(cJSON *st)
// Single untagged interface until the VLAN split: AES67 and web share it.
cJSON_AddStringToObject(st, "ip", ip);
cJSON_AddStringToObject(st, "aes67_ip", ip);
cJSON_AddStringToObject(st, "mask", mask); // in use (from DHCP or static)
cJSON_AddStringToObject(st, "gw", gw);
cJSON_AddStringToObject(st, "dns", dns);
cJSON_AddStringToObject(st, "mac", mac_s);
cJSON_AddStringToObject(st, "link", s_link);
}
+2 -1
View File
@@ -26,12 +26,13 @@ Reference devices for UI and defaults: Riedel Bolero (PTP status), Riedel Direct
## Core API
- GET/POST /api/config (full JSON; POST validates, stores to NVS, applies live where possible, returns 4xx with message on bad values)
- Write-only keys (`cfg_mark_secret`, e.g. passwords/client secrets): GET returns "" for them; a POST with "" keeps the stored value, null clears it. Stored in NVS in plain text (NVS encryption is not enabled).
- GET /api/status -> {model?, fw?, power?, ip, aes67_ip, inet_ip, inet_vlan, mac, link, tx_packets, underruns, uptime_s, heap_free, psram_free, temps:[…], ptp:{…}, …project fields}
- GET /api/status -> {model?, fw?, power?, ip, aes67_ip, mask, gw, dns, inet_ip, inet_vlan, mac, link, tx_packets, underruns, uptime_s, heap_free, psram_free, temps:[…], ptp:{…}, …project fields}
- model, fw, power are optional; rows only appear when present (power e.g. "PoE" / "USB" if the board can detect it).
- GET /stream.sdp, POST /api/reboot, POST /api/log/test
- Firmware: GET /api/ota, POST /api/ota, POST /api/ota/confirm, POST /api/ota/rollback (see Firmware update)
## Temperatures
- `status.ip/mask/gw/dns` = the addresses in use (from DHCP or static; "" when none). With DHCP on, the UI shows them in the greyed-out static fields; unticking DHCP keeps them there as the starting point for a static setup.
- `status.temps` = array of {name, c, min_c, max_c, warn_c}; min/max since boot, warn_c optional. Any number of sensors; the UI renders one row per sensor and marks HIGH (red) at or above warn_c.
- ESP32-P4: on-die sensor via the `temperature_sensor` driver (driver/temperature_sensor.h). Pick the measurement range to suit (e.g. -10..80 °C), sample every ~2 s in the health task, and report it as "SoC". It measures the die, not ambient: expect it to read well above room temperature.
- Implemented with range 20..100 °C (±2 °C; the warning level must lie inside the range), warn_c 85 °C. On the test board the die reads ~27 °C at idle in a cool room.
+9
View File
@@ -341,6 +341,13 @@ function read() {
return out;
}
// With DHCP on, the greyed-out static fields show what DHCP gave us. Unticking DHCP leaves the values
// there (a starting point for static addressing) until they are edited.
function netLive() {
if (!f['net.dhcp'].checked || !st.ip) return;
for (const k of ['ip','mask','gw','dns']) f['net.' + k].value = st[k] ?? '';
}
function toggle() {
PROJECT.toggle?.(f);
const role = f['ptp.role'].value;
@@ -354,6 +361,7 @@ function toggle() {
}
// VLAN split: default route lives on the internet VLAN, so the AES67 side gets no gateway/DNS.
if (vlan) f['net.gw'].disabled = f['net.dns'].disabled = true;
netLive();
// RTP multicast must be 224.0.2.0 – 239.255.255.255 (same range Riedel Director enforces).
const m = f['aes67.mcast'], o = m.value.split('.').map(Number);
const ok = o.length === 4 && o.every(x => Number.isInteger(x) && x >= 0 && x <= 255) &&
@@ -497,6 +505,7 @@ async function poll() {
...temps(st.temps)
});
showPtp(st.ptp);
netLive();
sdp();
} catch { table($('status'), {'Device': 'offline'}); showPtp(null); }
}