Arduino firmware, Python driver, and hardware housing for effectively simultaneous multi-color optogenetic stimulation on a DMD-equipped microscope.
## OverviewOptoTrigger cycles through multiple DMD patterns and LED channels continuously using an Arduino as a trigger intermediary. Before cycling begins, a sequence of patterns (one per LED channel) is pre-loaded into the DMD's internal sequence buffer; each TTL trigger pulse from the Arduino advances the DMD to the next slot in that buffer.
Each cycle:
- Turn the current LED off
- Wait a brief settle period (prevents bleed-through from the previous channel)
- Send a TTL trigger pulse to the DMD (advances it to the next pattern slot)
- Optionally wait for a DMD acknowledgment pulse — see ACK mode below
- Turn the next LED on for the configured exposure duration
- Repeat from step 1 with the next channel
The Arduino reports each LED activation as a FLASH serial event. The Python driver captures these with timestamps and, optionally, writes them to CSV.
- DMD: any Micro-Manager-compatible DMD that supports external TTL triggering. Tested on Mightex Polygon1000G.
- LED controller: any LED controller whose channels can be switched via Arduino TTL output lines. Tested on Lumencor AuraIII.
Device labels, trigger mode property names, and pin mappings are all configurable. See opto_trigger/python/README.md for the full list of values to update for other hardware.
Some DMDs — including the Mightex Polygon — send a TTL pulse back to the Arduino after each trigger is processed, confirming the new pattern is loaded. When ACK mode is enabled, the Arduino waits for this pulse before turning the LED on, eliminating the risk of the LED firing while the DMD is still switching patterns.
If your DMD does not support an ACK output, set ENABLE_ACK = False. You may need to increase SETTLE_MS to allow your DMD enough time to finish switching before the LED fires.
The Arduino and DMD run independently of the camera — they have no knowledge of when the camera is exposing. To avoid unwanted light reaching the sensor during readout, either:
- use optical filters that block the DMD-illuminated wavelengths from the detection path, or
- call
runner.stop_sequence(arduino)before each camera exposure andrunner.run_sequence(seq, arduino)afterwards.
Camera-synchronized acquisition (each frame locked to a specific DMD pattern slot) requires hardware-level camera→Arduino triggering and is not implemented here.
| Path | Description |
|---|---|
hardware/ |
3D-printable housing STL and assembly instructions |
opto_trigger/arduino/DMD_trig/ |
Arduino firmware |
opto_trigger/python/ |
Python driver and Micro-Manager sequence runner |
Print the STL in hardware/ and follow the assembly instructions there to wire the Arduino, BNC connectors, and LED control lines.
Open opto_trigger/arduino/DMD_trig/DMD_trig.ino in the Arduino IDE and upload it to your board.
cd opto_trigger/python
pip install -e .See opto_trigger/python/README.md for installation details, hardware-specific configuration, and API reference. A worked example is in opto_trigger/python/dmd_trig_example.ipynb.
from opto_trigger import run_example_session
timings = run_example_session(
arduino_port="/dev/tty.usbmodemXXXX", # COM10 on Windows
lines=["CYAN", "GREEN"],
exposure_ms=50,
settle_ms=2,
run_seconds=10,
csv_log="flash_events.csv",
)- Lumencor AuraIII LED light source
- Mightex Polygon1000G DMD
