Analog Read Fluctuates, But Only When Plugged into External Power Source

Hello Arduino Community! We're Arduino beginners trying to make a mechanism that opens a door when a certain pattern of knocks is made. The project works perfectly...except when it's plugged into any sort of external power.

The Problem:
We're using analogRead to read voltage from a vibration sensor. When the Arduino was powered by a computer USB port or by a nearly-dead 9V battery, it works perfectly. It reads between 0-6 when it isn't vibrating and gets up to about 60 with a strong knock. However, when we tried to power the Arduino by hooking the USB cable up to a 5V 1 Amp USB charger, or when we plugged it into a 9VDC 800mA converter, we got values that fluctuated between 0 and about 450. When the calls to analogRead have a less-than-30-ms or so delay between them, the output fluctuated between 0 and about 450 many times a second. When the delay was longer, we saw a jump to much slower fluctuation that was very regular in its period, taking about 4 seconds for a cycle. The values were not affected by knocking or vibrating the sensor in any way. Everything works perfectly again the moment we unplug the Arduino from the wall and put it back on the computer’s USB power.

The Setup:
We’re using an Arduino Uno and this sensor. One end of the sensor is plugged into A0, the other is plugged into Gnd. Three Mega-Ohm resistors connected in series also connect A0 to Gnd. It’s the same setup used in the Arduino Knock Tutorial but with 3 Mega-Ohm resistors instead of 1. The only other thing plugged into the Arduino is a servo. The wires going to the sensor are about a foot long. We used alligator clips to attach to the sensor leads.

Things We Have Already Tried:
-Attaching wires from the rest of the analog ports to ground
-Disconnecting the servo
-Commenting out all the Serial communication, including begin()

Code and Output:

Here’s a simple program to print analogRead every 50 ms.

const int knockSensor = A0; // the piezo is connected to analog pin 0
int sensorReading = 0;      // variable to store the value read from the sensor pin

void setup() {
 analogReference(DEFAULT);
 pinMode(A0, INPUT);
 Serial.begin(9600);       // use the serial port
}

void loop() {
  // read the sensor and store it in the variable sensorReading:
  sensorReading = analogRead(knockSensor);    
  Serial.println(sensorReading);
  delay(50);  // delay to avoid overloading the serial port buffer
}

Here is some output from running this program when the Arduino is powered by the external 9VDC power supply. You can see the periodic fluctuations in the readings between 0 and 400.

0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
3
44
44
78
96
122
160
203
230
269
315
348
359
369
387
412
418
403
391
366
341
308
282
257
228
190
116
40
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
14
14
65
93
121
155
212
269
304
360
406
415
403
401
417
442
434
424
400
379
348
317
291
269
237
193
94
35
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
10
10
63
88
109
141
185
226
245
294
339
363
358
361
382
405
400
387
371
349
323
290
267
243
215
179
95
40
3
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
40
40
76
97
120
159
205
237
272
320
358
373
384
395
421
434
415
407
378
353
318
289
263
237
198
139
48
9
0
0
0
0
0
0

Though it’s probably not relevant to my problem, for completeness’ sake here’s the full code for sensing knock patterns and opening the door.

#include <Servo.h> 

//sensing variables
const int knockSensor = A0; //knock input
int threshold = 10;  // threshold value to decide when the detected sound is a knock or not
const int lower_thresh = 9;
const int CYCLES_REST =30;
int averageReading = 0;
int restCounter = 0;

//Pattern logic variables
int cycle = 0; //how log it's been since knock started
const int TIMEOUT = 500;
const float TIMING_ERROR = 0.2;
boolean currentlyKnocking = false;
float scalar = 1;//scales pattern to
float pattern[] = {  //The knock pattern
  1, 1,1};  //knock-knock-knock 
int patternLength = 3;
int currentIndex;
boolean onTarget = true; //timing is good.
boolean knockCompleted = false;
const int KNOCK_RESET_TIME = 500;
boolean knockAllowed = true;

