This software allows you to get a visual representation of an analog signal using Arduino and Processing. The resolution is 10 bits so this is not like a
real oscilloscope but it is still pretty useful.
It works by sending values read from the Arduino board (pin 0) to Processing through serial communication.
Note: This simple idea inspired a more powerful software which we strongly recommend if you need to read several outputs at once: the
Arduinoscope.
Ingredients
Instructions
- Upload the Arduino code to your board (the code is included at the end of the .pde in attachment to this post)
- Preferably close the Arduino program (I think it can cause interferences during serial reading)
- Run the Processing code
Scope anything you want
Sometimes, you don't want to just scope the analog input: you want to scope a modified version of it, for instance. In that case, you can use include the oscilloscope.h file and send any value you want to the Processing program by using the writeOscilloscope(int) function.
Real scopes are only 8 bits
"The resolution is 10 bits so this is not like a real oscilloscope"
Sure it is. Scopes are only 8 bits. They just use low-noise, variable-gain circuitry to measure a huge range of values.
It's the very low sampling frequency that makes this a "poor man's" scope. Real scopes go up to the MHz.
I have 1 black screen
Hi, sorry asking newbie qn. But i am currently trying to measure the oscillation of a Fluoresecent Lamp running on 220V AC power. I've used a voltage divider setup to step down the voltage to less then 5V. However, is it advisable to measure a 220V source?
Moreover, i hadnt connect the AC to my board circuit yet, and i am getting a plain dark screen from Sketc_nova when i run the codes. Is that suppose to happen?
CUIduino?
I'm wondering, could the sample rate be improved via USB? Using the CUI and HID drivers, mapping analog inputs to joystick X/Y/Z/Throttle etc.... I'm curious.... Or is it simply a limitation of the arduino's processing/sampling power?
Mitul from Technology Blog
You have a nice blog … I liked your blog very much. The contents are worth reading and I’ve found much valuable information from your post. My learning curve is increasing :-). Thanks for sharing such a nice post with us. Keep it up.
Screenshot
Here's my screenshot:

