This is documentation for controlling a behavioral experiment using an Arduino microcontroller using Python. The experiment can be controlled from a Python command line or with an easy to use web browser interface. Included in this system is an option to simultaneously record video using a Rapsberry Pi video camera. Our goal is to provide a starting point for open-source behavioral experiments that can be extended to new experimental designs.

Figure 1. Web-browser interface.

The top section provides an interface to start/stop a trial and plots real-time feedback as the trial is running.

The Stimulus section provides an interface to set stimulus parameters and to upload these parameters to an Arduino. This section also provides a plot of the stimulus waveform based on the current set of parameters.

System design

The core system consists of an Arduino, a stepper motor and motor driver, and a rotary encoder. The system comes together with a circular treadmill that can be driven by the stepper motor and whos current position is read using the rotary encoder.

The Arduino can be controlled with serial commands and can be triggered with general purpose digital input-output (GPIO) pulses. By relying on serial commands and GPIO pulses, this system is not dependent on the details of other pieces of equipment already in place for an experiment and can be extended to new and unique experimental configurations.

Using the provided Python code, an experiment can be controlled from a Python command prompt or a web browser interface.

Optionally, if this code is run on a Raspberry Pi, we provide Python code to simultaneously trigger and time-stamp video recording during an experiment. Please see our Trigger Camera documentation for a full dscription of how to integrate video recording into this system

We are routinely using this treadmill and video aquisition system while simultaneously acquiring in vivo two-photon images using ScanImage software (in Matlab) with National Instruments data acquisition cards.

See the parts list for a full list of parts, prices, and links to buy online.


The majority of Arduino compatible micro-controllers will work with the code provided. We suggest starting with an Arduino Uno. An good alternative Arduino compatible microcontroller is the Teensy. The Teensy is advantageous as it has a more powerful processor, provides more low level interrupts, and has more memory than an Arduino Uno.

Raspberry Pi

The Raspberry Pi is a fully functional credit-card sized computer that runs Linux. It comes with USB, Ethernet, Wifi, and HDMI ports. It can be used as a host computer to program an Arduino using the Arduino IDE and can easily run Python code. A unique feature of the Pi is that it has built in digital GPIO. Thus, a Raspberry Pi can send/receive GPIO signals to/from most laboratory equipment including an Arduino. The Pi can be equipped with a dedicated video camera (5MP or 8MP) that can be controlled from Python and can be (relatively) precisely triggered by GPIO pulses. Given its small footprint, a Raspberry Pi is easily integrated into the same electronics box as the Arduino.

Please note, the GPIO ports on the Rapsberry Pi are only 3.5V tolerant while most laboratory equipment (including many Arduinos and National Instruments boards) use 5V GPIO. Thus, a logic-level converter is needed to convert between 3.5V on the Pi to 5V on other equipment.

As the Raspberry Pi is running a full Linux operating system, the precision and reliability of the GPIO pins is lower than can be achieved using a dedicated microcontroller like an Arduino. Thus, we have built this system using a Teensy microcontroller as a 'pass through' device to record the precise timing of experimental events to then be compared to and to calibrate the performance of the Raspberry Pi.

Wiring the system

Here, we are wiring the system to interact with ScanImage software via National Instruments DAQ boards. This can easily be modified by wiring the system to other in-place acquisition systems such as those from Scientifica, Bruger (Prarie), Zeiss, or Nikon.

Keep in mind, the Raspberry Pi GPIO pins are not 5V tolerant. Use a 5V to 3.5V level shifter to wire the Raspberry Pi to 5V GPIO signals. A Teensy microcontroller is advantageous in that it will accept 5V GPIO input but only outputs 3.5V GPIO. Thus, a Teensy and Raspberry Pi GPIO pins can be directly wired without a level shifter.


See the images page and the treadmill section of the parts list.

Building the treamill is easy but finding the pieces to build with can be time-consuming and frustrating. A good starting point is to use Actobotics parts from ServoCity or Sparkfun. In particular, ServoCity, has made a useful set of visual guides and project ideas that are really helpful in designing hardware components. Structural components include: frames, rods, bearings, clamps, and motor mounts.

Upload code to the Arduino