//Servo Variables
Servo myservo;  // create servo object to control a servo
int servoPin = 9;
void setup(){
    Serial.begin(9600);  //Begin serial communcation
    normalizePattern();
    getaverageReading();
    //myservo.attach(servoPin); 
   // myservo.write(0);
    threshold = averageReading + 10;
}

void loop(){
    if(knockAllowed)
    {
      if(currentlyKnocking)
      {
        cycle++;
         if(cycle > TIMEOUT)
       {
          currentlyKnocking = false;
     //     Serial.println("Knock Pattern TimedOut");
       }
      }
     
      if(checkKnock()){    
            //knock has started
          if(!currentlyKnocking)
          {
            startKnock();
          }
          else
          {
            recordCycle(cycle);
            if(knockCompleted)
            {
              openDoor();
              currentlyKnocking = false;
              cycle = KNOCK_RESET_TIME;
       //       Serial.println("Knock Completed Correctly");
              knockAllowed = false;
              return;
            }
            else if(!onTarget)
            {
         //     Serial.println("Knock Abort!");
              currentlyKnocking = false;
              cycle = KNOCK_RESET_TIME;
              knockAllowed = false;
              return;
            }
           // Serial.println("Knock");
            cycle = 0;
          }
         // Serial.println("high"); 
      }
    }
    else
    {
      if(--cycle < 0)
      {
        knockAllowed = true;
       // Serial.println("Ready For next Knock");
      }
    }
    delay(5);
}

void recordCycle(int gap)
{
  if(currentIndex == 0)
  {
    scalar = gap;
  }
  else
  {
    float adjusted = pattern[currentIndex] * scalar;
    if(gap > adjusted * (1 - TIMING_ERROR) &&
       gap < adjusted * (1 + TIMING_ERROR))
       {
         //correct timing
         if(currentIndex + 1 == patternLength)
           knockCompleted = true;
       }
       else
       {
         //wrong timing
         onTarget = false;
       }
  }
  currentIndex++;
}

void startKnock()
{
  //Serial.println("Knock Pattern Started");
  currentlyKnocking = true;
  currentIndex = 0;
  onTarget = true;
  knockCompleted = false;
  cycle = 0;
}

boolean checkKnock()
{
   // read the sensor
  int sensorReading = analogRead(knockSensor); 
  if (restCounter <= 0 && sensorReading >= threshold) {
    restCounter = CYCLES_REST;
    return true;
  }
  else if(sensorReading <= averageReading + 2) restCounter--;
  return false;
}


void normalizePattern()
{
  if(pattern[0] != 1)
  {
    float scaleBy = 1/pattern[0];
    for(int i = 0; i < patternLength; i++)
    {
      pattern[i] *= scaleBy;
    }
  }
}

void getaverageReading()
{
  int total = 0;
  for(int i = 100; i > 0; i--)
  {
    int sensorReading = analogRead(knockSensor);
    total+= sensorReading;
    delay(10);
  }
  averageReading = (total / 100.0 + .5);
}

void openDoor()
{
 myservo.attach(servoPin);
 myservo.write(180);
 delay(2000);
 myservo.write(0);
 delay(1000);
 myservo.detach();
}

This problem has been really discouraging for us today after so much success in assembling our contraption. To reiterate, the whole thing works perfectly, just as long as it's plugged into the computer USB port. Any help is much appreciated!

First of all, thank you for the good explanation and test results.

Perhaps you have to think bigger.
There is electrical noise and magnetic fields all around, caused by the mains power. There might be a grounding problem, or ground currents. The 9V converter might have a lot of influence.

The input with 3Mohm is very high impedance. Any wire will pick up electrical and magnetic noise.
Even 1Mohm is very high. I use 10k impedance in my circuits as a rule of thumb for unprotected and unshielded parts.

Can you tell what kind of 9V converter it is ?
What happens if you use that, and make a connection from the Arduino GND to a real ground.

