Pick-up Voltage without draining Current

Hi,

I need to pick up the natural response frequency of a circuit (In this case LC). Had several challenges with having the circuit and power supply reduce noise. Finally I could just feed a square signal with a Digital pin, and get both a nice steady state resonance and a natural response upon current drop, (in scope screen). As soon as I plug in the A1 pin to read, it goes away in the shape of an exponential capacitor discharge...

I take it's pin A1 draining previously oscillating current, thus changing measured value. But why doesn't that happen when it's the scope reading the value (it probably contributes to the dampening but without distorting the signal shape).

Question is

So, how could I have the Arduino no disrupting the signal while reading the voltage on A1?

A1 unplugged, in my thumb.

A1 plugged

Note, too, that the circuit is not connected to anode or ground, only the scope probes are connected to Arduino GND. If I close the circuit, unsurprisingly is also looks like capacitor discharging.

** Related to prev question in case you wonder how I'm intending to sample a 24KHz signal.
** Also related to electronics.stackexchange post on how to feed the circuit from DC supply (closing loop with anode).

Is pinmode A1 set as INPUT?

Thanks for answering, Yes:

void setup() {
  pinMode(A1, OUTPUT);

  Serial.begin (115200);
  Serial.println ();
  
  ADCSRA &= ~(bit (ADPS0) | bit (ADPS1) | bit (ADPS2)); // clear prescaler bits
  ADCSRA |= bit (ADPS0);                               //   2  
}

bool done = false;
// the loop function runs over and over again forever
void loop() {
    PORTA  = B00000001; // On Digital pin 22

    // Read on A1
    if (! done) {
       unsigned long startTime = micros ();
    
        // Fast AnalogRead test
        SampleRate sr1 = readSignal(800, true);
        done = true;
        float freq = findNaturalFreq(sr1);
        unsigned long duration = micros () - startTime;
        Serial.print("Natural Freq: ");
        Serial.println(freq, 8);
        Serial.print("E2E sampling duration: ");
        Serial.println(duration, 8);
        
    } 
    PORTA  = B00000000; // Off
      
    delay(1);
  
  // digitalWrite(healthPin, LOW);
  //delayMicroseconds(60);
}

Yes? That looks like OUTPUT to me!

That would explain your problem. Why are you doing that?

How was you 'scope probe connected. Where is the probe's grund lead connected.

many more articles on t'internet. Poor 'scope probe conenction and lack of compensation makes a huge difference to what you see on the screen.
https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwiF042cwdP5AhVyoFwKHaArADAQFnoECAMQAQ&url=https%3A%2F%2Fwww.edn.com%2Fwp-content%2Fuploads%2F1999%2F07%2F7.22.19_DF.pdf&usg=AOvVaw1a-mvvDa44YCOtbKjEzf4Q

Are all those caps in series? Why?
You could not find a 1 uF capacitor, so you put 10 10uF capacitors in series?

The OUTPUT was messed up after cleanup for pasting here. Sorry.

There is only one capacitor (10uF). And an array of Inductors (900mH).

Probes are 10x setting. Gnd to Arduino GND pin.
Chn 1 to capacitor leg.
Chn 2 to digital pin pulse.
Will read your reference.

That says output.

It would be good if you followed the forum guidelines.
You should post your full code, and a schematic.

In your pic it seems A1 is plugged into the row next to your resistor...
What kind of time constant do you expect from your LC circuit? Must be pretty slow with a big capacitor and 9 coils in series...

An input pin (set as INPUT) should have a resistance of 20 Mohm. So it should not take current...

Why do you expect the oscillations visible in pic. 1?

You were right all along. I was already dizzy after a few days of testing variants. Apologies.

Setting as INPUT respects the dampened sinus shape, but quality is horrible.

Reading with INPUT A1.

Now I have the opposite effect, before readings were 0, now 1023. I tried pluging my CD power supply in the AREF, set to 3.44V, but when I connect the anode to GND, it shows 4.9V (Not sure why.).

Then the difference was scope's resistor vs Arduino's resistor.
So I guess the problem now became:

  • How to increase resistance on Arduino's pin to prevent altering the reading.
  • Hot to set the correct ranges so I read something from 0-1023 and not only 1023?

