Platform-universal Arduino library for reading SFP/SFP+ diagnostics (SFF-8472 style) over I2C.
- A0h identity parsing (vendor, PN, SN, date, compliance bytes)
- A2h live DOM telemetry:
- temperature (°C)
- Vcc (V)
- TX bias (mA)
- TX power (mW / dBm)
- RX power (mW / dBm)
- Alarm/warning thresholds
- Live flag words
- Internal and external calibration handling
- Raw EEPROM access and hex dumps
- SFP+ upper-page helpers:
selectUpperPage(page)readA2Paged(page, offset, buffer, len)readUpperPage00..03()decodeUpperPageLinkCodes()
Uses only standard Arduino TwoWire (Wire).
Designed for AVR, SAMD, ESP32, RP2040, STM32, and other cores with normal Wire support.
- Copy this folder into your Arduino
librariesdirectory. - Restart Arduino IDE.
- Include the library:
#include <Wire.h>
#include <Sfp.h>VCC (3.3V)GNDMOD_DEF1 / SCLMOD_DEF2 / SDA
Optional but useful:
TX_DISABLE(control)TX_FAULT(status)RX_LOS(status)MOD_DEF0 / ABS(presence detect)
SFP cage/module MCU
---------------------------------------------
VCC_T (+3.3V) -> 3V3
VEE_T (GND) -> GND
VCC_R (+3.3V) -> 3V3
VEE_R (GND) -> GND
MOD_DEF1 (SCL) -> I2C SCL
MOD_DEF2 (SDA) -> I2C SDA
TX_DISABLE -> GPIO output (optional)
TX_FAULT -> GPIO input (optional)
RX_LOS -> GPIO input (optional)
MOD_DEF0 / ABS -> GPIO input (optional)
- Do not connect SFP high-speed differential lines directly to Pico GPIO/UART pins.
- SFP
TD+/TD-andRD+/RD-are high-speed analog differential pairs (SerDes level), not TTL UART. - Pico can connect directly only to:
- I2C diagnostics (
MOD_DEF1/SCL,MOD_DEF2/SDA) - control/status pins (
TX_DISABLE,TX_FAULT,RX_LOS, optionalMOD_DEF0/ABS)
- I2C diagnostics (
- For actual payload transport over fiber, use a dedicated PHY/SerDes/media converter in the data path.
So in this library:
- Pico ↔ SFP is for monitoring/control via I2C
- Optical TX/RX payload is handled by external hardware path
RP2040 Pico pin SFP signal
----------------------------------
3V3(OUT) -> VCC_T + VCC_R
GND -> VEE_T + VEE_R
GP5 (I2C0 SCL) -> MOD_DEF1 / SCL
GP4 (I2C0 SDA) -> MOD_DEF2 / SDA
GP6 (output) -> TX_DISABLE (optional)
GP7 (input) -> TX_FAULT (optional)
GP8 (input) -> RX_LOS (optional)
GP9 (input) -> MOD_DEF0 / ABS (optional)
Wire.setSDA(4);
Wire.setSCL(5);
Wire.begin();
Wire.setClock(100000);ESP32 pin SFP signal
----------------------------------
3V3 -> VCC_T + VCC_R
GND -> VEE_T + VEE_R
GPIO22 (SCL) -> MOD_DEF1 / SCL
GPIO21 (SDA) -> MOD_DEF2 / SDA
GPIO19 (output) -> TX_DISABLE (optional)
GPIO18 (input) -> TX_FAULT (optional)
GPIO5 (input) -> RX_LOS (optional)
GPIO17 (input) -> MOD_DEF0 / ABS (optional)
Wire.begin(21, 22); // SDA, SCL
Wire.setClock(100000);-
SFP modules require a stable 3.3V supply (not 5V).
-
Budget enough current on 3.3V rail:
- typical optical modules: often ~150–300 mA
- some modules can draw more during startup/laser activity
-
Decouple near the cage/module power pins (for example 100 nF + 10 µF close to the connector).
-
Tie all grounds together (MCU + SFP power ground).
-
SFP I2C is typically 3.3V logic.
-
SDA/SCL usually need pull-ups to 3.3V:
- common values: 2.2k–4.7k
- if your breakout already has pull-ups, do not add too-strong parallel pull-ups
-
If MCU is 5V-only, use proper level shifting for I2C and status/control GPIOs.
-
Keep wiring short and clean for reliable I2C.
-
If reads are unstable, lower I2C clock (e.g., 100 kHz) and re-check power integrity.
#include <Wire.h>
#include <Sfp.h>
sfp::Sfp sfpMod;
void setup() {
Serial.begin(115200);
Wire.begin();
Wire.setClock(100000);
if (sfpMod.begin(Wire, 0x50, 0x51) != sfp::Error::Ok) {
Serial.println("SFP init failed");
while (1) {}
}
sfp::Identity id;
sfp::DomData dom;
if (sfpMod.readAll(id, dom) == sfp::Error::Ok) {
Serial.print("Vendor: "); Serial.println(id.vendor);
Serial.print("PN: "); Serial.println(id.partNumber);
Serial.print("Temp C: "); Serial.println(dom.temperatureC, 2);
Serial.print("RX dBm: "); Serial.println(dom.rxPowerdBm, 2);
}
}
void loop() {}=== SFP Identity ===
Vendor: FINISAR
PN: FTLF1318P3BTL
SN: FNS1234567A
DOM implemented: yes
Int cal: yes
Ext cal: no
--- DOM ---
Data ready: yes
Temp [C]: 36.75
Vcc [V]: 3.2991
TX bias [mA]: 6.120
TX power [mW]: 0.8123 / [dBm]: -0.90
RX power [mW]: 0.6550 / [dBm]: -1.84
TX fault: 0
RX LOS: 0
--- Thresholds ---
Temp HA/LA/HW/LW: 85.00 / -10.00 / 75.00 / -5.00
Vcc HA/LA/HW/LW: 3.60 / 3.00 / 3.50 / 3.10
TXB HA/LA/HW/LW: 12.00 / 2.00 / 10.00 / 3.00
TXP HA/LA/HW/LW: 1.58 / 0.10 / 1.26 / 0.13
RXP HA/LA/HW/LW: 1.58 / 0.05 / 1.26 / 0.08
Flags temp/vcc/txb/txp/rxp: 0x0000 0x0000 0x0000 0x0000 0x0000
begin(wire, a0Addr=0x50, a2Addr=0x51)readIdentity(Identity&)readDom(DomData&)readThresholds(Thresholds&)readFlags(Flags&)readAll(Identity&, DomData&, Thresholds* = nullptr, Flags* = nullptr)readA0(offset, buf, len)/readA2(offset, buf, len)dumpA0(stream, start, len, cols)/dumpA2(...)selectUpperPage(page)readA2Paged(page, offset, buf, len)readUpperPage00..03(...)decodeUpperPageLinkCodes(LinkCodes&)
examples/basic_dom/basic_dom.ino— identity + live DOM pollingexamples/sfp_plus_pages/sfp_plus_pages.ino— upper-page reads and link code decodeexamples/identity_thresholds/identity_thresholds.ino— identity, thresholds, and flags reportexamples/usb_serial_sfp_bridge/usb_serial_sfp_bridge.ino— transparent USB↔UART bridge with SFP diagnostics monitoring; LED blinks on SFP errorsexamples/rp2040_pio_manchester_serial/rp2040_pio_manchester_serial.ino— RP2040 PIO Manchester TX/RX character bridge (for hardware with external differential interface)
All functions return sfp::Error.
Common non-OK values:
NotInitializedI2cNackBadParamDomNotSupportedDataNotReady
Different SFP vendors expose diagnostics differently. Some modules provide partial or vendor-specific behavior. Validate against your exact transceiver models.