From 1f6e07a1af0cad9a75316466c8e648d88cc24e90 Mon Sep 17 00:00:00 2001
From: diijkstra <16804536+diijkstra@users.noreply.github.com>
Date: Fri, 13 Mar 2026 23:48:49 +0100
Subject: [PATCH 1/3] Web: Add SFP state/pin status
The state of the SFP module is being already send in status.json,
but was not parsed via Web UI. When debuging SFP LOS/Signal detection
it is usefull to see what module is reporting back.
Extended the SFP mouse-over information with:
- RX LOS as reported in 0x02 bit of register 238
- External TX Disabled from 0x80 bit of register 238
- TX fault from 0x04 of the same register
- The last state of RX LOS pin (available also when there is no 0x40
option on the module)
This should help identify missing/incorrect setup of TX disabled pin.
---
html/main.js | 4 ++++
httpd/page_impl.c | 3 ++-
rtlplayground.c | 3 +++
3 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/html/main.js b/html/main.js
index 085db57..1677a38 100644
--- a/html/main.js
+++ b/html/main.js
@@ -97,10 +97,14 @@ function update(callback) {
if (p.sfp_options & 0x40) {
iHTML += "
| Temp | : | " + (Number(p.sfp_temp) >> 8) + "." + ((Number(p.sfp_temp) & 0xff)/256.0 * 100).toFixed(0) + " ℃ |
";
iHTML += "| Vcc | : | " + (Number(p.sfp_vcc) / 10000.0).toFixed(2) + " V |
";
+ iHTML += "| TX-Fault | : | " + (Boolean(Number(p.sfp_state) & 0x4)) + " |
";
+ iHTML += "| TX-Disabled | : | " + (Boolean(Number(p.sfp_state) & 0x80)) + " |
";
iHTML += "| TX-Bias | : | " + (Number(p.sfp_txbias) / 500.0).toFixed(1) + " mA |
";
iHTML += "| TX-Power | : | " + (Number(p.sfp_txpower) / 10.0).toFixed(0) + " mW |
";
iHTML += "| RX-Power | : | " + (Number(p.sfp_rxpower) / 10.0).toFixed(0) + " mW |
";
+ iHTML += "| RX-LOS | : | " + (Boolean(Number(p.sfp_state) & 0x2)) + " |
";
}
+ iHTML += "| RX-LOS pin | : | " + Boolean(Number(p.sfp_los)) + " |
";
} else {
pAdvertised[n] = parseInt(p.adv, 2);
}
diff --git a/httpd/page_impl.c b/httpd/page_impl.c
index 82e5314..56aa523 100644
--- a/httpd/page_impl.c
+++ b/httpd/page_impl.c
@@ -655,7 +655,8 @@ void send_status(void)
slen += strtox(outbuf + slen,"\",\"sfp_serial\":\"");
for (register uint8_t s = 0; s < 16; s++)
outbuf[slen++] = sfp_module_serial[machine.is_sfp[i]-1][s];
- char_to_html('"');
+ slen += strtox(outbuf + slen,"\",\"sfp_los\":");
+ bool_to_html(sfp_pins_last & (0x2 << (((machine.is_sfp[i]-1) << 2))));
} else {
bool_to_html(0);
}
diff --git a/rtlplayground.c b/rtlplayground.c
index 041c7ed..8f320a7 100644
--- a/rtlplayground.c
+++ b/rtlplayground.c
@@ -124,6 +124,9 @@ __code uint16_t bit_mask[16] = {
__xdata uint8_t linkbits_last[4];
__xdata uint8_t linkbits_last_p89;
+// Last known state of the SFP detection/Loss of Signal pins
+// SFP1 b0 = 1 => module missing, b1 = 1 => LOS;
+// SFP2 b4 = 1 => module missing, b5 = 1 => LOS;
__xdata uint8_t sfp_pins_last;
__xdata char sfp_module_vendor[2][17];
__xdata char sfp_module_model[2][17];
From 6b64534f4000485ffcba7bbddc3ff630789c6082 Mon Sep 17 00:00:00 2001
From: diijkstra <16804536+diijkstra@users.noreply.github.com>
Date: Sat, 14 Mar 2026 13:15:01 +0100
Subject: [PATCH 2/3] WEB: Display single RX LOS
As sugested, lets display single value, and raise alarm when pin
is different than module.
---
html/main.js | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/html/main.js b/html/main.js
index 1677a38..badd491 100644
--- a/html/main.js
+++ b/html/main.js
@@ -102,9 +102,22 @@ function update(callback) {
iHTML += "| TX-Bias | : | " + (Number(p.sfp_txbias) / 500.0).toFixed(1) + " mA |
";
iHTML += "| TX-Power | : | " + (Number(p.sfp_txpower) / 10.0).toFixed(0) + " mW |
";
iHTML += "| RX-Power | : | " + (Number(p.sfp_rxpower) / 10.0).toFixed(0) + " mW |
";
- iHTML += "| RX-LOS | : | " + (Boolean(Number(p.sfp_state) & 0x2)) + " |
";
}
- iHTML += "| RX-LOS pin | : | " + Boolean(Number(p.sfp_los)) + " |
";
+ iHTML += "| RX-LOS | : | "
+ var rx_los_pin = Boolean(Number(p.sfp_los));
+ if (p.sfp_options & 0x40) {
+ var rx_los_module = Boolean(Number(p.sfp_state) & 0x2);
+ if (rx_los_module != rx_los_pin) {
+ iHTML += "pin=" + rx_los_pin + " ";
+ iHTML += "mod=" + rx_los_module + " ";
+ iHTML += "❗❗❗❗";
+ } else {
+ iHTML += rx_los_pin;
+ }
+ } else {
+ iHTML += Boolean(Number(p.sfp_los));
+ }
+ iHTML += " |
";
} else {
pAdvertised[n] = parseInt(p.adv, 2);
}
From b47a868db3b1e8e1f4ee79066ea6067071e05757 Mon Sep 17 00:00:00 2001
From: diijkstra <16804536+diijkstra@users.noreply.github.com>
Date: Sun, 15 Mar 2026 12:18:02 +0100
Subject: [PATCH 3/3] Web: Handle missing LOS pin
When device has no LOS pin and module has no extended status, the
RX LOS value will not be shown. When both are present, the equality
check is performed. When only one is present, the present value
will be shown.
json from the switch, will still contain the field, but can be
null for when pin is not available.
---
html/main.js | 51 +++++++++++++++++++++++------------------------
httpd/page_impl.c | 6 +++++-
2 files changed, 30 insertions(+), 27 deletions(-)
diff --git a/html/main.js b/html/main.js
index badd491..116384d 100644
--- a/html/main.js
+++ b/html/main.js
@@ -90,11 +90,12 @@ function update(callback) {
var iHTML = "";
iHTML += "| Link speed | : | " + linkS[p.link + 1] + " |
";
if (p.isSFP) {
- pAdvertised[n] = 0;
+ pAdvertised[n] = 0;
+ const hasExtendedStatus = p.sfp_options & 0x40;
iHTML += "| Vendor | : | " + p.sfp_vendor + " |
";
iHTML += "| Model | : | " + p.sfp_model + " |
";
iHTML += "| Serial | : | " + p.sfp_serial + " |
";
- if (p.sfp_options & 0x40) {
+ if (hasExtendedStatus) {
iHTML += "| Temp | : | " + (Number(p.sfp_temp) >> 8) + "." + ((Number(p.sfp_temp) & 0xff)/256.0 * 100).toFixed(0) + " ℃ |
";
iHTML += "| Vcc | : | " + (Number(p.sfp_vcc) / 10000.0).toFixed(2) + " V |
";
iHTML += "| TX-Fault | : | " + (Boolean(Number(p.sfp_state) & 0x4)) + " |
";
@@ -103,35 +104,33 @@ function update(callback) {
iHTML += "| TX-Power | : | " + (Number(p.sfp_txpower) / 10.0).toFixed(0) + " mW |
";
iHTML += "| RX-Power | : | " + (Number(p.sfp_rxpower) / 10.0).toFixed(0) + " mW |
";
}
- iHTML += "| RX-LOS | : | "
- var rx_los_pin = Boolean(Number(p.sfp_los));
- if (p.sfp_options & 0x40) {
- var rx_los_module = Boolean(Number(p.sfp_state) & 0x2);
- if (rx_los_module != rx_los_pin) {
- iHTML += "pin=" + rx_los_pin + " ";
- iHTML += "mod=" + rx_los_module + " ";
- iHTML += "❗❗❗❗";
- } else {
- iHTML += rx_los_pin;
- }
- } else {
- iHTML += Boolean(Number(p.sfp_los));
+ // Not all devices & modules have LOS pin...
+ const rx_los_pin = p.sfp_los !== null ? Boolean(Number(p.sfp_los)) : null;
+ const rx_los_module = hasExtendedStatus ? Boolean(Number(p.sfp_state) & 0x2) : null;
+ if (rx_los_module !== null || rx_los_pin !== null) {
+ iHTML += ` |
| RX-LOS | : | ${rxLosHTML(rx_los_pin, rx_los_module)} |
`;
}
- iHTML += "";
} else {
pAdvertised[n] = parseInt(p.adv, 2);
- }
+ };
iHTML += "
";
tt.innerHTML = iHTML;
- }
- }
- if (callback)
- callback();
- }
- };
- xhttp.open("GET", "/status.json", true);
- xhttp.timeout = 5000;
- sendXHTTP(xhttp);
+ }}
+ if (callback)
+ callback();
+ }};
+ xhttp.open("GET", "/status.json", true);
+ xhttp.timeout = 5000;
+ sendXHTTP(xhttp);
+}
+
+function rxLosHTML(pinStatus, moduleStatus) {
+ if (moduleStatus !== null && pinStatus !== null && moduleStatus !== pinStatus) {
+ return `pin=${pinStatus}
mod=${moduleStatus}
❗❗❗❗`;
+ }
+
+ // Returns first non null value
+ return moduleStatus ?? pinStatus;
}
function callbackXHTTP()
diff --git a/httpd/page_impl.c b/httpd/page_impl.c
index 56aa523..92aed11 100644
--- a/httpd/page_impl.c
+++ b/httpd/page_impl.c
@@ -656,7 +656,11 @@ void send_status(void)
for (register uint8_t s = 0; s < 16; s++)
outbuf[slen++] = sfp_module_serial[machine.is_sfp[i]-1][s];
slen += strtox(outbuf + slen,"\",\"sfp_los\":");
- bool_to_html(sfp_pins_last & (0x2 << (((machine.is_sfp[i]-1) << 2))));
+ if (machine.sfp_port[machine.is_sfp[i]-1].pin_los == GPIO_NA) {
+ slen += strtox(outbuf + slen,"null");
+ } else {
+ bool_to_html(sfp_pins_last & (0x2 << (((machine.is_sfp[i]-1) << 2))));
+ }
} else {
bool_to_html(0);
}