Extremely inconsistent analogRead() on Nano 33 IoT

One of my Nano 33 IoT projects incorporates reading the position of a potentiometer--pretty standard stuff. I noticed that the analogRead() values seemed a bit more jumpy than other Arduino boards I'm familiar with. I uploaded a simple program to print out the analogRead() value and removed all the components except a wire connecting the analog pin to ground. I found the value couldn't even stay at 0 for more than a few lines at a time and would frequently jump as high as 25.

I did some research and came across similar issues with these boards, such as the voltage issues described here: https://forum.arduino.cc/index.php?topic=650488.0 From this, and similar posts, I would expect the Arduino to at least give somewhat consistent readings when plugged into USB, but I can't even get that. I must say that I'm disappointed in the quality of the boards. I expected more from a first party Arduino board.

The one thing I have yet to find a solid answer to, which is why I'm posting this -- what can I do about it? I know I have seen people mention adding filtering code to some degrees of success. Is that the best option? Should I add a capacitor somewhere? Is there an alternate board that doesn't have these issues?

Thanks,
Andrew

Some things will depend in the exact parts used and the wiring.
Most Arduinos can be prone to external noise so good cable practices should be employed such as screened cable and as with guitar pots a ground to the pot case if it is metal.
Always use shortest cable runs where possible or add small ferrites on longer runs.

A stable and consistent voltage and current is a must and should also take into account the requirements of any other attached devices that may pull or cause a small voltage spike.
If you have anything like that connected then a capacitor on the supply line to that device may help smooth out the spikes.

Could you also take a few moments to Learn How To Use The Forum.
It will help you get the best out of the forum in the future.

Posting tips.

  • Your OS and version can be valuable information, please include it along with extra security you are using.
  • Always list the version of the IDE you are using and the board version if applicable.
  • How to insert an image into your post. ( Thanks Robin2 )
  • Add your sketch where applicable but please use CODE TAGS ( </> )
  • Add a SCHEMATIC were needed even if it is hand drawn
  • Add working links to any specific hardware as needed (NOT links to similar items)
  • Remember that the people trying to help cannot see your problem so give as much information as you can

Bob.

The ADC of the SAMD21 microcontroller on the Nano 33 IoT has a differential input. Theoretically you should be able to measure your signal against the boards voltage and therefore remove the ripple.

If you like to try that I would be happy to help test some code, if you can get started.

Klaus_K:
The ADC of the SAMD21 microcontroller on the Nano 33 IoT has a differential input. Theoretically you should be able to measure your signal against the boards voltage and therefore remove the ripple.

Thanks! I was hoping you'd reply since you seem knowledgeable of the board (I just wish I had remembered to look back at this thread sooner).

That actually makes a lot of sense why I was getting those strange readings; I assumed the ADC worked like the Uno's. How do I set the other pole of the differential input? I'm using two pots, so do I need to have two additional lines to ground/vcc? Which pin(s) do I need to connect?

Thanks,
Andrew

I did a few experiments and even though I managed to get some differential readings there is still a lot of noise in the result. The ADC has a lot of settings and features I did not try and I do not feel like spending a lot of time on that issue.

If you like to try. You will need to have a look into the following file

C:\Users\UserName\AppData\Local\Arduino15\packages\arduino\hardware\samd\1.8.4\cores\arduino\wiring_analog.c

I created a copy of the analogRead function and modified that.

There are some disadvantages in using the differential input. One is that the voltage range is smaller. So there is some additional complications for something that should be a lot simpler.

Klaus_K:
I did a few experiments and even though I managed to get some differential readings there is still a lot of noise in the result. The ADC has a lot of settings and features I did not try and I do not feel like spending a lot of time on that issue.

If you like to try. You will need to have a look into the following file

C:\Users\UserName\AppData\Local\Arduino15\packages\arduino\hardware\samd\1.8.4\cores\arduino\wiring_analog.c

I created a copy of the analogRead function and modified that.

There are some disadvantages in using the differential input. One is that the voltage range is smaller. So there is some additional complications for something that should be a lot simpler.