(I'm tempted to accept your answer and create a new one with the above?)

I though I was helping you by removing extra code.
later will upload all of it, not in that laptop ATM.
I expect my current implementation to pick up frequencies from 2kHz to 44KHz, tested with fn generator, but facing this issue with natural responses.

Complete code:

#define PinD7 B00000001
#define PinD6 B00000010
#define PinD5 B00000100
#define PinD4 B00001000

#define PinD3 B00010000
#define PinD2 B00100000
#define PinD1 B01000000
#define PinD0 B10000000

byte Dpins[8] = {PinD0, PinD1, PinD2, PinD3, PinD4, PinD5, PinD6, PinD7};

#define healthPin 22

// How many peaks to sample for frequency estimation.
#define PEAKS 5
#define BUFFER_SIZE 1000
int resultBuff[BUFFER_SIZE] = {};

// Classes to move o a library

class SampleRate {

    public:
      // Duration in microS
      unsigned long duration;
      int samples;

      // Should be in samples per microS
      double sampleFrequency;
      
      // Should be in microS
      double sampleDuration;
      
      SampleRate (unsigned long duration, int samples) 
          : duration(duration)
          , samples(samples)
          , sampleFrequency((double) samples / duration)
          , sampleDuration((double) duration / samples) {};
};



// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(healthPin, OUTPUT);
  Serial.begin (115200);
  pinMode(A1, INPUT);

  Serial.println ();
  
  ADCSRA &= ~(bit (ADPS0) | bit (ADPS1) | bit (ADPS2)); // clear prescaler bits
  
  // uncomment as required

  ADCSRA |= bit (ADPS0);                               //   2  
//  ADCSRA |= bit (ADPS1);                               //   4  
//  ADCSRA |= bit (ADPS0) | bit (ADPS1);                 //   8  
//  ADCSRA |= bit (ADPS2);                               //  16 
//  ADCSRA |= bit (ADPS0) | bit (ADPS2);                 //  32 
//  ADCSRA |= bit (ADPS1) | bit (ADPS2);                 //  64 
//  ADCSRA |= bit (ADPS0) | bit (ADPS1) | bit (ADPS2);   // 128
}

bool done = false;

// the loop function runs over and over again forever
void loop() {
  // port toggle test, max rate
//  while (true) {
//      //delayMicroseconds(3);
//      // Freq 2.667Mhz
//      // duty cycle: 70% (because of the loop delay it's not 50%)
////      PORTA  &= !B00000001; // Off
////      PORTA  |= B00000001;  // On sets Arduino pin 22 (Digital)
      PORTA  = B00000001; // On
      //delay(300);
      //PORTA  = B00000000; // Off
      //delayMicroseconds(1);
 //  }
  

    if (! done) {
      unsigned long startTime = micros ();
    
        // Fast AnalogRead test
        SampleRate sr1 = readSignal(800, true);
        done = true;
        float freq = findNaturalFreq(sr1);
        unsigned long duration = micros () - startTime;
        Serial.print("Natural Freq: ");
        Serial.println(freq, 8);
        Serial.print("Samples: ");
        Serial.println(sr1.samples);
       // delay(10);
        Serial.println();
        Serial.print("E2E sampling duration: ");
        Serial.println(duration, 8);
        
    } 
    delay(1);
    PORTA  = B00000000; // Off
      
    delay(1);
  
  // digitalWrite(healthPin, LOW);
  //delayMicroseconds(60);
}

/**
 * 
 * Avg slope of last four data points positive is considered SlopeUp,
 * Anything else is considered slopeDown, as a catch all for noise.
 */
bool isSlopeDown(int currentIdx, SampleRate sr) {
    // skips immediate to avoid noise
    return ! (((float) (resultBuff[currentIdx] - resultBuff[currentIdx - 1]
    + resultBuff[currentIdx - 1] - resultBuff[currentIdx - 2]
    + resultBuff[currentIdx - 2] - resultBuff[currentIdx - 3]
    + resultBuff[currentIdx - 3] - resultBuff[currentIdx - 4]) / 4) > 0);
    
} 

/**
 * find index of 1st two peaks in result buffer. 
 */
int * findPeaks(SampleRate sr) {
    static int peaks[PEAKS] = {0, 0, 0, 0, 0};
    int runningPeak = 0;
    int runningPeakValue = 0;
    bool foundPeak = false;
    for (int i = 3; i < sr.samples - 3; i++) {
        // wait till we are slope up
        if(isSlopeDown(i, sr)) {
            if (foundPeak) {
                foundPeak = false;
                if (runningPeak < 5) {
                    runningPeak += 1;
                    runningPeakValue = 0;
                } else {
                    break;  
                }
            }
            continue;
        }
        // Now we are riding signal up. keeping highest seen value.
        Serial.print("SlopeUp!: ");
        Serial.println(i);
        if (runningPeakValue <= resultBuff[i]) {
            peaks[runningPeak] = i;
            runningPeakValue = resultBuff[i];
            foundPeak = true;
        } 
    }
    Serial.print("Peaks: ");
    for (int i = 0; i < PEAKS; i++) {
        Serial.print(peaks[i]);
        Serial.print(" : ");
    }
    Serial.println();
    return peaks;  
}

