Intended Audience: Researchers, engineers, and makers working on laboratory automation, microscopy, robotics, or hardware-software interfacing. This module is designed for users who need flexible control of LEDs and other light sources from Python and Arduino, including generation of synchronized trigger signals for cameras or other instruments.
Related Modules:
- ControlCamera - Camera acquisition interface
- ControlMotors - Motor and stage control
- ControlSerial - Python serial interface
- Main Project Documentation - Complete microscope setup
This repository demonstrates how to control light sources with Arduino and Python, and output a trigger signal to synchronize a camera.
The codes rely on Arduino, pyserial and the ControlSerial module.
- Install ControlSerial and the Arduino software
- The light sources are already set-up. Refer to the example gallery for ideas.
- The light sources can be controlled by a trigger, or pulse-width modulated signal (PWM)
- The code was tested on Windows and Linux
Here are the different hardware equipment the
| Component | Quantity | Price per unit | Example |
|---|---|---|---|
| Arduino Uno | 1 | 24β¬ | Robotshop |
| Light source controller | tested up to 5 | X | Thorlabs |
| Software | Version we used | Download |
|---|---|---|
| Arduino | 1.8.13 | download |
| Python | 3.7 | install |
| ControlSerial | 1.0 | install |
ControlLight can be used the following way:
from ControlLight import ControlLight
arduino_port = "COM5"
sec = 1000 #conversion ms to s
blue_param = {'pin': 11,
'offset': 0.5*sec, #ms
'period': 5*sec, #ms
'duration': 2*sec, #ms
'analog_value': 255
}
arduino_light = ControlLight(arduino_port)
arduino_light.add_digital_pulse(blue_param)
arduino_light.start_measurement(300*sec)
arduino_light.wait()The lights are controlled with periodic digital pulses. Each light has its own configuration, as in the blue_param variable in the example above. The following parameters are expected:
pin: The Arduino pin to which the LED light is connected.period: The period of the output signal (an integer value in milliseconds).duration: The amount of time the signal is 'on' during a period. This is an integer in milliseconds and should be less than the period.offset: The number of milliseconds that the signal should be delayed (milliseconds).analog_value: When the light is 'on', it is possible to generate a PWM signal. The underlying implementation uses Arduino's analogWrite, and the value should be between 0 and 255.
Once the lights are configured, use start_measurement to begin the experiment. This function takes one optionnal argument, the duration of the experiment in milliseconds. The methid light.wait will block the Python script until the experiment is finished.
Download or clone the repository:
git clone https://github.com/SonyCSLParis/ControlLight.git
-
Get the wiring to connect the Arduino to the light source controller. To begin, connect the wire to pin 11.
-
Open the LEDControl/LEDControl.ino file.
-
Select the Arduino board type in the "Tools/card type"
- Press the check sign. If an error related to "RomiSerial" appears, verify that you have properly followed the instructions in the ControlSerial repository.
-
If no error appears you can click the arrow to load the code in the Arduino.
-
To test that you can properly interact with the Arduino, click on the magnifying glass in the upper right to open the serial monitor. Select 115200 baud and Both NL & CR and type: "#?:xxxx" and ensure you get this output:
- Test the control command:
#d[11,0,0,1,0,2,0,255]:xxxx#d[pin, delay_s, delay_ms, time_high_s, time_high_ms, period_s, period_ms, value]:xxxx
11 β Arduino pin 11 (output pin for the light) 0 β start delay in seconds 0 β start delay in milliseconds β start immediately 1 β βonβ time in seconds 0 β βonβ time in milliseconds β LED on for 1 second each cycle 2 β period in seconds 0 β period in milliseconds β one full cycle every 2 seconds 255 β PWM value (0β255), i.e. full intensity when on So this command creates a pulse on pin 11: ON for 1 s, OFF for 1 s, repeating (0.5 Hz), at full brightness.
It is a command to generate on and off output on pin 11. The pin stays on for 1 seconds with a period of 2 seconds, at intensity 255. It is later on embeded in a Python code for clarity. You should see a character sequence appear.
- To start the experiment type:
#b[100,0]:xxxx#b[duration_s, duration_ms]:xxxx
You should see the LEDs blink (frequency 0.5Hz).
- Stop the blinking, type:
#e:xxxx**
cd ControlLight
pip install -e .
- Try running the code:
On Windows: python ControlLight/ControlLight.py --port COMx by replacing "COMx" by the correct COM port identified in step 1.
On Linux: python3 ControlLight/ControlLight.py --port /dev/ttyACM0
You should see the LED blink.
This repository currently provides example scripts rather than a full automated test suite. To quickly verify that your setup works:
-
Basic blink test:
cd ControlLight python test/test.py -
PWM/intensity test:
cd ControlLight python test/test2.py
Make sure the Arduino is flashed with the LEDControl firmware and connected on the expected COM port before running these scripts.
- Open the python code to see how it works. Open the python code ControlLight.py. The code is commented and allows to control the frequency and amplitude of the LEDs. Set the parameters:
The content of interest is after
if __name__ == __main__:
- replace the COM port with the one of your set-up (tutorial).
- input the correct ports for the LED control. The port 3 and 11 are good choices because they are PWM pins which allow to control the intensity level of the LEDs rather than only ON-OFF.
- you can change the other parameters that correspond to this scheme:
(Note: to build an LED controller refer to this OpenUC2 repository, otherwise you might already use one of these Thorlabs controlers
The constructor.
ControlLight(self, arduino_port)
arduino_port: The name of the serial device. Example "COM5" (Windows) or "/dev/ttyACM0" (Linux).
Creates a periodic digital pulse.
add_digital_pulse(self, params):
params: A dictionnary with the following entries:pin: The Arduino output pin.offset: The delay, in milliseconds, before the pulse short start.period: The period of the pulse, in milliseconds.duration: The duration of the pulse in milliseconds (0 <= duration <= period)analog_value: The implementation uses Arduino's analogWrite. This means that the pulse can actually be a pulse-width modulated signal. (0 <= analog_value <= 255)
If analog_value is zero, no pulse will be generated. If it is 255, a square wave will be gerated with the given duration. For a value between zero and 255, a PWM is signal is generated with a duty cycle of analog_value/255.
Using a PWM is usefull to control the light intensity of the LEDs. The following example will generate the signal shown in the figure below, with a duty-cycle of ~75% (=192/255).
from ControlLight import ControlLight
arduino_port = "COM5"
sec = 1000
blue_param = {'pin': 6,
'offset': 0,
'period': 0.1*sec,
'duration': 0.05*sec,
'analog_value': 192
}
arduino_light = ControlLight(arduino_port)
arduino_light.add_digital_pulse(blue_param)
arduino_light.start_measurement(60*sec)
arduino_light.wait()Make a pulse secondary to another pulse.
set_secondary(self, primary, secondary):
primary,secondary: The same descriptions of the pin configuration as inadd_digital_pulse. Only the keypinis actually needed to identify the two pulses.
When a pulse is defined as secondary to another pulse, it is turned off when the primaty pulse is on. This is useful, for example, when an activation light should be turned off when the mesurement light is turned on.
from ControlLight import ControlLight
#arduino_port = "COM5"
arduino_port = "/dev/ttyACM0"
ms = 1
sec = 1000
blue_param = {}
purple_param = {}
purple_param["pin"] = 11
purple_param["offset"] = 0
purple_param["duration"] = 10*ms
purple_param["period"] = 50*ms
purple_param["analog_value"] = 255
blue_param["pin"] = 6
blue_param["offset"] = 0
blue_param["duration"] = 0.5*sec
blue_param["period"] = 1*sec
blue_param["analog_value"] = 255
arduino_light = ControlLight(arduino_port)
arduino_light.arduino.set_debug(True)
arduino_light.add_digital_pulse(blue_param)
arduino_light.add_digital_pulse(purple_param)
arduino_light.set_secondary(purple_param, blue_param)
arduino_light.start_measurement(20*sec)
arduino_light.wait()The following two figures show the difference between the two output signals, taken from test3.py and test4.py
Here is the output when both lights are primary:
This is the output when the bottom signal is set as secondary to the top signal:
Start the experiment.
start_measurement(self, duration=0):
duration: (Optional): The duration of the experiment in milliseconds. If duration is negative or equal to zero, the experiment will run forever. The default value is zero.
Stop the experiment.
Block the execution of the current thread until the experiment is over.
Remove all pulses and starts with a clear set-up. See test5.py for an example.
This project is licensed under the GNU General Public License v3.0