See this:
http://code.google.com/p/arduinoscope/wiki/Usage
If you 'd like info about configuring or changing it to do other things.
Re: hi
i have used your code,i have placed the controlip5 in the right folder also,
but i get error as insufficient memory,wen i change it to 2GB in preferences, i get error as memory too high,finally i set it to 1.2 GB,where i get no error but i dont see the output
kindly help
Will not compile
Is there any reason why when I compile the Arduino code it gives me, "In function 'void setup()':
error: redefinition of 'void setup()' In function 'void loop()':"?
Careful
Careful, the .pde code is for Processing and will not compile. You should simply cut/paste the commented (Arduino) code at the end and put it in an Arduino file.
Using the basic code i can only get very low frequency signals
Using the basic code i can only get very low frequency signals. I used a function generator on analog pin 0 starting at 1kHz with a square wave with an amplitude of 2 volts and the oscilloscope was unable to process this i had to drop it to 10hz before i seen the waveform. Seen as you have down extensive work on this code why doesnt it work at 1kHz and what is the reason for it running at only very low frequencies?
Low Sample Rate
If you look at the code at all, you can see that the Arduino is transferring across RS232 at 9600 baud. The code uses two bytes per sample to carry the 10 bits of A/D precision. RS232 (typically) takes 10 bits on the wire to transmit 8 bits of data.
9600/10 gives us the byte rate of the serial channel: 960 bytes per second
960/2 gives us the sample rate: 480 samples per second
480/2 gives us the absolute maximum bandwidth of the system, or 240Hz. (This also assumes that the peaks of the measured signal align nicely with the sample rate.)
The ATmega chips have rather limited sample rates as well. They're not designed for this sort of work. They use a "successive approximation" type A/D that samples one bit at a time. At 10 bits of resolution, ATmel lists 15k samples per second as the maximum sample rate. This gives an absolute upper limit of 7.5kHz bandwidth at that resolution. That sample rate doesn't actually appear to allow time for actual code to run to DO anything with that data. A more realistic limit for the is about 3.8kHz.
The A/D converters onboard the ATmega chips are designed for low rate sampling, such as a few samples per second of a given voltage or temperature.
My version
I made a version that is easier to work with in any GUI (IMHO) and an example app that reads 6 analog pins.
Here are some features:
logic analyzer mode that shows 1's and 0's clearly.
pause frame
save frame
configurable pin-count
use as many pins as will fit on screen (tested with 12 at 800x800, seems ok)
shows volts, based on scaling settings
I set it up to be useful as a logic analyzer for digital pins, or oscilloscope for analog.
http://code.google.com/p/arduinoscope/
I'd like to add more features like extended frame logging (continuous) and detecting bytes in logic probe view, like SPI/i2C. I'd also like to add a parallel mode, for reading 8-pins at a time into a single byte.
picture of 6 channel version
In case you're wondering what it looks like, this is a screen capture.
http://www.mittsonline.com/6_ch.gif
6 channel version
//Have fun.
import processing.serial.*;
Serial port; // Create object from Serial class
int valA;
int valB;
int valC;
int valD;
int valE;
int valF;
// this should have been some kind of 2-diminsional array, I guess,
but it works.
int[] valuesA;
int[] valuesB;
int[] valuesC;
int[] valuesD;
int[] valuesE;
int[] valuesF;
PFont fontA;
void setup()PFont fontB;
{
// make sure you have these fonts made for Processing. Use
Tools...Create Font.
// "fontA" is a 48 pt font of some sort. It's what we use to show
the "now" value.
fontA = loadFont("CourierNewPSMT-48.vlw");
// "fontB" is a 14 pt font of some sort. It's what we use to show
the min and max values.
fontB = loadFont("CourierNewPSMT-14.vlw");
// I wouldn't change the size if I were you. There are some
functions that don't use
// the actual sizes to figure out where to put things. Sorry about that.
size(550, 600);
// Open the port that the board is connected to and use the same speed
// anything faster than 38.4k seems faster than the ADC on the
Arduino can keep up with.
// So, if you want it to be smooth, keep it at or below 38400. 28800
doesn't work at all,
// I do not know why. If you turn on smooth() you need to drop the
rate to 19.2k or lower.
// You will probably have to adjust Serial.list()[1] to get your serial port.
port = new Serial(this, Serial.list()[1], 38400);
// These are 6 arrays for the 6 analog input channels.
// I'm sure it could have just as well been a 2d array, but I'm not
that familiar
// with Processing yet and this was the easy way out.
valuesA = new int[width-150];
valuesB = new int[width-150];
valuesC = new int[width-150];
valuesD = new int[width-150];
valuesE = new int[width-150];
valuesF = new int[width-150];
// the -150 gives us room on the side for our text values.
// this turns on anti-aliasing. max bps is about 19.2k.
// uncomment out the next line to turn it on. Personally, I think
it's better left off.
//smooth();
}
int getY(int val) {
void draw()// I added -40 to this line to keep the lines from overlapping, to
// keep the values within their gray boxes.
return (int)(val / 1023.0f * (height-40)) - 1;
}
{
String decoder = "";
while (port.available() >= 3)
{
// read serial until we get to an "A".
decoder = port.readStringUntil(65);
}
// sanity check. make sure the string we got from the Arduino has
all the values inside.
if ((decoder.indexOf("B")>=1) & (decoder.indexOf("C")>=1) &
(decoder.indexOf("D")>=1) & (decoder.indexOf("E")>=1) &
(decoder.indexOf("F")>=1))
{
// decoder string doesn't contain an A at the beginning. it's at the end.
valA=int(decoder.substring(0,decoder.indexOf("B")));
//println("A" + str(valA));
valB=int(decoder.substring(decoder.indexOf("B")+1,decoder.indexOf("C")));
//println("B" + str(valB));
valC=int(decoder.substring(decoder.indexOf("C")+1,decoder.indexOf("D")));
//println("C" + str(valC));
valD=int(decoder.substring(decoder.indexOf("D")+1,decoder.indexOf("E")));
//println("D" + str(valD));
valE=int(decoder.substring(decoder.indexOf("E")+1,decoder.indexOf("F")));
//println("E" + str(valE));
valF=int(decoder.substring(decoder.indexOf("F")+1,decoder.indexOf("A")));
//println("F" + str(valF));
}
//shift the new values into the array, move everything else over by one
for (int i=0; i<width-151; i++) {
valuesA[i] = valuesA[i+1];
valuesB[i] = valuesB[i+1];
valuesC[i] = valuesC[i+1];
valuesD[i] = valuesD[i+1];
valuesE[i] = valuesE[i+1];
valuesF[i] = valuesF[i+1];
}
// -151 because the array is 151 less than the width. remember we
// saved room on the side of the screen for the actual text values.
valuesA[width-151] = valA;
valuesB[width-151] = valB;
valuesC[width-151] = valC;
valuesD[width-151] = valD;
valuesE[width-151] = valE;
valuesF[width-151] = valF;
background(0);
textFont(fontA);
// I'm sure these c/should have been determined using height math,
but I don't have the time really.
// Draw out the now values with the big font.
text(valA + 1, (width-140), 108-5);
text(valB + 1, (width-140), 206-5);
text(valC + 1, (width-140), 304-5);
text(valD + 1, (width-140), 402-5);
text(valE + 1, (width-140), 500-5);
text(valF + 1, (width-140), 598-5);
textFont(fontB);
// Draw out the min and max values with the small font.
// the h value (30,128,266,etc) is a function of height,
// but I didn't bother to actually do the math.
// I guess it's (98*n)+30 where n is 0,1,2,3,4,5, but I don't know
// exactly how height (600) relates to 98... ((h/6)-2??)
drawdata("0", width-90, 30, valuesA);
drawdata("1", width-90, 128, valuesB);
drawdata("2", width-90, 226, valuesC);
drawdata("3", width-90, 324, valuesD);
drawdata("4", width-90, 422, valuesE);
drawdata("5", width-90, 520, valuesF);
for (int x=150; x<width-1; x++) {
// next line adjusts the color of the stroke depending on the x
value. (fades out the end of the line)
check(x,255,0,0);
// next line draws the line needed to get this value in the array
to the next value in the array.
// the offsets (6+ in the next line) were used to get the values
where I wanted them without
// having to actually do real spacial math. There's a hack in getY
that offsets a little, too.
line((width)-x,
6+((height/6)*0)+((height-1-getY(valuesA[x-150]))/6), (width)-1-x,
6+((height/6)*0)+((height-1-getY(valuesA[x-149]))/6));
check(x,0,255,0);
line((width)-x,
4+((height/6)*1)+((height-1-getY(valuesB[x-150]))/6), (width)-1-x,
4+((height/6)*1)+((height-1-getY(valuesB[x-149]))/6));
check(x,0,0,255);
line((width)-x,
2+((height/6)*2)+((height-1-getY(valuesC[x-150]))/6), (width)-1-x,
2+((height/6)*2)+((height-1-getY(valuesC[x-149]))/6));
check(x,255,255,0);
line((width)-x,
0+((height/6)*3)+((height-1-getY(valuesD[x-150]))/6), (width)-1-x,
0+((height/6)*3)+((height-1-getY(valuesD[x-149]))/6));
check(x,0,255,255);
line((width)-x,
-2+((height/6)*4)+((height-1-getY(valuesE[x-150]))/6), (width)-1-x,
-2+((height/6)*4)+((height-1-getY(valuesE[x-149]))/6));
check(x,255,0,255);
line((width)-x,
-4+((height/6)*5)+((height-1-getY(valuesF[x-150]))/6), (width)-1-x,
-4+((height/6)*5)+((height-1-getY(valuesF[x-149]))/6));
}
// draw the boxes in gray.
stroke(170,170,170);
// these 5 lines divide the 6 inputs
line(0,108,width-1,108);
line(0,206,width-1,206);
line(0,304,width-1,304);
line(0,402,width-1,402);
line(0,500,width-1,500);
// these four lines make up the outer box
void drawdata(String pin, int w, int h, int[] values)line( 0, 0, width-1, 0); // along the top
line(width-1, 0, width-1, height-1); // down the right
line(width-1, height-1, 0, height-1); // along the bottom
line( 0, height-1, 0, 0); // up the left
}
{
text("pin: " + pin, w, h);
void check(int xx, int rr, int gg, int bb)text("min: " + str(min(values) + 1), w, h + 14);
text("max: " + str(max(values) + 1), w, h + 28);
}
{
// floating point operations in Processing are expensive.
// only do the math for the float (fading out effect) if
// we have to. You can change 170 to 160 if you want it to
// fade faster, but be sure to change the other 170 to 160
// and the 20 to 10.
// (20 is the difference between 170 and 150)
if (xx<=170)
{
float kick = (parseFloat(170-xx)/20)*255;
// invert kick so the brighter parts are on the left side instead
of the right.
stroke(rr,gg,bb,255-kick);
}
else
{
stroke(rr,gg,bb);
}
}
/*
This is the Arduino Code:
void setup() {
Serial.begin(38400);
}
void loop() {
int val[5];
val[0] = analogRead(ANALOGA_IN);
val[1] = analogRead(ANALOGB_IN);
val[2] = analogRead(ANALOGC_IN);
val[3] = analogRead(ANALOGD_IN);
val[4] = analogRead(ANALOGE_IN);
val[5] = analogRead(ANALOGF_IN);
Serial.print( "A" );
Serial.print( val[0] );
Serial.print( "B" );
Serial.print( val[1] );
Serial.print( "C" );
Serial.print( val[2] );
Serial.print( "D" );
Serial.print( val[3] );
Serial.print( "E" );
Serial.print( val[4] );
Serial.print( "F" );
Serial.print( val[5] );
}
Missing code
Hi,
Not sure whats happened but the code for 6 inputs is not fully listed, any chance you can send it on? ian.macdonald@aibh-ye.net
Thanks
Ian
Sorry, it is my fault. I put
Sorry, it is my fault. I put it back properly.
Is there a reason why the
Is there a reason why the bps rate is limited to 9600? Wouldn't it make more sense to go higher- say 115,200?
Max frequency?
Hi,
This is cool.
I guess that in this case the max input frequency is restricted by the serial output...
Couldn't be possible to catch 1K values and send them?
In this case... how many samples per second would be achievable? Anyone?
Resolution of the arduino's
Resolution of the arduino's ADC is 4.9 mV, worst-case sampling rate is approximately 10kHz (if not using serial communication at the same time).
hi, I'm very interested in
hi, I'm very interested in knowing how you calculated the worst case sampling rate. To me the only available spec (which is a bandwidth of 115200bps) really don't mean anything in real-world terms.
Interesting!
Posted and translated into Spanish for my Blog. Thank you!
www.albinoblackcrow.es
Great! Muchas gracias!
Great! Muchas gracias!
displaying a grid with values
Hi there,
I'm not very familiar with Processing.
Could I expand your sketch to see a grid or the peak values?!?
Maybe some lines for the timbase would also be very helpful.
Thanks for your great sketch!
And regards from Germany,
jo
Works Great...Thanks!
This is a screen shot of your oscope displaying the output of a photo resistor aimed at an LED flashing a 1kHz.
Thanks for the work!
Are you sure about that frequency?
1khz? How did you managed to get this scope running so fast? I'm barely getting one sample per second :(
There's probably a problem
There's probably a problem with you setup. Did you upload the exact same program? Try using a higher baudrate.
D.A Vajvod.
It'is wery fun!
1 KHz for Oscilloscope!!!
I seem that ADC (ex. AD 7810 -100 KHZ) + MCU is many better than built-in ADC.
Sorry, bad english)))
+2 pins?
Thanks for the code, it works great! I wanted to do some more measuring. How can I go about measuring (and displaying) voltage from more than just pin 0? For that matter, any other colors available on the display? I'm a little light on processing/wiring.
Excellent
Thanks for this, what a great addition to my desk!
Easty.
5V limit?
I am not an electronics guru, so bare with me. If I am correct, there is a 5V limit on the analog pin. It would be nice if maybe 20V could be used. Any idea with what components you could create some kind of reference voltage. To make this more clear is, if the max voltage that can be applied is 20V, then I would like to have this converted to 5 V, 18V is then 4.5V (18*5/20), 12V is then 3V.
Would be nice if this is doable.
look-up "voltage divider" --
look-up "voltage divider" -- two resistors.
Simple
V=IR
Fantastic! (after a small change to the code)
Brilliant setup, marvellously simple. Reminds me of a project I did for A-level with a PIC, that had a lower resolution still (8 bits, and at half the refresh rate as I had 2 inputs) to make like a graphics digitiser with a couple of pots measuring angles.
The Processing application is the key here, you've got very smooth plotting going on there, which is fantastic.
To those of you complaining it doesn't work:
Download the file and open it in Processing, then scroll down to the bottom of it, copy the code in the bit marked "// The Arduino code." into a new Arduino project and send that to your board. I then also had to look in the Tools -> serial port menu in the Arduino application to determine which serial port I was using. My port is the 5th one down, so that's number 4 (you start counting at 0). Then I went back to the Processing code (after quitting the Arduino application) and changed the line:
port = new Serial(this, Serial.list()[0], 9600);
so it read:
port = new Serial(this, Serial.list()[4], 9600);
Then after clicking Run in the toolbar it worked fine. :-)
Planning any additions or is this just proof of concept? I'd love to see some sort of scale, some controls to change refresh rate/voltage divisions?
Looking good though!
So pretty
Thanks for your comments: they helped me running this nice litlle project.
May I kindly complement them with this. If you have (like me, but this is an exemple) COM1, COM8 and COM9 active, and your Arduino is connected to this COM9, the serial port index is... 2. Read it as "this is the third active serial COM port countent in base 0".
Happy scoping
Didier (The Arduino nerd)
Error when trying to compile in arduino 0011
Has anyone tried compiling this yet? I get an error on the first line.
error: 'import' does not name a type In function 'void setup()':
In function 'int getY(int)':
In function 'void draw()':
That's what I thought at first too, but...
After reading the instructions on this page more carefully, I noticed that the code he gave isn't for the Arduino environment at all; it was for a program called "Processing 1.0.3". I downloaded the program from their website, followed his instructions with uploading code to the Arduino through the environment, then after running the code on Processing and changing the Serial port index from 0 to 1, it was working fine.
Compile in Processing
Firs part off the code is to compile in Processing!
Find "arduino code", and compile in arduino just downn code!
awesome
I'm going to try this when I get back to the bench. What do you use for 10-bit serial to the mac?
Awesome work posted
Excelent work! I have posted it (translated to spanish) in my blog:
http://www.bricogeek.com/index/cat/13/nid/1327/
I'll keep tuned on your project. Best regards!
gotta try this
actually, this is "multiplatform" if you have a usb arduino :)
?
Where's the processing code. I see the arduino project/sketchbook file,but not this processing code you speak of.
? - answer
they are both in the same file.
the Processing code first, than the Arduino code.
good luck.
S.
Made the small changes WORKS
Thanks for the update. I'm a bit of a arduino noob.
Woot!