Sei sulla pagina 1di 10


How to control LED intensity with PWM using Arduino.

Updated May 18, 2012 by



8-bit PWM (default)

GuiArduinoLED (Python, .py)

GuiArduinoLED (Matlab, .m, .fig)

GuiArduinoLED (LabVIEW, .vi)

10-bit PWM


Scientific Applications


Contrast sensitivity and glare measurement

Silent substitution

Other vision research

Life sciences in general

LED Illumination implementations


Open-Source / DIY Projects

Commercial products

Here a simple introduction to LED intensity control using pulse-width modulation (PWM) with Arduino is
presented. For more detailed discussion, following sites are recommended: Secrets of Arduino PWM by
Ken Shirriff, Adjusting PWM Frequencies in Arduino, ATmega328 (datasheet) of Arduino Uno, and PWM

Demonstration of LED intensity control with PWM-signal from Arduino Uno using graphical Python frontend.

8-bit PWM (default)
To demonstrate the simplest case of LED light control needed in the pupillometry application described in
the accompanying paper (DOI). The Arduino sketch arduinoLED-simpleGUI.ino was developed based on
the code provided by John Meichle for controlling RGB LEDs, as a part of his neuroelec library for

GuiArduinoLED (Python, .py)

Arduino sketch (GuiArduinoLED.ino) needed to make Arduino respond to incoming serial communication
for example from Python to signal desired changes in light intensity.