/** 
 * Finds st two peaks and computes frequency in peaks/microSeconds 
 * turned into Hz
 */
double findNaturalFreq(SampleRate sr) {
    int *peaks = findPeaks(sr);
    int deltas[PEAKS - 1] = {}; 
    
    for (int i = 1 ; i < PEAKS; i++) {
        int prev = *(peaks + (i - 1));
        int current = *(peaks + i); 
        
        deltas[i-1] = current - prev;
    }
    
    Serial.print("Deltas: ");
    for (int i = 0; i < PEAKS - 1; i++) {
        Serial.print(deltas[i]);
        Serial.print(" : ");

    }
    Serial.println();
    
    // remove noise, tollrance: 3 datapoints difference between deltas
    int suspect1 = -1;
    int suspect2 = -1;
    int tolerance = 3;
    int removed = 0;
    float acumDeltas = 0;
    for (int i = 0 ; i < PEAKS - 2; i++) {
        acumDeltas += deltas[i];
        int deltaDelta = deltas[i] - deltas[i + 1];
        if (abs(deltaDelta) > tolerance) {
            if (suspect1 > -1) {
                int deltaSuspect = deltas[suspect1] - deltas[i + 1];
                int guilty = abs(deltaSuspect) > tolerance ? suspect1 : suspect2;
                
                suspect1 = -1;
                suspect2 = -1;
                removed++;
                acumDeltas -= deltas[guilty];
                deltas[guilty] = -1;
            } else {
                if (i == PEAKS - 2) {
                    deltas[i + 1] = -1;
                    removed++;
                } 
                suspect1 = i;
                suspect2 = i + 1;
            }
        }
    }
    acumDeltas += (deltas[PEAKS - 2] > -1 ) ? deltas[PEAKS - 2] : 0;
    float avgDeltas = acumDeltas / ( PEAKS - 1 - removed);
    
    Serial.print("Deltas wothout noise: ");
    for (int i = 0; i < PEAKS - 1; i++) {
        Serial.print(deltas[i]);
        Serial.print(" : ");

    }
    Serial.println();
    Serial.print("AVG Deltas: ");
    Serial.println(avgDeltas);
    Serial.print("PEAKS - 1 - removed: ");
    Serial.println(PEAKS - 1 - removed);
    
    float period = avgDeltas * sr.sampleDuration;
    Serial.print("period: ");
    Serial.println(period, 4);
    
    return ((float) 1000000 / period);
}


/**
 * With 1000 datapoints I can describe at this sample rates:  
 *   500 Hz wave, 1.5 cycles. (good enouh to find max values)
 *   20KHz Is the max freq I Can describe accurately.  
 *   30KHz Is the max freq I Can describe accurately with noInterrupts() on.
 *   40KHz Still can see a signall, about 8 datapoints peak to peak with noInterrupts() on.
 *     Good to find max valtage in range.
 */
int cycle = 0;
SampleRate readSignal(int samples, bool isVerbose) {
    if (samples > BUFFER_SIZE) {
      // silently limiting samples to max
      samples = BUFFER_SIZE;
    }
    
        
    // Warm-up  
    for (int i = 0; i < 200; i++) {
        resultBuff[i] = analogRead (A1);
         
    }
    
    unsigned long startTime = micros ();
    for (int i = 0; i < samples; i++) {
        resultBuff[i] = analogRead (A1);
         
    }
    unsigned long duration = micros () - startTime;
    
    SampleRate sr = SampleRate(duration, samples);
    if (isVerbose && (cycle % 10) == 0) {
      for (int i = 0; i < samples; i++) {
          Serial.println(resultBuff[i]);     
      }  
      
      Serial.print("Duration: ");      
      Serial.println(duration);
      Serial.print("sample freq samples/MicroSeconds: ");      
      Serial.println(sr.sampleFrequency);
      Serial.print("samples: ");      
      Serial.println(sr.samples);
      Serial.print("sampleDuration: ");      
      Serial.println(sr.sampleDuration, 6);
      
      Serial.println();
      cycle = 0;
    }
    cycle++;
    return sr;
}

Notes:

Why do you expect the oscillations visible in pic. 1?

