[SOLVED] Problem getting a smooth capacitor charge.

Please tell me I'm dumb. I've setup a simple capacitor charging circuit where my Arduino supplies the circuit power from a digital pin, and measures the capacitor voltage as it charges.

My problem is my sensor results aren't showing a smooth voltage curve, there is a short period in the first 10-15 sensor samples after I turn the supply pin on (digitalwrite HIGH) that show a much slower charge rate before changing to its actual charge rate.

I attached 2 sets of results, the red graph shows the capacitor charging with the first 10-15 samples different to the actual charging rate.
The green graph shows the same setup but I altered the code slightly to run twice, you can see that the same 10-15 non ideal samples are there again, but in the second charge/discharge the response is completely ideal.

I was reckoning that there's some current limiting magic happening from the digital pin where it doesn't like the initial current rush to charge the capacitor, but i tried a variety of resistor/capacitor setups and they all have the same 10-15 sample period - besides, the green graph results show that in the second charging/discharging the pin is able to provide for an ideal response.

I guess I should clarify that I need an ideal response for a system identification project. If possible I need someway of disabling/bypassing whatever is causing this non ideal period, or if I'm doing something else that is completely messing with it please tell me I'm dumb.

Here is my beginner level code that produces the red graph results:

int Sensor = A0;                  //Sensor pin
float SenVal = 0;                 //Float for sensor readings
int Supply = 8;                   //Supply Pin

void setup() {
 Serial.begin(9600);
 pinMode(Supply, OUTPUT);          //Supply pin is an output
 digitalWrite(Supply, LOW);        //Supply is initially off
}

void loop() {
 SenVal = analogRead(Sensor);      //Sample and print (will be 0.0 as supply is off)
 Serial.println(SenVal);
 digitalWrite(Supply, HIGH);       //Supply 5V step 
 while(1){
  SenVal = analogRead(Sensor);     //Sample and print until Capacitor charged to 5V
  Serial.println(SenVal);     
   if (SenVal > 1020){             //Stop and switch off supply (capacitor will charge down)
   while(1){
    digitalWrite(Supply, LOW);
   }
  }
 }
}

Sorry for the long post, I tried to be as concise as possible. Also sorry if this isn't the best place for this post, i wasn't sure.

-----------------EDIT: SOLUTION-----------------

MrMark:
Mook, I think...

Yes! This was the problem, and as usual it all makes so much sense now :sweat_smile:
Thanks everyone for helping (karma++), I went through all solutions and am grateful for finding one so quickly.

I have attached new test results that I just produced as proof of the solution for anyone who might be interested. It compares the old code from the OP (top) with the new code included in this post (bottom)

Here is the new code exactly as MrMark suggested:
Changes from OP code
-115200 baud instead of 9600.
-Inclusion of delay(1) to dominate the sample time.

int Sensor = A0;                  //Sensor pin
float SenVal = 0;                 //Float for sensor readings
int Supply = 8;                   //Supply Pin

void setup() {
 Serial.begin(115200);
 pinMode(Supply, OUTPUT);          //Supply pin is an output
 digitalWrite(Supply, LOW);        //Supply is initially off
}

void loop() {
 SenVal = analogRead(Sensor);      //Sample and print (will be 0.0 as supply is off)
 Serial.println(SenVal);
 digitalWrite(Supply, HIGH);       //Supply 5V step 
 while(1){
  SenVal = analogRead(Sensor);     //Sample and print until Capacitor charged to 5V
  Serial.println(SenVal);   
  delay(1);  
   if (SenVal > 1020){             //Stop and switch off supply (capacitor will charge down)
   while(1){
    digitalWrite(Supply, LOW);
   }
  }
 }
}

Also thank you TomGeorge for posting my attachments :). I didn't know you could, and have now included them here.

A capacitor acts initially as a dead short circuit, and overloads the output pin.

You should not draw over 20 mA from an output pin, or if you exceed 40 mA, it can actually be destroyed. In any case, an output pin is far from an ideal voltage source.

Your post lacks some important details, like component values.

A better experiment would be to study the charging of a capacitor connected to Vcc via a resistor. If the resistor value is greater than about 150 Ohms, you can hold the capacitor at close to zero V using an analog pin as a digital output (set LOW). Then convert the pin to an analog input using analogRead() and measure the voltage as the capacitor charges.

Is the non-ideal period always at power on? If so, then create a delay for twice that period of time before you start making a measurement. You could also try swapping the digitalWrite( ) and pinMode( ) instructions in setup( ).

Hi,
OPs pics.
teast.jpg
Graephs.jpg
Tom… :slight_smile:

Hi,
Try a

pinMode(Sensor,INPUT);

In the setup().

At the moment the first;

SenVal = analogRead(Sensor);

could be the transition point where the input is setup as an INPUT and Analog.

Once the first analogRead() is done, the pin is already setup.
Tom.... :slight_smile:

Your diagram shows an electrolytic capacitor. Those will NEVER be linear because a chemical reaction is involved.

Paul

Mook, I think the issue isn't that the capacitor isn't charging as expected, it's that the ADC sampling rate is not uniform.

In the outer "while loop" the analogread() takes about 0.1 ms. The serial.println() nominally takes about 1 ms per character of output at 9600 baud, so a few milliseconds per loop. The other operations in the loop are much quicker, on the order of microseconds. Thus the interval between ADC samples is a few milliseconds with the loop interval dominated by the serial output.

However (here's the obscure bit) the Arduino core has a 64 byte output buffer so that the first 15 or so serial.println() calls are buffered quickly, the code is not blocked and continues with the next loop iteration, while the Arduino core is outputting serial data in the background. Since the buffer is filling faster than it is emptying, after 15 or so loops there is not sufficient room in the buffer for the print and the code stalls at that point for a few milliseconds as described above until enough characters are output to allow the serial.println() to complete.

A higher baud rate (e.g. Serial.begin(115200) in setup) will reduce the time to output serial data and a short delay (e.g. delay(1)) should give you a more uniform sampling period since the loop interval would then be dominated by the delay() function.

MrMark:
(...)

1000% agree :wink: Also very nicely written explanation.

As a side note there is another hidden "catch" in Serial. When you write numbers into Serial leading zeros are dropped. So if the sampling speed is limited by speed of Serial you are sampling faster when voltage close to zero is read (less characters to send -> faster Serial). From the green graph it seems the discharging part of the curve has more samples than the charging one.