The source code for the Arduino can be found in /arduino/src/treadmill.cpp.

Required libraries

Non-blocking Arduino libraries need to be used, otherwise the system will not perform well. Without non-blocking libraries, the code to turn the stepper motor will block other code like reading the rotary encoder and vica versa. Using these non-blocking libraries ensures that (but do not gaurantee) the stepper motor movement does not stutter and all the rotary encoder positions are logged.

Arduino IDE

Use the standard Arduino IDE to upload treadmill.cpp to an Arduino. Make sure the required Arduino libraries are installed. Be sure to activate additional low level interrupts if using an Arduino Uno.


Code can also be uploaded to an Arduino using PlatformIo. This has the distinct advantage that code can be compiled and uploaded from the command line on a headless computer including a Raspberry Pi. Please see this blog post to Install PlatformIO, compile code and upload it to an Arduino.

Low Level Interrupts

The Arduino Uno only comes with two pins (2 and 3) capable of low-level interrupts and more pins need to be enabled. Two low level interrupts are needed for the Rotary Encoder, another for a GPIO trigger, and another for GPIO pulses coming from a frame clock. See Pin-change interrupts for information on exposing additional pins as low-level interrupts.

We have included a compiler directive _expose_interrupts_ in the treadmill.cpp Arduino code that, if activated, will run code to expose the needed interrupts on an Arduino Uno.

//Uncomment this line if running on an Arduino Uno and compiling with the arduino IDE
//#define _expose_interrupts_ 1

Python server setup

Download and install Anaconda. Anaconda is a python installation that will install many commonly used libraries. It is much easier to get started with Anaconda rather than a basic installation of Python.

Python libraries

Install additional required python libraries using the included requirements.txt file

pip install -r requirements.txt

Here is the requirements.txt file


Required python libraries on Raspberry Pi

pip install -r raspberry_requirements.txt


Running an experiment

At its core, an experiment is run on an Arduino by interacting with treadmill.cpp through a serial port interface. In addition, a Python command line interface and a web based interface are provided.

These interfaces can be extended by directly interacting with the Arduino code in treadmill.cpp (with serial commands), the python code in, or the web server code in

Arduino serial interface

The Arduino code treadmill.cpp provides a set of serial port commands to get/set parameters and start/stop a trial. Once the code is uploaded to an Arduino, any serial port interface will allow control of an experiment as follows.

startTrial # start a trial
stopTrial # stop a trial
getState #

settrial takes the name and value of a trial parameter to set. The name needs to be one of: numPulse, numEpoch, epochDur, preDur, etc. These names match the 'Stimulus' parameters provided in the web interface. See the SetTrial() function in treadmill.cpp for all possible trial parameters.

Entering getState in a serial port interface and the Arduino will return the current values for all trial parameters. This is a good way to find the names of trial parameters and then set them. For example, settrial,epochDur,5000.

=== Arduino State ===
=== Done ===

Python interface

An experiment can be controlled from within Python by interacting with This includes interaction from a Python command line interface, a iPython/Jupyter interface, or custom written Python scripts. The Python interface and Arduino interface share all trial parameter names.

Here is an example of running an experiment from a Python script.

import treadmill
t = treadmill.treadmill() # create a treadmill object

t.startTrial() # start a new trial
t.stopTrial() # stop a trial

t.GetArduinoState() # get the current state with all trial parameters (see Arduino getstate serial command).
t.settrial('epochDur',5000) # set the value of 'epochDur' trial parameter to 5000 ms

t.startTrial() # start a new trial

Web interface

An experiment can also be controlled through a web browser interface. Run the web interface with python The code for this web interface, in, uses the Flask Python library. Flask is a micro-framework that allows a web-server to be created and controlled all from within Python. To make the web client interactive, we use SocketIO to communicate between the web client and the Flask python server.

Configuring the web interface

Change the default IP address and port of the web server in

Client side

The web interface is using a number of client and server libraries. See index.html and analysis2.html for client-side code.


We have provided a description of all the pieces necessary to construct a system to control an experiment using an Arduino from Python. By creating this system with open-source hardware and software, our aim is to lower the barrier of entry to get started with implementing custom built experiments.