You are on page 1of 9

instructables

Multichannel Arduino oscilloscope

by rgco

An oscilloscope shows how electric signals vary in time. It is an essential instrument for electronic design and
experiments that involve sensors or actuators.

High-end oscilloscopes that measure billions samples per second (Gs/s) or more can cost thousands of euros, but
one can also find oscilloscopes with lower speeds for much less.

The popular Arduino (Uno R3) microcontroller has a built-in analog-to-digital converted (ADC) to sample analog
electric signals and can thus be used as a rudimentary oscilloscope, when connected to a PC for displaying the
traces. Indeed you can find several examples of those here on instructables. However, I did not manage to find
one that requires no additional hardware, samples at the maximum speed of the Arduino ADC (77ks/s) and run on
both Windows, Mac and Linux.

The Arduino oscilloscope described in these instructions has the following highlights:

No extra hardware beyond an Arduino connected to a PC


Sample rate of up to 77ks/s
Up to six input channels
10-bit precision
Two vertical ranges (0-5V or 0-1.1V)
Trigger on rising or falling edge
Free running or one-shot operation
Integrated pulse generator
Display on computer screen using open-source processing sketch

Although the sampling speed is modest, its applications are many, to name a few:

Debugging electronic circuits with audio-frequency signals


Studying signal filters
Testing transformers and inductors
Testing and characterising sensors
Testing and characterising motors and other actuators
Multichannel Arduino oscilloscope: Page 1
Step 1: Setup

1. Download and install the Arduino integrated development environment (IDE)


2. Download and install the Processing IDE
3. Connect the Arduino to the PC with USB cable
4. Download the Arduino sketch ArduinoOscilloscope_v1_0.ino and upload to the Arduino (the
warning about low memory can be ignored)
5. Download the Processing sketch ArduinoOscilloscope_v1_0.pde and run it
6. Connect pin D9 to A0 on the Arduino

The last step sends the signal from the pulse generator to the first channel. You should now see on your computer
screen a window displaying a 1kHz signal, similar as in the picture above. The signal will not be stable since the
readout rate does not in general correspond to an integer number of pulses. Take a single shot or activate the
trigger (next step) to stabilise the trace.

Multichannel Arduino oscilloscope: Page 2


http://www.instructables.com/ORIG/F6Z/KK90/IZ6BKQ5Y/F6ZKK90IZ6BKQ5Y.ino

