analogRead returns faulty "0" after while loop with same analogRead?

My analogRead gives me a "0" value back when i don't expect it. Specially because there is a working while loop just before it reading it out with succes.
I seem to be able to fix it with a short delay(5) between the last and second last read. But I want to understand what is happening and hopefully not use a delay.

My Goal and Setup :
Current Sensing with shunt resistor for continues-rotating-Servo.
Analog read from pin A1 (and tried pinA4 with same result).
Chip 32u4
SetupServo.PNG

My code (simple) :

  // Start rotating the servo
  current = 1;          
  myservo.write(100);   
 
  // Check when Servo actually starts trying to moving   
    while (current < 5){                          
        current = analogRead(analogPin);          // read the analog current pin 
        Serial.print(current);                        
        Serial.print(".");                         
    }    
                                         
   // Servo Started, now continue
    Serial.println("Started!");                 
//delay(5);                                       // Uncommeting this fixes reading a 0
    current = analogRead(analogPin);              // read the analog current pin
    Serial.println(current);

I would expect the following example result :
0.0.0.0.14.Started!
210

But what I get is :
0.0.0.0.14.Started!
0

If I uncomment the delay function it suddenly does work as expected.

So I tryed to debug this:

  • Funny thing is that this happens only the first time I call up this function. Every following time this function is called again it works normal, even without the delay.

  • Doing an extra analogRead in stead of the delay, even 5 extra, all still give "0"

  • The delay can also be moved up after the analogRead inside the while loop, but will make it all a bit slower of course.

  • Delay(1) is not enough, 5 seems to work every time now so far.

I see mixed results on internet if you should always use delay's around analog reads. But most say its not needed if you don't have to wait for value stabilization. Though this is not an stabilization issue I think. Specially since the same code fired a 2nd or more times from a loop gives a good result. It kinda feels like the analogRead is prevented somehow the first time after the while loop and returns a "0". But why here? And not already in the while loop where it gets requested already pretty fast until the value changes?

If I change the while loop to:

