ESP32 firmware bridging the 1550 nm MOPA (Koheron CTL200 seed + Agiltron M511 two-stage EDFA) to MQTT/HA as a single device. Telemetry and control for both instruments verified end-to-end: MQTT command -> seed lasing -> booster ramp to 27 dBm (~515 mW) at the documented 2500 mA operating point. - PlatformIO/Arduino project, project-local venv, Makefile upload flow - Koheron ASCII driver on UART2; Agiltron binary 0x55AA driver on UART1 - HA discovery (sensors, binary_sensors, switches, numbers); LWT availability - Static IP, firmware current clamps on seed and booster pump
7.9 KiB
koheron-esp32-bridge
ESP32 firmware that bridges the Butterfly EDFA Rack — a 1550 nm MOPA:
a Koheron CTL200
driving a DFB seed into an Agiltron M511 EYDFA power amplifier (output up to
~1 W / 30 dBm) — to WiFi/MQTT for monitoring and control from Home Assistant
(broker 192.168.0.102:1883, anonymous).
The whole rack is presented as a single HA device (butterfly_edfa_rack).
Topics, mirroring the lab's EDFA→MQTT convention:
lab/laser/butterfly_edfa_rack/availability online | offline (LWT)
lab/laser/butterfly_edfa_rack/diag/<key> ESP/network diagnostics
lab/laser/butterfly_edfa_rack/seed/<key> Koheron CTL200 (increment 3)
lab/laser/butterfly_edfa_rack/amp/<key> Agiltron M511 (increment 4)
lab/laser/butterfly_edfa_rack/set/<...> commands (increment 5)
HA discovery configs are published under homeassistant/..., all sharing one
device block so entities group under the single rack device.
Optical architecture
The rack is a MOPA (master-oscillator power-amplifier) at 1550 nm:
Koheron CTL200 Agiltron M511 EYDFA (two-stage)
┌──────────────┐ ┌─────────────────────────────────────┐
│ DFB1550LP │ fibre │ pre-amp stage ──► booster stage │ fibre
│ seed diode ├─────────►│ (pump diode A) (pump diode B, ├────────► output
│ + TEC │ │ ~170 mA, ~16 dBm "pump-1") │ (up to ~1 W
└──────────────┘ └─────────────────────────────────────┘ / 30 dBm)
ilaser, lason input PD preamp PD output-1 PD
The M511 has two internal pump diodes, and the firmware reads both:
- Pre-amp pump — runs semi-independently at ~170 mA, lifts the seed to
~16 dBm (
amp/preamp_current_ma,amp/preamp_power_dbm). - Booster pump = "pump-1" — the controllable output stage
(
amp/pump1_current_ma, set viaset/amp/pump1_current_ma). - "Pump-2" is a second booster channel that this unit leaves at 0 mA
regardless of settings (per
Lab/Equipment/Lasers/Agiltron EDFA); we don't drive it. It is read but not exposed as a control.
Run the booster in ACC (constant-current) mode — APC stepping quantises the output. Below transparency the booster erbium fibre absorbs the pre-amp output, so output stays at the PD floor (−60 dBm) until the pump is high enough. Measured 2026-06-17 with ~6 mW (7.8 dBm) seed into the EDFA:
| Pump-1 | Output-1 | note |
|---|---|---|
| 500–900 mA | −60 dBm | below transparency, signal absorbed |
| 1200 mA | 21.0 dBm | booster turns on |
| 1500 mA | 23.3 dBm | |
| 2000 mA | 25.9 dBm | |
| 2500 mA | 27.1 dBm | documented operating point, ~515 mW |
Pump current overshoots ~2× briefly on each step up and settles in 2–4 s, so ramp gradually rather than jumping straight to the setpoint.
Safety: this is a Class 4, ~1 W, invisible 1550 nm source. The firmware clamps pump-1 to
PUMP_CURRENT_MAX_MA(3000 mA) and seed current toSEED_CURRENT_MAX_MA, but those are not interlocks — treat the output as hazardous unless it terminates in a power meter / beam dump / closed system.
Hardware
-
ESP32-D0WD-V3 dev board (WROOM-32 class), CP2102N USB bridge →
/dev/ttyUSB0, MACb0:cb:d8:8f:ba:10. -
The ESP32 USB port is used only for flashing/console (UART0).
-
Two instrument links on separate hardware UARTs:
ESP32 Instrument Protocol GPIO17 (TX2) → Koheron CTL200 RX (terminal block) ASCII, 115200 8N1, \r\n,>>promptGPIO16 (RX2) ← Koheron CTL200 TX GPIO26 (TX1) → Agiltron M511 RX (P2 pin 3) binary 0x55AA packets, 115200 8N1 GPIO25 (RX1) ← Agiltron M511 TX (P2 pin 7) GND both grounds All links are 3V3 logic (CTL200 is 5V-tolerant). UART1 is remapped off its default flash-clashing pins.
Network: the firmware pins a static IP
192.168.0.150(gateway.1, /24) — seeUSE_STATIC_IPininclude/config.h. This avoids the clash with the BKtel EDFA Pi on.101that a DHCP lease originally caused..150is outside any device documented in the HA notes; move it if your DHCP pool overlaps.
Toolchain
PlatformIO (Arduino framework), pinned in a project-local .venv so nothing
is installed globally.
make # show targets
make build # compile
make upload # compile + flash (PORT=/dev/ttyUSB0 by default)
make monitor # serial console at 115200
make detect # identify the chip on the port
A fresh checkout self-bootstraps the venv on first make.
Configuration
Copy include/secrets.example.h → include/secrets.h (git-ignored) and fill
in WiFi + MQTT details. Needed from increment 2 onward.
Increment roadmap
- 1 — toolchain check. Blink + serial heartbeat; confirms build/upload/monitor.
- 2 — WiFi + MQTT + HA discovery. Joins WiFi, connects to broker with an LWT availability topic, publishes diagnostic entities (uptime, RSSI, IP).
- 3 — Seed telemetry (read-only). UART2 → Koheron
lason/ilaser/vlaser/rtset/rtact/itec/vtec/tboard/iphd/err; published underseed/...as HA sensors. Verified live against CTL200 fw v0.21 (echo + value +>>framing, parsed correctly). - 4 — Amp telemetry (read-only). UART1 → Agiltron
get_status(binary 0x55AA, ported fromedfa.py); publishes module/pre-amp temp, pre-amp & pump-1 currents, input/pre-amp/output dBm, warnings underamp/.... Verified against the live unit (SN Y4022708, PN EDFA-1C51113331111, device ID 0x0000006F).amp/pump_enabledreads the authoritativeget_settingsfield. - 5 — control.
set/...for seed (lason,ilaser) and amp (pump on/off, pump-1 ACC current) as HA switch/number entities, with firmware current clamps. Verified end-to-end: MQTT command → seed lasing → booster ramp to 27 dBm. (Future: seedrtset/tecon, amp APC power, Koheronsave.)
MQTT interface
State topics retained under lab/laser/butterfly_edfa_rack/:
| Topic suffix | Meaning |
|---|---|
availability |
online / offline (LWT) |
diag/{uptime,rssi,ip} |
ESP / network diagnostics |
seed/link |
Koheron reachable? |
seed/laser_enabled |
seed laser on (1/0) |
seed/current_ma, seed/voltage_v |
seed drive |
seed/rt_setpoint_ohm, seed/rt_actual_ohm |
thermistor setpoint / actual |
seed/tec_current_a, seed/tec_voltage_v, seed/board_temp_c |
seed TEC / board |
seed/photodiode_ma, seed/error |
seed monitor PD, CTL200 error code |
amp/link |
Agiltron reachable? |
amp/module_temp_c, amp/preamp_temp_c |
EDFA temperatures |
amp/preamp_current_ma, amp/preamp_power_dbm |
pre-amp stage |
amp/pump1_current_ma, amp/pump1_set_ma |
booster pump actual / setpoint |
amp/input_power_dbm, amp/output1_power_dbm, amp/output2_power_dbm |
optical powers |
amp/pump_enabled, amp/warnings |
booster state, decoded warnings |
Command topics (publish to lab/laser/butterfly_edfa_rack/set/):
| Topic suffix | Payload | Action |
|---|---|---|
seed/laser |
1 / 0 |
Koheron lason |
seed/current_ma |
mA | Koheron ilaser (clamped ≤ SEED_CURRENT_MAX_MA) |
amp/pump |
1 / 0 |
Agiltron pump on / off |
amp/pump1_current_ma |
mA | set ACC mode + pump-1 current (clamped ≤ PUMP_CURRENT_MAX_MA) |