I have some testing I'd like to do that would benefit from an oscilliscope. Unfortunately, I don't have an oscilloscope, and there's no room in the budget to buy one at the moment. So I thought I'd see what kind of oscilloscope I can make out of an Arduino microprocessor.
I know an Arduino oscilloscope has been done any number of times before. This is just my version of the concept.
I originally thought I'd have to do some moderately complex programming to make the Arduino act as an oscilloscope. I figured I'd have to buffer readings, and have a communication protocol to transfer the data, set up trigger conditions, etc. Luckily, I decided to run some tests before I started the serious coding.
A quick test app reveals that the analogRead() function 112 or 116 microseconds to execute (the micros() function has a resolution of 4us, so the actual time is somewhere between those two values.) Serial.write() of a single byte takes only 8 microseconds.
The slow analog conversion time and the fast time to send a single byte gave me an idea: instead of having a complicated Arduino program to act as an oscilloscope, I could use an extremely simple program that just constantly streams measurements from an analog input pin. For my purpose, I doubt I need the full 10 bits of A2D resolution; 8 bits should be enough. This means that I don't even have to worry about synchronizing with the data stream from the microcontroller, since every byte is an valid independent reading. A sample rate of 5000 Hz (200 us between samples) would only be 40kbps, which should be easily handled by a 115200 baud rate.
Unfortunately, further testing revealed that a 5000 Hz sample rate caused the serial data to arrive in large chunks about once a second. I guess that some part of the serial line was being overwhelmed. I don't know if it was in the microcontroller, the PC, or in the FTDI USB to serial chip. Wherever the problem lies, a sample rate of 4000 Hz (250 us between samples) seems to cure the problem.
The resulting Arduino application is so simple that I'm going to post the whole thing here:
void setup()
{
// need fast serial rate to keep up with 1 byte data every 250 us
Serial.begin(115200);
}
void loop()
{
static uint16_t analogVal;
static uint32_t nextReadTime;
static uint32_t nowTime;
// start by calculating time that next read will take place
nextReadTime = micros() + 250;
// read analog value
analogVal = analogRead(0);
// send analog value (as single byte)
Serial.print((uint8_t)(analogVal >> 2), BYTE);
// wait for right amount of time to do next read
nowTime = micros();
if(nowTime < nextReadTime)
delayMicroseconds(nextReadTime - nowTime);
}
Pretty simple, isn't it?
Of course, now all the oscilloscope logic has to go in a program on the PC side.
I started trying to write an application in Python, pySerial, and pygame. It was going a little slowly though, so I ended up throwing togeather a quick program in C# instead. Here's what it looks like after a few hours of messing around:
In the first two pictures, the signal is just whatever is picked up with the analog input pin of the Arduino disconnected. It makes a nice sine wave for testing the display application.
The third picture is the output of a pin set to output PWM with a 25% duty cycle. The 4000 Hz sample rate of the scope is just barely enough to see the 500 Hz PWM waveform: each cycle gets 4 samples.
I don't know if this will actually be useful for anything, but it was fun to do. I got to play with microcontrollers and computer graphics at the same time!