while (current < 300){

I get
0.0.0.0.0.0.0.0.0.0.0.0.0.0.12.247.247.246.243.244.246.252
So I'm pretty sure the when 12 triggers the while < 5 loop. The next value I should read would be ~245, and not falling back to 0 again.

Hope someone can shed some light on this as I think this is not the first time I've run in this kinda problem, and would like to learn from it to make much more ridged code. Using a delay doesn't feel ridged for me unless I understand what is actually happening and know what a recommended delay should be.

Greetings! :slight_smile:

SetupServo.PNG

Welcome, and well done on your first post choosing a descriptive title, posting your code in code tags, using paragraphs, and even inlining your diagram. Exemplary! Karma added.

However, why strip the code to nothing and crop the diagram? Sure, maybe there is nothing interesting there...but then maybe there is. Why over-think? Just post all the code and the complete diagram and allow the folks here to decide what's important.

Are you sure that the MCU is a 32U4? The Pro-Trinket looks to have a 328P.

Without more info, i’d guess that powering your servo from the Arduino s the issue.
The startup current sinks the regulator for a moment - hence zero volts getting to the servo, which recovers as the motor starts to move.

Common error. Power the servo +V from elsewhere.

Hallo and thank you for your responses, and thanks for your kind word and advice arduarn :wink:

@lastchancename about voltage drop being a possible problem. I understand what you mean, it was my first thought too, but I don't think its the case here because :

  1. It only does it the first time the function is used. Every next time has the exact same startup current, sometimes even higher, so I would expect it to happen more often (i'm having a testing loop doing the startup in both direction and let it run for like 100+ times, and it only happens 1st time).
  2. If I adjust the while loop to continue reading the analogpin the whole time it does work normally as I showed in my post :

If I change the while loop to:
Code: [Select]

while (current < 300){

I get
0.0.0.0.0.0.0.0.0.0.0.0.0.0.12.247.247.246.243.244.246.252
So I'm pretty sure the when 12 triggers the while < 5 loop. The next value I should read would be ~245, and not falling back to 0 again.

So it only happens after I return from the while loop, and directly do the same analog read again within ~5ms (since a delay(5) seems to fix it most of the times).

But, regardless, I will try an external power source for the V+ of the servo just for confirming this is not the case here :wink:

@arduarn, thanks again for your kind words and feedback.
To answer your questions :

  • Stripping the code, euhm... multiple reasons :wink:
    1a. I'm not that good yet in coding.
    1b. I'm often digging to deep in stuff that I actually should just abandon instead of keep trying to achieve it. But I do it regardless because I don't like giving up, want to try something new, and somehow its the way I learn the fastest without reading to much books.
    1c. 1a+1b makes for (in my opinion) crazy experimental solutions that often people don't like to get involved in.
    So I include only what I think is necessary/related to have an higher change of people trying to dig into it. Anyway, I posted the full under contruction code at the bottom of this post.
  • Full diagram
    I did only include the part that I have currently build. I have not added anything else to it yet. Anyway, full schematic also at the bottom :wink:
  • Chip 32U4?
    Yup, pretty sure, I'm using an Adafruit Feather 32u4 RFM69HCW at the moment for code development.

Full schematic (only servo part is build though):

Full Code (only servo part is being build in this one, for experimenting with current sensing)
The thing it should do now is :
a. Do a startup full-sweep in setup() to check full motion and collision detection based on current.
b. Then loop doing 10 closing steps and 10 opening steps.
c. Report a whole bunch of things on serial using the stallSense() function :wink:

Code will follow, its to big >.<

/*
 by AcE Krystal @ 14-3-2019
 Version 0.01.1(Playground) 
 Project 201903p01s02-03

    
Future idea's and functions : 
 1. Learn Servo characteristics during startup sweep (min current and max current) and handle small current changes in higher level with more weight then changes at the lower level currents.
 Solution for : known problem around starting while physicly already at the end, the startup peak current doesn't lower enough to regonize it rizing again when stalling.

 */


#include <Servo.h>

Servo myservo;  // create servo object to control a servo

int pos = 0;            // variable to store the relative servo position
int posSignal = 90;     // variable to store the real signal for rotating servo's
int analogPin = A4;     // Sensing Current draw before Shunt Resistor R2 towards Ground
int current = 0;        // variable to store the sensed current in.
int increaseFlow = 100; // Direction and speed to increase flow
int decreaseFlow = 80;  // Direction and speed to decrease flow
// Stall detector 1 --------------
int lastCurrent = 0;      // Used for calculating stalling rising current in function stallSense()
int stepTollerance = 5;   // Current rise it will allow without raising the stallLevel
int startTollerance = 30; // Current rise it will allow at startup because full sweeps give bigger momentum = lower current =? bigger changes
//int maxTollerance = -15;  // 
int tolleranceImpact = 5; // if tollerance is exceeded, it will be devided by this number to determin how serieus this event was to set Confirm level
int startDelay = 6;       // Fixes "0" analogRead after while loop. See https://forum.arduino.cc/index.php?topic=603226.0
int stepDelay = 0;        // Setting the fix back to 0 in stepping mode.
int difference = 0;       // Used for storing the calculated difference of last current and new current. If it goes below 0 current is rizing.
int stallLevel = 0;       // The nunber of time a Stallvalue was exceeded in a row.
int stallConfirm = 6;     // The number of times a Stallvalue should be exceeded before considering it a true Stall and not just high peak load
int stallCount = 0;       // Debug keeps track of number of stallConfirms flagged in total.
boolean stalled = 0;      // If true it means the servo is stalled.
// Stall detector 2 --------------
int Stallval = 180;       // Units set as limit. Higher should trigger "stalled" vlag.
int stallLevel2 = 0;      // The nunber of time a Stallvalue was exceeded in a row.
int stallConfirm2 = 3;    // The number of times a Stallvalue should be exceeded before considering it a true Stall and not just high peak load
int stallCount2 = 0;      // Debug keeps track of number of stallConfirms flagged

void setup() {
  myservo.attach(5);  // attaches the servo on pin 9 to the servo object
  Serial.begin(9600);           //  setup serial
  delay(5000);
  
  // Begin startup Sweep
  Serial.println("1 - Close");
  pos = changeFlow(400, decreaseFlow, startTollerance, startDelay); 
  delay(500);
  Serial.println("2 - Open");
  pos = changeFlow(400, increaseFlow, startTollerance, stepDelay);
  delay(500);
  Serial.println("3 - Close");
  pos = changeFlow(400, decreaseFlow, startTollerance, stepDelay);
  pos = 0;
  // End Startup sweep
}

void loop() {
  int b = 10;
  int c = 0;
  while ( c < b){
    c++;
    delay(1900);
    pos = changeFlow(10, increaseFlow, stepTollerance, stepDelay); // increase flow with steps
    delay(100);
    stallSenseDebug();
  }
  while (c > 0){
    c--;
    delay(1900);
    pos = changeFlow(10, decreaseFlow, stepTollerance, stepDelay); // decrease flow with steps
    delay(100);
    stallSenseDebug();
  }

}

 int changeFlow (int multiplier, int directionSpeed, int tollerance, int readDelay){
  int F = 1;
  F = F * multiplier;
  // Start rotating the servo 
  current = 1;          // Set current to 1, not 0 for debug
  myservo.write(directionSpeed);   // Tell rotating servo to start moving (write 90 is stopping, so 100 or higher is good for turning)
 
  // Check when Servo actually starts trying to moving
    Serial.print("Waiting for Servo te start decrease ");      
    while (current < 5){                          // Wait for servo to respond (they seem to have there own read cycles that can take up to ~15ms to actually start
        current = analogRead(analogPin);          // read the analog current pin 
        Serial.print(current);                        
        Serial.print(".");                         
    }                                             // This part prevent the stall detection function measuring 0 current because of late starting servo's. It also helps making equal steps because timers start when there is actually current measured from the servo.
   // Servo Started, now continue
    Serial.println("Started!");                 
 //   Serial.print("lastCurrent = ");
 //   Serial.print(lastCurrent);
delay(readDelay);                                       // Uncommeting this fixes reading a 0
    current = analogRead(analogPin);              // read the analog current pin
    lastCurrent = current;                        // read the input pin again (update for lastCurrent to give it a change to get higher value at spinup
//    Serial.print(" and now current is ");
//    Serial.print(current);
//    Serial.print(" and new lastCurrent = ");
    Serial.println(lastCurrent);  
        
  for (int a = 0; a < multiplier; a++){ 
        delay(4);
        stallSense(tollerance);
        if (stalled){
          Serial.println("Detected high current by stallSense-(1), quiting function decreaseFlow");                  // debug value
          break;
        }
  }
  myservo.write(90);
  if (!stalled){
      pos--;    
  }
  stalled = 0;          // reset stalled flag for new movements
  stallLevel = 0;
  return pos;
 }

int stallSense(int tollerance){
int current1 = analogRead(analogPin);  // read the input pin
int current2 = analogRead(analogPin);  // read the input pin
int current3 = analogRead(analogPin);  // read the input pin
current = (current1 + current2 + current3) / 3;
int newCurrent = current;
int L = 0;
difference = lastCurrent - newCurrent + tollerance;
if ( difference < 0 ){
  L = difference / (-tolleranceImpact); 
  for (int F = 0; F < L; F++){
    stallLevel++;
 //   Serial.print(F);
  }
}else{
  stallLevel = 0;  
}
//int tolleranceAdjust = stepTollerance * stallLevel;
//int usedTollerance = tollerance - tolleranceAdjust;
    Serial.print(current);                // debug value
    Serial.print(",");                  // debug value
//    Serial.print(posSignal);                  // debug value
//    Serial.print(",");                  // debug value
    Serial.print(stallLevel);       // debug value
    Serial.print(",");                  // debug value
    Serial.print(stallCount);       // debug value
    Serial.print(",");                  // debug value
    Serial.print(pos);       // debug value
    Serial.print(" // ");                  // debug value //////////////////
    Serial.print(current);       // debug value
    Serial.print(".");                  // debug value
    Serial.print(difference);       // debug value
    Serial.print(".");                  // debug value
    Serial.print(lastCurrent);       // debug value
    Serial.print(".");                  // debug value
//    Serial.print(newCurrent);       // debug value
//    Serial.print(".");                  // debug value
    Serial.print(tollerance);       // debug value
    Serial.print(" !");                  // debug value
    Serial.println(L);       // debug value
    
lastCurrent = newCurrent;
if (stallLevel >= stallConfirm){
  myservo.write(90);            // Stop the rotating Servo
  stallLevel = 0;               // Reset Stalled level
  stalled = 1;                  // raise the stalled flag for all functions
  stallCount++;                 
  }
}
  

  int stallSenseDebug(){
current = analogRead(analogPin);  // read the input pin
    if (current > Stallval)
      { 
        stallLevel2++;
      }else{
        stallLevel2 = 0;
      }
    Serial.print(current);                // debug value
    Serial.print(",");                  // debug value
    Serial.print(stallLevel2);       // debug value
    Serial.print(",");                  // debug value
    Serial.print(stallCount2);       // debug value
    Serial.print(",");                  // debug value
    Serial.println(pos);       // debug value
     if (stallLevel2 >= stallConfirm2)
      {
        myservo.write(90);            // Stop the rotating Servo
        Serial.println("Detected high current by stallSense-(2), quited all movement");                  // debug value
        stallLevel2 = 0;
        stallCount2++;
      }
  }

Thanks for posting the additional info. Well, I don't see anything obvious in the code that could be causing the symptoms as you present them.

I imagine lastchancename has the right idea with the power supply, even though the symptoms are odd. If you are powering the servo from the USB port of your PC then you probably shouldn't be, that could break something if you are unlucky.

You say it only happens the first time the function is called; so, does it happen 1) the first time after an upload? 2) the first time after pressing the reset button? 3) the first time after a POR?

Post the results of the power supply change and we'll see if anyone has any more suggestions.

Hi arduarn. I have tested it with other power supply, but still happens. And again only the first time the function is triggert. All following times it runs through the same code it does work without problem.

Also, i tested now on a arduino 32u4, 328, and M0. They all seem to display the same problem.

Very interesting ;). I just leave the +5ms delay in there the first time I call the function and reset it to 0ms after it has run the first time as work around for now.

Guess this is a minor thing that won't really be fixed, but at least there is a post about it now ;).
Thanks for the reply's.