I'm trying to make a mildly accurate oscilloscope using Arduino Uno R3 and I've done some research on the best method to do so. First of all I need to measure the voltage with a rather high sampling rate and then I'm going to transfer my data through the serial port and plot the data using Python or Matlab.

According to what I have read the analogread() function will cause some delay and therefore some inaccuracies and thus it is not suitable for my purpose.

I found on this website some good info about how I should write my code, but I have some questions:

  1. I want my reference voltage to be 1.1 V so that by dividing it to 1023 parts I will have an accuracy of about 1 mV. How should I edit the mentioned code in order to do so?

  2. The code in the void loop() setup is of no use to me (I prefer not to save the measured data on the Arduino and then send to my PC because of memory limitations). After deleting it will the data still be sent to the serial port?

  3. Will sending the data through the serial port affect my sample rate/accuracy? I mean how does it effect my accuracy?

P.S: by mildly accurate this is what I mean: I want to measure voltage accurately with 1 mV precision for frequencies of max. 500 kHz (a bit less or more won't hurt).

  • That’s an interesting question, even if only to define the limits of “mildly accurate” imposed by the Uno platform. You may need to break your coding down a piece at a time, asking all three questions at once may constrain the answers you receive. I think the analogReference() function may assist with the first question. arduino.cc/reference/en/language/functions/analog-io/…
    – RowanP
    Jun 25, 2022 at 10:31
  • @rowanp thanks for the help with analogreference. I will edit the question for better defining mildly accurate.
    – infinite
    Jun 25, 2022 at 11:52
  • When ever hardware is pushed to its limits I find one needs to be very familiar with both the hardware and software. Just for starters, consider Python is 100s if not over 1000 times slower then, say, compiled C. I say this even though it sounds like you are running Python on other than an Arduino. At the other end of the spectrum, even though the ADC has 10 bits this does not necessarily mean you can get 1024 steps of resolution for any single sample. Noise will likely reduce this considerably.
    – st2000
    Jun 25, 2022 at 15:31
  • @st2000 I understand that Python is much slower than C. So will using Matlab as the data analyzer on the other end give better results? since it is C-based or should I just test that?
    – infinite
    Jun 25, 2022 at 15:39
  • 2
    You won't get anywhere near 500 kS/s (kilosamples per second) with an Uno. The ADC accuracy degrades as you speed up its clock, and it becomes essentially garbage past 1 MHz (which gives 76.9 kS/s). See ADC conversion on the Arduino, by Nick Gammon. Jun 25, 2022 at 20:02

1 Answer 1


I want my reference voltatge to be 1.1v so that by dividing it to 1023 parts I will have the accuracy of about 1mv.

You will have a resolution of about 1 mV. The accuracy will be significantly worse than that due to the imperfections of the ADC (offset error, gain error, non-linearity) and noise.

how should I edit the mentioned code in order to do so?

The reference is configured by the bits REFS0 and REFS1 of the ADMUX register. The internal 1.1 V reference is selected by setting both bits, as shown in table 24-3 of the datasheet. You can then patch the original code like this:

@@ -8,7 +8,7 @@
   ADCSRA = 0;             // clear ADCSRA register
   ADCSRB = 0;             // clear ADCSRB register
   ADMUX |= (0 & 0x07);    // set A0 analog input pin
-  ADMUX |= (1 << REFS0);  // set reference voltage
+  ADMUX |= (1 << REFS0) | (1 << REFS1);  // set reference voltage
   ADMUX |= (1 << ADLAR);  // left align ADC value to 8 bits from ADCH register
   // sampling rate is [ADC clock] / [prescaler] / [conversion clock cycles]

the code in the void loop() setup is of no use for me (I prefer not to save the measured data on the arduino and then send to my pc because of memory limitations). after deleting it will the data still be sent to the serial port?

In the linked code, loop() is the only function sending anything to the serial port. If you remove it, nothing will be sent. You could Serial.write() in the ISR though, in which case an empty loop() would be fine.

Note that writing to Serial from within an ISR is generally discouraged. Your situation though (an oscilloscope code) is one of the very few cases where it does make sense to do so.

will sending the data through the serial port affect my sample rate/accuracy?

It can certainly affect your sampling rate. Serial.write() is usually non-blocking, as all it does is write the data to the RAM-based transmit buffer. If the buffer fills up, however, Serial.write() will block waiting for the serial port to actually send the data, and make enough room in the buffer.

This means that, in order for the oscilloscope to not miss samples, you have to make sure that the serial port can send the data at least as fast as the ADC is acquiring it.

Example calculation: If you clock the ADC at 1 MHz, you get one sample every 13 µs. You can follow the example given in the code you linked to, and discard the last two bits in order to transmit only 8 bits of the sample. Transmitting those in plain binary will take 10 “bits” worth of the serial port bandwidth (one start bit, 8 data bits and one stop bit). Each bit should then take less than 1.3 µs, which translates to a baud rate of 769,231 bits per second. You will probably have no other choice than configuring the serial port for 1 Mb/s.

If you want to transmit the whole 10 bits of the ADC readings, you will have to lower the sampling rate by a factor two.

At this point you may notice that the serial port, rather than the ADC, is the bottleneck for the performance of your oscilloscope. If this is too limiting, you may consider building a scope that works by bursts: it stores a burst of samples in memory, then sends it at a leisurely rate through the serial port.

  • 1
    You might want to consider using a Teensy 3.x or Teensy 4.x instead of an Arduino R3. Teensy 4.1 has a CPU speed of 600MHz vs R3 of 16MHz. Both use Arduino, and both are in the same price range
    – starship15
    Jun 28, 2022 at 19:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.