Screen capture of the Geany view of the code with the GUI for 8-bit LED Control
Main the loop() is given below:
void loop() {
if(Serial.available() >= 2){
// The cases are given from the .py file (or some other frontend)
switch( byte( )) {
case 'r':
ledOut_ch1 =;

case 'g':
case 'b':
case 'w':
case 'v':
case 'y':


// write the PWM values now to all the channels
analogWrite(ledOut_ch1Pin, ledOut_ch1);
analogWrite(ledOut_ch2Pin, ledOut_ch2);
analogWrite(ledOut_ch3Pin, ledOut_ch3);
analogWrite(ledOut_ch4Pin, ledOut_ch4);
analogWrite(ledOut_ch5Pin, ledOut_ch5);
analogWrite(ledOut_ch6Pin, ledOut_ch6);
// delay in each loop, in milliseconds
delay(20); // going too low maybe lead to unstable behavior
// default value 20 ms worked well

Where if(Serial.available() >= 2){ checks whether serial commands are sent to Arduino, and if it has
been sent then the program advances the switch conditional determining which channel of the 6 LED
channels the user wanted to control.
In the sending end of Python code, the code ( looks like the following for one channel:
if name == "Channel1":
self.ser.write("r" + chr(int(val)))

where the first character "r" specifies the desired controlled channel (ledOut_ch1 variable in above
Arduino sketch), to which is added (+) the desired value for the channel. The variable val is first
converted to integer and then to character (8 bits).
In the Arduino sketch all channels are now updated either with the updated value or the existing value
independent of whether there was any change. The latency of 20 ms (with delay function) is added for
more stable behavior of the loop.

GuiArduinoLED (Matlab, .m, .fig)

A simple example for Matlab is provided to control the light intensity using the MATLAB Support Package
for Arduino (aka ArduinoIO Package)written by Giampiero Campa.
Change the following line (line 42) of the GuiArduinoLED.m to match the serial port of your Arduino,
typically like 'COM2' in Windows and '/dev/ttyACM0' in Linux. Also remember to upload the
file adiosrv.pde sketch to your Arduino from the ArduinoIO package to make the demo work.
% initialize Arduino
port = '/dev/tty.usbmodemfa131';

Screencap of controlling LED intensity (1-channel example) with Arduino from LED

GuiArduinoLED (LabVIEW, .vi)

Demonstration of how to use LabVIEW with Arduino is done using the LabVIEW Interface for
Arduino (LIFA) package provided by National Instruments (NI, the company behind LabVIEW and vast
selection of data acquisition instruments). The example is modified from the example "Simple LED"
provided by NI.

Demonstration of LED intensity control with PWM-signal from Arduino Uno using graphical LabVIEW VI
You need to upload the Arduino sketch LVIFA_base.pde in order to make Arduino responsive to
commands sent from LabVIEW:

Screen capture of LVIFA_base.pde with its accompanying files.

The block diagram of the Virtual Instrument ("LabVIEW program") looks the following:

VI Block diagram for 8-bit LED intensity control with PWM for 2-channels
And the front panel (GUI) as the following:

VI Front panel. The knob buttons control the intensity (0 to 255, 8 bit resolution) and from below them one
can choose the pins to which the LEDs are connected. Debug loop count is useful when testing your
Arduino and making sure that there is nothing slowing down the program and while loop is actually being

10-bit PWM
The default 8-bit resolution of Arduino PWM can be extended for limited amount of pins using for example
the 3rd party timer1 library developed by Jesse Tane. Before being able to upload the accompanying
sketch bit10PWM.ino to Arduino board, the timer1 need to be added for your system folder. For
instructions how to do it, see Arduino Playground's tutorial. Alternatively you can just place the external
library files to the same folder as your Arduino sketch (see how).

The use of a library instead of "brute-force" low-level implementation simplifies the Arduino sketch
(bit10PWM.ino) slightly as seen in the following segment:
void setup()
// Set the baud rate

// Define output pin, and its mode

pinMode(10, OUTPUT);
// Initialize
// initialize timer1, and set a 1/2 second period
// Note that this breaks analogWrite() for digital pins 9 and 10 on Arduino.
// Adjust the PWM period to be something else
// Output the PWM
Timer1.pwm(pinOut, led_dutyCycle_Out); // setup pwm on pin X

Timer1.attachInterrupt(callback); // attaches callback() as a timer overflow interrupt


Now the library can be called with more 'human-readable' calls such as Timer1.pwm() with two input
parameters pinOut specifying to which pin you have connected the anode of your LED, and
`led_dutyCycle_Out' describing the wanted output intensity as 10-bit duty cycle (0 is 0% of the time ON,
and 1023 is 100% of the time ON).
The case handling of which LED channel want to be controlled from the Python GUI is the same as for
the above described 8-bit PWM, but now special treatment is required for the value sent from Python as it
is 10-bit and single characters are always 8-bit ones in serial communication.
The sending Python code ( looks like the following:
def on_changed(self, widget):

val = widget.get_value()
name = widget.get_name()

c = int(val) >> 8 # The value of x shifted 8 bits to the right, creating coarse.
f = int(val) % 256 # The remainder of x / 256, creating fine.

if name == "Channel1":
self.ser.write("c" + chr(int(c)) + chr(int(f)))
print "ERROR: Invalid widget name, in on_changed function"

Where the 10-bit value is divided to coarse and fine part of the integer (see for further details about
splitting 16-bit integer into two 8-bit integers). And now three characters ( "c", chr(int(c)), chr(int(f))) are
sent to Arduino, the first indicating the case, the second the coarse part, and the third part the fine part.
The receiving end in the Arduino sketch (bit10PWM.ino) looks like this then:
// The cases are given from the .py file (or some other frontend)
switch( byte( )) {
case 'r':
// now the intensity is encoded as two 8 bit integers
// so we need to recombine
// read the 8 bit ones
int highByte =;
int lowByte =;
// combine
// e.g.
unsigned int led_dutyCycle_Out = highByte * 256 + lowByte;

// Output the PWM
Timer1.setPwmDuty(pinOut, led_dutyCycle_Out); // setup pwm on pin X

Now the highByte is used for the coarse value, and lowByte for the fine value to introduce common
terminology for novice users (the variable names are also Arduino functions highByte() and lowByte()).

Screen capture of the Geany view of the code with the GUI for LED Control. Notice that now for each
slider value two bytes are sent for Arduino (coarse and fine bytes)
Now the PWM is updated inside the loop every time the user wants to change the LED intensity using the
library call .setPwmDuty.