Thanks! This seems a bit more complicated than I assumed. It’s not outside my capabilities, but definitely seems like it’s more trouble than it’s worth. I think I’m going to find an ADS1115 external ADC to use, as it seems simpler to use and hopefully won’t have the same noise issues. I can’t believe there isn’t an easier way to get native analog input on the Nano 33 IoT.

Thanks,
Andrew

I posted in Nov 19 some trouble with extrem noisy 3.3 - onboard voltage - until today an open issue ...
The onboard voltage is horrible noisy, because the pcb design rules of the MPM3610 are ignored :-((

The boards habe an heavy mistake in design of the voltage regulator, so I think you can't use these boards for analoge measurements.

Workaround for me:

  1. Don't wire the Vin, let this pin float.
  2. Take an 3.3V constant voltage regulator (UA78M33) and wire him at pin Vout of the board

.. so you can use the microcontroller at specs.

In order to get a better 3.3V line, you should desolder R9 (39K resistor from AAM pin of the mpm3610 to ground). That will make the mpm3610 works in ccm mode always, which is the low noise mode.

KaiMatzen:
I posted in Nov 19 some trouble with extrem noisy 3.3 - onboard voltage - until today an open issue …
The onboard voltage is horrible noisy, because the pcb design rules of the MPM3610 are ignored :-((

Which mistakes? You can desolder the R9 resistor but other than that, the rest seems ok. :?

Luisonson:
Which mistakes? You can desolder the R9 resistor but other than that, the rest seems ok. :?

Have a look at the MPM3610 datasheet. There are PCB Layout Guidelines (page 19), that the PCB does not seem to follow.

Klaus_K:
Have a look at the MPM3610 datasheet. There are PCB Layout Guidelines (page 19), that the PCB does not seem to follow.

Yes, is not as clean and close as the datasheet, but I don't think that is the problem. I think the mpm3610 is working in AAM mode that has a huge noise. Please, desolder R9 and check again.

I'm just starting out with this board and, like Antyos, I need to measure the voltage from a pot and am having problems with noise. Other boards that I own do not exhibit the same behaviour. I tried Luisonson's suggestion and thought I'd feedback my findings.

I created a very basic sketch that simply reads analog pin A0 and writes the value to the serial port while pin A7 is HIGH. The only additional circuitry I added was a jumper wire going from pin A0 to ground or the 3.3V output, and the jumper wire for taking A7 HIGH.

I'm using Arduino IDE 1.8.12 in Windows 10, and watching the values stream past in the serial monitor or serial plotter. The board is powered by a short micro USB cable from my PC.

Doing 10 bit ADC with pin A0 tied to ground, the values are very noisy, regularly exceeding 30 and ocassionally exceeding 45, although most are below 5. When I tie A0 to the 3.3V output, I get similar amounts of noise.

I'm not sure I have the skill to replace R9 if I had to put it back so, instead of desoldering it, I manually shorted it to ground with a breadboard jumper wire. There was no subjectively discernable difference in noise. Here is an image of where I think R9 is, in case anyone wants to correct me:

I also tried powering the board with a range of voltages between 5 and 16V to the Vin pin, instead of by USB. No difference. And I tried different USB cables.

The only improvement I have found so far, without meddling with settings in wiring_analog.c or exploring differential measurements, is by adding a 10k resistor between the voltage I want to measure and pin A0. It gives a significant reduction in noise, but not enough. The first image is with A0 tied to ground, and the second is with A0 tied to the 3.3V output:

Is it likely that the Nano 33 IoT can be made to perform as well as other arduinos, or is it junk in terms of ADC? If the latter, I need to decide pretty quickly to switch to something else.

floater:
I'm not sure I have the skill to replace R9 if I had to put it back so, instead of desoldering it, I manually shorted it to ground with a breadboard jumper wire. There was no subjectively discernable difference in noise. Here is an image of where I think R9 is, in case anyone wants to correct me:

Hello Floater,
The R9 is the only 39K resistor in the board, so with a multimeter should be easy to find it.

R9 resistor connect AMM pin (18), of the mpm3610, to ground. That AMM pin has to be high or floating to force the mpm3610 to work in CCM mode. Conecting that pin to ground force the oposite! Always high noise.

So... Connect a wire from vcc to the mpm side of the resistor... Or remove the resistor that is perfectly ok if you don't need super low consuption.

Thanks for testing. Waiting for your new tests :relaxed:

Luisonson:
R9 resistor connect AMM pin (18), of the mpm3610, to ground. That AMM pin has to be high or floating to force the mpm3610 to work in CCM mode. Conecting that pin to ground force the oposite! Always high noise.

D'oh! Of course! Apologies. And thank you.

I just double checked the resistance of the resistor I highlighted. R38.67. Looks like not everything I've done this evening is a silly mistake :slight_smile:

I've tried taking it high now and sadly, I can't see any difference:

floater:
I've tried taking it high now and sadly, I can't see any difference:

Wow!, I didn't expect the same result with the MPM3610 working in AAM mode and in CCM mode... That's super wird... but the results are clear :slightly_frowning_face:

Thank you for testing.

I think I'm making good progress by editing wiring.c.

I've got the noise down to +/-3 at GND voltage, and down to +/-4 at Vref, on a span of 4096, sampling at around 1kHz.

I still have several other things to try so hopefully there's even more improvement to be had.

I'll report back later but I thought I'd let people know so that we can avoid duplicating effort.

It looks like the Nano 33 IoT is going to be able to do the task I bought it for. Apologies if much of this is obvious to everyone or if I've forgotten any key info or made any mistakes.

Here's the last set of data I captured:

Maybe other boards are busily oversampling in the background by default and so nobody notices. Or maybe this board is particularly noisy. I have no idea. Prior to a couple of months ago, my last experience of A2D was with a PIC micro solar tracking robot I built 23 years ago! Arduino is magically easy by comparison!

Adding a 10k resistor between the voltage I wanted to measure and the ADC pin reduced the noise significantly. Then I started meddling with wiring.c file.

I'm pretty new to microcontrollers like arduino so, rightly or wrongly, I've started to think of this file as Nano's answer to regedit. There's a lot of control over the sampling rate, sample size, resolution, averaging etc. I'd like to know if I can change these settings on-the-fly (different sensors have different characteristics and requirements).

The ADC can run very fast (or so it seems to me), which means it isn't necessarily a problem to average a large number of readings. I started off oversampling sixteen 12bit numbers but this gives me a lot more data than I need, both in terms of resolution and frequency.

I now have it set to average 64 conversions at a time and I'm getting readings every half a millisecond. I've also exploited the bit-shifting function (AVGCTRL_ADJRES) to drop the resolution down to only 512 levels. The combination of large sample sizes, bit-shifting, and a 290 degree pot is giving me lovely smooth data with a 0.6 degree resolution. But I could easily now tune this to give me more datapoints or more resolution.

Moral of the story: it may well be worth delving into the settings provided, to tune it to your particular requirements.

By the way, I'm running off USB, giving a Vin of 4.814V and a Vref of 3.285V and I'm still logging data by pasting from the serial monitor on my PC. Next job is figuring out how to send the data over wifi.

Also there's nothing special about the sketches I'm running so far, but for completeness:

/*Test to see if arduino logs analogue data without noise
 * 
 */

void setup() 
  {
  Serial.begin(9600);
  pinMode(A0, INPUT);
  }

void loop() 
  {
  Serial.println(analogRead(A0));
  }

Or, slightly easier to manage:

/*Test to see if arduino logs analogue data without noise
 * 
 */
int count = 500;

void setup() 
  {
  Serial.begin(9600);
  pinMode(A0, INPUT);
  pinMode(A7, INPUT);
  }

void loop() //Writes 500 measurements to the serial port whenever A7 goes high
  {  
  if (digitalRead(A7)==HIGH)
    { 
    while (count > 0)
      {
      Serial.println(analogRead(A0));
      count --;
      }
    count = 500;
    while (digitalRead(A7)==HIGH) //Wait until you release A7 before moving on
      {
      }
    delay(100);       //Delay to cope with any switch bounce
    }
  }

Things I haven't tried yet:

Using an external voltage reference for the ADC (there's a pin broken out for this purpose and a registry setting to enable it - see info in analogReference() - Arduino Reference )

Changing the sampling time length - there's a hint in the MCU datasheet that this may increase the input impedence. I wonder if that could do the same job as the resistor I'm using.

I've exceeded the maximum message length so I'll paste the wiring.c file into a reply.

My version of wiring.c follows. I’ve probably committed terrible sins in there, but hey-ho. I’ve added some section breaks to make it clear which bits I meddled with. Enjoy!

/*
  Copyright (c) 2015 Arduino LLC.  All right reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the GNU Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "Arduino.h"

#ifdef __cplusplus
extern "C" {
#endif

/*
 * System Core Clock is at 1MHz (8MHz/8) at Reset.
 * It is switched to 48MHz in the Reset Handler (startup.c)
 */
uint32_t SystemCoreClock=1000000ul ;

/*
void calibrateADC()
{
  volatile uint32_t valeur = 0;

  for(int i = 0; i < 5; ++i)
  {
    ADC->SWTRIG.bit.START = 1;
    while( ADC->INTFLAG.bit.RESRDY == 0 || ADC->STATUS.bit.SYNCBUSY == 1 )
    {
      // Waiting for a complete conversion and complete synchronization
    }

    valeur += ADC->RESULT.bit.RESULT;
  }

  valeur = valeur/5;
}*/

/*
 * Arduino Zero board initialization
 *
 * Good to know:
 *   - At reset, ResetHandler did the system clock configuration. Core is running at 48MHz.
 *   - Watchdog is disabled by default, unless someone plays with NVM User page
 *   - During reset, all PORT lines are configured as inputs with input buffers, output buffers and pull disabled.
 */
void init( void )
{
  // Set Systick to 1ms interval, common to all Cortex-M variants
  if ( SysTick_Config( SystemCoreClock / 1000 ) )
  {
    // Capture error
    while ( 1 ) ;
  }
  NVIC_SetPriority (SysTick_IRQn,  (1 << __NVIC_PRIO_BITS) - 2);  /* set Priority for Systick Interrupt (2nd lowest) */

  // Clock PORT for Digital I/O
//  PM->APBBMASK.reg |= PM_APBBMASK_PORT ;
//
//  // Clock EIC for I/O interrupts
//  PM->APBAMASK.reg |= PM_APBAMASK_EIC ;

  // Clock SERCOM for Serial
  PM->APBCMASK.reg |= PM_APBCMASK_SERCOM0 | PM_APBCMASK_SERCOM1 | PM_APBCMASK_SERCOM2 | PM_APBCMASK_SERCOM3 | PM_APBCMASK_SERCOM4 | PM_APBCMASK_SERCOM5 ;

  // Clock TC/TCC for Pulse and Analog
  PM->APBCMASK.reg |= PM_APBCMASK_TCC0 | PM_APBCMASK_TCC1 | PM_APBCMASK_TCC2 | PM_APBCMASK_TC3 | PM_APBCMASK_TC4 | PM_APBCMASK_TC5 ;

  // Clock ADC/DAC for Analog
  PM->APBCMASK.reg |= PM_APBCMASK_ADC | PM_APBCMASK_DAC ;

// Defining VERY_LOW_POWER breaks Arduino APIs since all pins are considered INPUT at startup
// However, it really lowers the power consumption by a factor of 20 in low power mode (0.03mA vs 0.6mA)
#ifndef VERY_LOW_POWER
  // Setup all pins (digital and analog) in INPUT mode (default is nothing)
  for (uint32_t ul = 0 ; ul < NUM_DIGITAL_PINS ; ul++ )
  {
    pinMode( ul, INPUT ) ;
  }
#endif

  // Initialize Analog Controller
  // Setting clock
  while(GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY);

  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID( GCM_ADC ) | // Generic Clock ADC
                      GCLK_CLKCTRL_GEN_GCLK0     | // Generic Clock Generator 0 is source
                      GCLK_CLKCTRL_CLKEN ;

  while( ADC->STATUS.bit.SYNCBUSY == 1 );          // Wait for synchronization of registers between the clock domains


//___________________________________________________________________________


  ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV16 |  // Divide Clock by 4/8/16...512. 512 is quite slow. 16 is very fast. 4 and 8 didn't work properly for me
  ADC_CTRLB_RESSEL_16BIT |         		// 10 bits resolution was default, changed to 16 to allow facilitate accumulating whilst minimising the need for bit shifting
  ADC_CTRLB_FREERUN;				//Enabled
//  ADC_CTRLB_DIFFMODE;				//Differential mode disabled. My reading of this is that you only need it if the voltage you are measuring may go negative.

  ADC->SAMPCTRL.reg = 0x3f;                     // Set max Sampling Time Length. I haven't meddled with this yet.

  while( ADC->STATUS.bit.SYNCBUSY == 1 );       	// Wait for synchronization of registers between the clock domains

  ADC->INPUTCTRL.reg = ADC_INPUTCTRL_MUXNEG_GND;   	// No Negative input (Internal Ground)

  // Averaging (see datasheet table in AVGCTRL register description)
  ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM_64 |    	// # of samples to accumulate. (1/2/4/8.../1024)
  ADC_AVGCTRL_ADJRES(0x7ul);   				// Divide the accumulated value by n^2. For example, a 16x sample divided by 2^2 reduces the span from 65536 down to 16384. I found that it bitshifted up to a value of n=7 and I'm using that to drop unnecessary resolution.


  analogReference( AR_DEFAULT ) ; 
	//AR_DEFAULT: default analog ref=3.3V. AR_INTERNAL: iternal 2.23V ref. AR_INTERNAL1V0: internal 1.0V ref. AR_INTERNAL1V65: internal 1.65V ref. AR_INTERNAL2V23: internal 2.23V ref. AR_EXTERNAL: the voltage applied to the AREF pin is used as the reference


//  ADC->REFCTRL.reg = ADC_REFCTRL_REFCOMP;	//Reference Buffer Offset Compensation (increases accuracy of gain stage but increases start up time of the reference, didn't clearly help so commented out)


//___________________________________________________________________________




  // Initialize DAC
  // Setting clock
  while ( GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY );
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID( GCM_DAC ) | // Generic Clock ADC
                      GCLK_CLKCTRL_GEN_GCLK0     | // Generic Clock Generator 0 is source
                      GCLK_CLKCTRL_CLKEN ;

  while ( DAC->STATUS.bit.SYNCBUSY == 1 ); // Wait for synchronization of registers between the clock domains
  DAC->CTRLB.reg = DAC_CTRLB_REFSEL_AVCC | // Using the 3.3V reference
                   DAC_CTRLB_EOEN ;        // External Output Enable (Vout)
}

#ifdef __cplusplus
}
#endif

Hey

Came across this thread after having some trouble with this exact issue myself.

I tried you modified version of wiring.c but it did not appear to make any difference.

Have you looked further into this? Any further tips?

For my project i was just wanting to measure the temperature so i have decided to get a sensor with a digital readout. Hoping that wont be affected by this problem.

Hi Magnebear,

I haven't done any more work on this.

After I sent my last message, I sent this thread to a colleague. She had the same experience in terms of ADC noise but was able to copy my steps and get good data out of two other Nanos. She went on to develop a human factors device that resembles the product we are developing. The device logs the angle and torque of a user action and sends it via wifi to a web browser. Unfortunately, we haven't been able to gather data from target users because of the C-19 lockdown, but in the tests we've done so far, we've been very happy with the data it logs from the pot and loadcell (both analogue).

Good luck!