(https://cdn.instructables.com/ORIG/F6Z/KK90/IZ6BKQ5Y/F6ZKK90IZ6BKQ5Y.ino)

http://www.instructables.com/ORIG/F6P/5WJQ/IZ6BKQ7B/F6P5WJQIZ6BKQ7B.pde

(https://cdn.instructables.com/ORIG/F6P/5WJQ/IZ6BKQ7B/F6P5WJQIZ6BKQ7B.pde)

Step 2: Instructions for use

Multichannel Arduino oscilloscope: Page 3


The main screen with the traces is divided in divisions point where the signal goes either above (rising edge)
(div), which are then divided in subdivision with or below (falling edge) a certain threshold. The
thinner lines. The time scale (ms/div) and voltage threshold is indicated with the little arrow on the left of
scale (V/div) refer to the full divisions. The the screen. You can set it higher or lower by clicking
subdivisions are there to improve the precision of (not dragging!) at any other height above or below the
time and voltage reading from the main screen. arrow. The little arrow on the top of the screen is the
trigger offset. Anything to the left of the arrow
By clicking on various parts of the screen the settings happened before the trigger, anything to the right of
can be changed: the arrow happened after the trigger. To go back to
triggerless running, click on the (rising or falling)
Free running or single shots trigger symbol that is highlighted in grey. It will go turn
black again and the trigger will turn off.
By default, the display continually updates the signal.
If instead you want to freeze the signal, click on shot Time scale settings
and it will make a single sweep which then can be
studied quietly. To take another sweep, click shot The fastest possible readout of 77ks/s is selected by
again. To go back to free running, click run choosing 1ms/div. To see slower signals over longer
time spans, a larger value of ms/div can be chosen.
Channel selection This will slow down the sampling rate to 35.5ks/s for
2ms/div, 15.5 ks/s for 5ms/div etc. For the slowest
The displayed channels are highlighted in grey. Click settings, the update frequency of the screen will go
on the numbers 0-5 to select or deselect additional down, simply because it takes more than 1,5s to
channels. The channels 0-5 take their input from the make a full sweep at 100ms/div. Beware that the at
Arduino pins A0-A5, respectively. Beware that the the fasted speed, 1ms/s, the Arduino ADC cannot
Arduino internally has only one ADC, so if you select switch fast enough between channels, and the multi-
more than one channel, it rotates the measurements channel readout gives distorted signals in case the
between the different channels, thus reducing the signals differ strongly. Revert back to either single-
actual sampling rate on each channel. channel operation or slower readout speeds.

Trigger settings Number of samples

Next to the channel number are two symbols, for The default number of samples is 1200. That is close
rising edge and falling edge respectively. Click on any to the maximum that the Arduino can store in its 2kB
of these to activate edge-triggering. This will stabilize RAM memory. This number can be reduced to 600 or
the signal because it fixes the time reference to the 300 to zoom in on very fast signals or to increase the

refresh rate of the display. The Arduino has integrated hardware timers that can
be used to generate signals. Here, output A of timer 1
Vertical scale settings is used, which gives pulsed signals on pin D9. This
signal can be directly fed to any of the analog inputs
The Arduino has two voltage references that can be to test the oscilloscope, or it can be used to activate
used without additional hardware: the 5V USB power other components (for example an RC filter), whose
or an internal 1.1V reference voltage. By default the output can then be studied on any of the other analog
full scale refers to 5V (divided into 5 divisions of 1V inputs. The pulser bar has four rows. The upper two
each), but to zoom in on smaller signals, click on rows are to select the period/frequency and the lower
0.2V, which will reduce the full scale to 1.1V. two rows to select the pulse length/duty cycle.

Pulser on pin 9

Multichannel Arduino oscilloscope: Page 4


Step 3: Technical notes about the implementation

Here I mention some of the considerations for the acquisition or free running mode. The standard
technical implementation. Feel free to skip to the next analogRead() function does a single acquisition and
sections with example measurements. returns the value when the ADC is ready. Even with a
faster ADC clock, this can never result in the fastest
ADC clock speed possible measurement, since some processing time
is needed in between measurements. It is also
The ADC is of the successive approximation type and possible to start the ADC, do some processing, wait
needs 13 ADC-clock cycles to complete a for the ADC to be ready and then start a new
measurement. The 16MHz Arduino clock is too fast measurement. However, this approach will result in a
for the ADC to operate properly, therefore it needs to minimum of 14 ADC-clock cycles per measurement.
be prescaled. Prescale factors of 1 (no prescale) to Here, the free-running mode is selected, which is the
128 can be selected. By default, the Arduino IDE sets fastest. The challenge is to synchronize the ADC
the prescale to 128, resulting in an ADC-clock period measurement with the storage and processing (e.g
of 128/16MHz=8 microseconds, and thus a sampling triggering, multiplexing). It is possible to use interrupts
period of 13*8=104 microseconds. Many tests have when the ADC measurement is ready, but interrupts
shown reducing the prescale to 16 results in only a have a small CPU overhead and can be difficult to
slight loss of precision, while even faster clocks result program. Instead, it is also possible to use a loop and
in garbage. Therefore the fastest possible poll for the ADIF flag which indicates that the ADC
measurement, at the 1ms/div setting, corresponds to reading is complete, even if no interrupts are called.
an ADC prescale of 16, and ADC-clock of 1MHz, a
sampling time of 13 microseconds and a sampling Signal storage
speed of 1/13 microseconds=76.92ks/s. At the
2/5/10ms/div settings the prescale is increased to 32, A circular buffer allows to visualize signals before the
64,128, respectively. Thus the full 10-bit precision is trigger, an extremely useful feature. Thus the samples
supposed to be achieved for time scales of 10ms/div are stored continuously and then continue for less
and above. But even at 1ms/div the traces look good than a full cycle after the trigger. The limited RAM of
with little noise. the Arduino is another issue. 1200 samples in 8-bit
take up 1200 bytes. To get 10-bit precision, we
ADC sampling cannot store the 2 extra bits in a byte each, as that
would take 2400 bytes. Instead, the LSBs of 4
The Arduino ADC can be operated in single measurements can be packed into a byte, resulting in

Multichannel Arduino oscilloscope: Page 5


a total of 1500 bytes, which does fit. Some attention continue by the total number of samples minus the
is paid to use low-level fast bit-shifting operations to trigger offset, so that samples from before the trigger
pack the bits. When sending to the PC, the bits are are not overwritten and can be displayed.
rearranged into 6+6 to avoid the number 255, which
is used to indicate a new sample. Pulse generation

Processing between measurements The built-in hardware timers are great to generate
pulses so that the same Arduino can also be used as
The CPU time between ADC measurements is very a pulse generator. There is no CPU cost since the
limited: 13x16=208 clock cycles are available to store pulse generation can be done simply by setting the
the signal in a circular buffer, switch between signals timer registers outside of the measurement loop.
and check for the trigger. However, it seems to be Timer1 is the most powerful timer: it has 16-bit
doable, paying attention to the following: Disable precision and has an operating mode known as
interrupts. By default, timer0 generates an interrupt Phase and Frequency Correct PWM mode, which
every ms to update the values used in millis() and allows to set independently the frequency and the
micros(). These take too long and result in missed duty cycle.
samples. Use the smallest possible data types (byte
for any number in the range 0-255) Use local Communication between the Arduino and the PC
variables where possible: they can be stored in fast
registers Avoid divisions and the modulo operator. The communication goes over the serial link, using
For a circular counter is is much faster to reset the the highest possible baud rate (115200 b/s). It is set
counter when the top is reached. Order if-statements up that the Arduino will only generate a sweep when
in such a way that the most unlikely condition is instructed to do so by the PC. There is no formal
tested first handshaking and it doesnt seem fully safe to me: the
number 255 is used to indicate the beginning of a set
Channel multiplexing of commands, but the number 255 can also appear
as part of the command. It is hoped that the strict
The oscilloscope allows arbitrary channel selection, checking of the 30-byte command removes most of
and the ADC multiplexer must switch after each the corrupt commands.
measurement. To speed this up, a lookup table of 6
entries which contain the next channel is used. This
table can be generated outside the measurement
loop. The ATMEGA328P datasheet mentions The
user is advised not to write new channel or reference
selection values during Free Running mode.
However it seems to give few problems in practice.
The only issue Ive seen is at the highest sampling
speed, when multiplexing between signals that differ
strongly in voltage level, the signals seem pulled
towards each other, as if there is a follower that
cannot keep up with the fast change in level.

Triggering

There is not much time for processing the signal to


check for triggering, however, a simple edge trigger is
feasible: the latest measurement is compared to the
previous one, and if one is above the trigger and the
other below, a trigger can be fired. The sampling will

Multichannel Arduino oscilloscope: Page 6


Step 4: Example 1: 50Hz pickup

The electric cables that supply power to our Note how the signal is asymmetric. Since the
household appliances create electric and magnetic coupling is capacitive, it is purely alternating current
fields that are easily picked up with an antennal. For (AC), therefore it is as often negative as positive.
the high-impedance analog input of the Arduino, a Here we see a limitation of the Arduino oscilloscope,
short single wire will show up this signal easily. which can only measure signals positive with respect
to ground. Negative signals are simply clipped off at
Attach a wire of ~10cm to the A0 input and let the end zero. Note also that the distance between the peaks
float in the air. On the oscilloscope, lower the trigger is precisely 20ms, indicating that the net frequency is
threshold to ~1V, select the upgoing trigger and a indeed 50Hz and that the time scale of the Arduino
timescale of 10ms/div. You may see something oscilloscope is precise
similar as in the picture, but the precise image will
depend strongly on the presence of conducting
bodies nearby and on how they are grounded.

Step 5: Example 2 : capacitive coupling

The capacitive coupling from the net also applies to Attach a ~10 cm wire to the pulse output of pin D9
signals that we generate ourselves. Two wires that and another ~10cm wire to the input of A0. Get the
run close to each other will influence each other even wires really close together, for example by twisting
if there is no direct electrical contact, just as if there is them together. Set the oscilloscope to a timescale of
a small-value capacitance between the two. The 2ms/div. You should get an image similar to the one
effect is strongest at high frequencies, but can also on the picture. In addition to the 50Hz net pickup,
be demonstrated at the default pulser frequency of there is now clearly visible a 1kHz square wave from
1kHz. pin D9.

Multichannel Arduino oscilloscope: Page 7


Step 6: Example 3 : low-pass RC filter

Resistor-capacitor (RC) filters are about the simplest the pulser on D9 and the ground. Connect input A0
signal filters that one can implement. Depending on directly to the pulser and A1 in between the resistor
the configuration, they can pass either the low and the capacitor, as in the picture and in the
frequencies (low-pass) or the high frequencies (high- schematics shown.
pass). More complex configurations can filter away
both high and low frequencies (band-pass filter). The First we will see the response of this filter for signals
main parameter of an RC filter is the characteristics with a frequency well below the characteristic time
time, which is simply the product of the resistor value tau=RC=1ms. Therefore, select the pulser to fire with
in Ohm with the capacitor value in Farad. In this period 20ms and display both traces 0 and 1,
example a resistor of 100kOhm and a capacitor of triggering on the rising edge of A0. Set the timescale
10nF is used, corresponding to a characteristic time to 5ms/div. The red curve shows the output of the
of tau=RC=10^5Ohm x 10^-8F =10^-3s, or 1 ms. filter and shows how it follows the input with some
using a 10kOhm resistor with a 0.1muF capacitor will delay: after every step, it takes a couple of ms to
give the same results. reach the level of the input.

For setups that require additional components, it is A low-pass RC filter can also be used to transform a
really handy to use a prototype shield for the Arduino: modulated signal into a constant voltage. The second
you stick it on the top and on the mini-breadboard you screenshot of the oscilloscope traces shows the
can place the components, connect them between response to a pulser frequency of 2kHz and a 20%
each other and with the Arduino pins. But also duty cycle. The output of the filter is centreed around
connecting it to a regular breadboard will be fine. 1.0V (20% of 5V) with a saw-tooth ripple of circa
0.2V.
Connect the resistor and capacitor in series between

Multichannel Arduino oscilloscope: Page 8


Multichannel Arduino oscilloscope: Page 9

You might also like