I expect any oscillation that results from a random LC circuit. after a pulse. As that'd be it's natural response. later I plan to hit it with pulses at that Fq. to get a higher voltage as a response.

In your pic it seems A1 is plugged into the row next to your resistor...

I put it like that in an attempt to lave less voltage on A1, (for safety?)

What kind of time constant do you expect from your LC circuit?

I don't want any constant, but my code to find the natural freq within 2KHz-44KHz.

Because the ADC returns such a large value, and if I see correctly from your oscilloscope pictures, the signal is probably much higher than the 5 volt peak voltage, and I'm guessing that's the problem because the microcontroller's built-in protection diodes are affecting the input signal. Perhaps a suitable resistor divider will help.

I think so, https://www.youtube.com/watch?v=psNAeHoZv0A&t=1264s
This should help I believe.
Will have to order, starter kits didn't bring that component.
Thanks!

signal is probably much higher than the 5 volt peak voltage,

scope shows max 5.8V when unplugged A1, max 3V when plugged.

Also interesting: natural response with plugged A1 is 21KHz, with A1 unplugged is 28.5KHz

later posting indicate this was unlikely to be you problem.
For good fidelity you want the loop from gnd clip to probe clip to be as small as possible. The loop forms an inductor that, with probe capacitance 'rings' as an LC circuit.

If I put the probe's GND anywhere closer I see either a square signal or the capacitor charging or discharging, exponential decay.
So what you are saying is the ringing comes from the Scope and not the LC in series?

I don't think that's the case, since when applying a pulse train at that Fq, the voltage maxes out. Which indicates I'm hitting the resonance freq of the LC, right?

If you use Aref as a reference for your input pin, you should add:

analogReference(EXTERNAL);

to your code.
If Aref is set to 3V you cannot measure above 3V.

If you add a resistor to a LC network, it will influence the response. But 20 Mohm should not really do much...

It seems to me both the capacitor and the coils are pretty big for 20 kHz. Did you do your calcs right?

Some coupling may occur if you put all your coils together like that. Better would be to have 1 coil. In RF coils are placed perpendicular so that their fields do not interfere (but you cannot put 9 coils perpendicular in a 3D world). Another way would be distancing.

You could add a buffer opamp on your board. Then the resistance of scope and A1 do not matter.

By the way, I am still missing the schematic.

I do not understand your explanation. As A1 is not connected to anything this way... also not to the resistor.

And to reduce the voltage you need a voltage divider (so also a resistance to ground).

Not the scope, the connection and internal circuitry of the 'scope probe.

Other can explain much better than me :slight_smile:

Plus a long ground lead and the current path of the ground forms an inductive loop. In a bad case that may be the whole mains return of the 'scope's grounding if gnd clip not connected.

For a series LC circuit, it will have minimum resistance at the resonant frequency because the reactance of the capacitor equals the reactance of the inductor but they have opposite signed (+/-) values. Also, it is possible for the voltage across each component to be greater than the applied voltage.

If you use Aref as a reference for your input pin, you should add:

analogReference(EXTERNAL);

Yes, I have missed that part at the time of my response, but the internal reference should be 5V (Mega2650). Yet it read 1023 (i.e. 5V) for every value (scope showed 3V).

It seems to me both the capacitor and the coils are pretty big for 20 kHz. Did you do your calcs right?

You are right, 1.67764Hz is the math. I did use Resonant Frequency Calculator for LC Circuit at the beginning but then went carried away with just testing with the scope to see if I read similar values. :confused:

Some coupling may occur if you put all your coils together like that. Better would be to have 1 coil. In RF coils are placed perpendicular so that their fields do not interfere (but you cannot put 9 coils perpendicular in a 3D world). Another way would be distancing.

I'll start over, for my goals it really didn't matter to get the math right at this point, I want consistency between scope and Arduino (waiting for a voltage divider) in the 2KHz-44KHz range. No idea why this is showing so high freq.

You could add a buffer opamp on your board. Then the resistance of scope and A1 do not matter.

Will investigate, had no idea, thanks.

By the way, I am still missing the schematic.

close as I can get with this tool. It has open ends, no cathode connected. Power comes from Arduino Digital pin with sleep intervals of 1ms.

I do not understand your explanation. As A1 is not connected to anything this way... also not to the resistor.
And to reduce the voltage you need a voltage divider (so also a resistance to ground).

As I said above, there is no cathode, I think that wave on screen is voltage changing as current flows back and forth between coils and capacitor, while dampening. But that Fq is far from the math. Maybe @stevemj has a point with scope's probe messing up? (but then why I got highest voltage around that "natural Freq", about max 44V readings).