Could you use 1Mohm resistors, that is still very high impedance but better than 3Mohm. You could select the analogReference(INTERNAL) for a higher value.

You could try some shielding by using shielded wires to the sensors and perhaps a copper foil (connected to Arduino GND) over the sensors.

The PVDF sensor are more sensitive for electrical noise than the round flat piezo elements (found in toys). For a knock sensor, you don't need the expensive PVDF sensor.
Could you try a few cheap round piezo elements ?
Some sellers sell the PVDF sensors together with a piece of copper foil, because they are so sensitive for noise.

You could try a capacitor at the analog input to ground to remove some high frequency noise. Start with 1nF.

Thanks for the advice! This is the adapter we're using. We're on the sixth floor of a building, so I don't know how we would ground our circuit. I have to go to class now, but I'll try some of these ideas later today.

Ground is not the physical ground but a signal, normally the zero volt line or the center pin of your mains socket.

We fixed it with Erdin's advice. It turns out the analog readings were MUCH more sensitive when the arduino was plugged into a stronger power source. We lowered the resistance and switched to simpler piezo sensor. The signal is also much noisier, so we still need to play around the tuning and maybe add a capacitor. Thanks for the help!

It turns out the analog readings were MUCH more sensitive when the arduino was plugged into a stronger power source.

What ever it turns out to be it is not that. The sensitivity of the input is not dependent in any way on the power supply. That is simply not how processors work.

^That's what I thought myself, which is why I was initially reluctant to make the resistance lower, but that's the only thing I can think of to explain what I observed. Perhaps it didn't increase the signal but just make it much, much noiser? I have no idea, I'm a programmer, not an electrical engineer.

Right now we have everything working pretty well when the arduino is plugged into the computer and the wall adapter, but when we unplug the computer it also seems to not work anywhere near as reliably, which is pretty silly because the arduino should be getting it's power from either the regulator or the USB and not a combination of both. It's hard to tune for this sensitivity, or even verify that it is indeed different, because I can't plug the USB cable into the computer to get print statements. Right now I have it calibrating its own threshold when it initializes instead of setting a constant numeric value. Tomorrow I'll add some LEDs to use as indicators of what the Arduino is sensing.

Is the computer connected to mains with earthing (grounding) or is it a laptop ?
If the Arduino is powered with the USB only, can you measure the 5V pin ? Perhaps it is only 4V.

The 9V adapter seems good quality.

I looked at the sketch, sorry I didn't do that before.

In the function getaverageReading() you add 100 samples and add that to 'total'.
100 samples = 100 * 1023 (maximum analogRead() value) = 102300.
'total' is a integer, that is 32768 maximum.

Your total will not fit in 'total', that is another reason why the outcome is unpredictable.
Please lower the value to 25.

You calculated the average with a mix of floating point and integer numbers.
I would not know what the outcome of that would be. In 'C' and 'C++' you have to be sure that compiler understands if it has to calculate with integers or floating point.

The function could return the result and you can use the Arduino pin as parameter.
Using it that way makes the code better and easier to read.

I think that detecting a knock would require a short time. I lowered the total time to 10ms. I'm not sure what that would do in the sketch, perhaps you have to use the previous delay.

// Function getAverageReading()
// It returns the analog value of the Arduino pin.
// The return value is in the range of 0...1023,
// and is the average of a number of samples
// during 10ms.
//
int getAverageReading( int pin)
{
  int total = 0;

  // 25 times 1023 will fit in integer
  for(int i = 0; i<25; i++)
  {
    int sensorReading = analogRead( pin);
    total += sensorReading;

    // 10ms total time
    // 10ms / 25 = 400 us
    // The analogRead() requires about 115 us
    delayMicroseconds(285);
  }

  // return the average value
  return( total / 25);
}

Perhaps it didn't increase the signal but just make it much, much noiser?

That is much more plausible.
You could do with looking at some signals on a scope to find out for sure.