Arduino Oscilloscope

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
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!
Post a Comment