Files
butterfly-edfa-rack-firmware/README.md
T
James Bateman (hp-laptop) 58475386ba Butterfly EDFA Rack bridge: seed + EDFA over WiFi/MQTT to Home Assistant
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
2026-06-19 10:16:50 +01:00

7.9 KiB
Raw Blame History

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 via set/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 to SEED_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, MAC b0: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, >> prompt
    GPIO16 (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) — see USE_STATIC_IP in include/config.h. This avoids the clash with the BKtel EDFA Pi on .101 that a DHCP lease originally caused. .150 is 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 under seed/... 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 from edfa.py); publishes module/pre-amp temp, pre-amp & pump-1 currents, input/pre-amp/output dBm, warnings under amp/.... Verified against the live unit (SN Y4022708, PN EDFA-1C51113331111, device ID 0x0000006F). amp/pump_enabled reads the authoritative get_settings field.
  • 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: seed rtset/tecon, amp APC power, Koheron save.)

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)