/*CAP-ESR-FREQ meter with Arduino Duemilanove. Version 1.1 December 2018. A metering instrument with three different applications: AlexP Capacitor metering for capacitors from 100nF up to 2000 uFarad or more. See the digital capacitor meter (C) 2013 Hackerstore. https://www.hackerstore.nl/BlogArtikel/4 Measuring the value of the capacitor: Capacitance is a measure of the ability of a capacitor to store electrical charge. The Arduino capacitance meter relies on the same basic property of capacitors: the time constant. The time constant of a capacitor is defined as the time it takes for the voltage across the capacitor to reach 63.2% of its voltage when fully charged. An Arduino can measure capacitance because the time a capacitor takes to charge is directly related to its capacitance by the next equation: TC = R x C. TC is the time constant of the capacitor (in seconds). R is the resistance of the circuit (in Ohms). C is the capacitance of the capacitor (in Farads). The formula to get the capacitance value in Farads is C = TC/R. In this meter the R value can be set for calibration between 15kOhm and 25 kOhm via potmeter P1. In the formula for the calculation a value of 22 kOhm is used. Adjustment is possible with potmeter P1. the full analog value on the ANALOG_PIN is 1023, so 63.2% is a value of 647. ESR metering (Equivalent Serie Resistance). See for definition of ESR https://en.wikipedia.org/wiki/Equivalent_series_resistance. ESR can be used to find faulty capacitors durung repair sessions. See https://forum.arduino.cc/index.php?topic=80357.0 Thanks to szmeu for the start of this forum topic and mikanb for his esr50_AutoRange design. Frequency meter from 1 Hz. to 3 MHz. See https://forum.arduino.cc/index.php?topic=324796.0#main_content_section Thanks to arduinoaleman. The different meters are selected via a three position selector switch, connected to pins A1, A2 and A3. A0: esr input. A1: capacitor meter selection. A2: ESR meter selection. A3: frequency meter selection. A4: the esr zeroing and general reset button. A5: capacitor input. D5: frequency input. See https://www.instructables.com/id/Arduino-CAP-ESR-FREQ-Meter for assembly instructions and how to use it. LCD Library and settings used by all programs********************** */ #include LiquidCrystal lcd(13,11,6,4,3,2);// these setting can be altered when needed. // note: an lcd screen with i2C connections could not be used, this setup conflicted with the other software used. int wait =2000; int rememberSetting=0; // Used to force a reset after changing the meter type selection. //capacitor meter initialisation ************************************ #include #define ANALOG_PIN A5 // Analog pin for measuring. The value can be between 0 and 1023. #define P1_PIN 12 // Pin for charging the capacitor via R9 & P1. #define R8_PIN 7 // Pin for discharging the capacitor via R8. float MicroFarad =0; long tStart =0; float nDuur =0; //esr meter initialisation ****************************************** //The prescaler is changed for the ADC to make the conversion happen faster. //this code section was sugested on the arduino forum. #include #define FASTADC 1 // defines for setting and clearing register bits #ifndef cbi #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) #endif #ifndef sbi #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) #endif #define DISCHARGE_PIN 8 #define PULSE_HighRange_PIN 10 #define PULSE_LowRange_PIN 9 #define ESR_PIN A0 #define BUTTON_PIN A4 unsigned long measureESR(void); //measuring function, increases ADC to 14-bit resolution by oversampling unsigned long esrSamples; double milliVolts; double esrCalib; double vRef = 1.1; //voltage on the Vref pin (this sketch uses internal voltage reference 1.1V) double milliAmps = 48.48; //(in mA) needed only for "Zeroing". //Proper calibration can be done entering the right value for the milliAmps, (U=I*R). // in the ideal situation "milliAmps" is 50 mA, this condition is true only if R10 is 100 Ohm, // and Vcc is exactly 5V and the transistor while fully saturated idealy is at 0 ohm. double Rs = 1003.0; //proper calibration can be done entering the right value for Rs. //Enter the value also in the "IF" branch! This is only the initialization of the Upper Range! double Vin = 5000; double Rm; boolean esrRange = false; int stabilizer = 0; //freq meter initialisation ******************************************************** /* Pin D5 of Arduino must be used for frequency input. Counter1 : 16 bits / used to count the frequency impules Counter2 : 8 bits / used to generate a 1000ms or 100ms gate time for measurement I use "bitClear(TIMSK0,TOIEO)" instead of "TIMSK0 &=~(1< volatile unsigned long frequency=0; volatile boolean measurement_ready; volatile unsigned char overflow_counter; // number of overflows within gate_time volatile unsigned int time_so_far; // number of ISR calls volatile unsigned int gate_time; ISR(TIMER2_COMPA_vect) { if (time_so_far >= gate_time) { // end of gate time, measurement is ready TCCR1B &= B11111000; // stop counter1 by setting CS12, CS11 and CS10 to "0" bitClear(TIMSK2,OCIE2A); // disable counter2 interrupts bitSet(TIMSK0,TOIE0); // enable Timer0 again // millis and delay measurement_ready=true; // set global flag for end count period // calculate now frequeny value frequency=0x10000 * overflow_counter; // mult #overflows by 65636 (0x10000) frequency += TCNT1; // add counter1 contents for final value overflow_counter=0; // reset overflow counter } else { time_so_far++; // count number of interrupt events if bitRead(TIFR1,TOV1) { // if Timer/Counter 1 overflow flag = "1" then ... overflow_counter++; // increase number of counter1 overflows bitSet(TIFR1,TOV1); // reset counter1 overflow flag } }; } void setup() { //setup used by all meter types*********************************************** pinMode (A1,INPUT_PULLUP); // select capacitor meter S2. pinMode (A2,INPUT_PULLUP); // select esr meter S2. pinMode (A3,INPUT_PULLUP); // select frequency meter S2. pinMode (A4,INPUT_PULLUP); // select reset/zeroing button S3. lcd.begin(16, 2); // lcd screen with 2 rows with each 16 characters. // LDC test for (int row=0; row<=1; row++) { for (int column=0; column<=16; column++) { lcd.setCursor(column,row); lcd.print("*"); delay(20);} }; delay(wait); // three different setups, selected by the switch S2. (A1, A2 and A3). //capacitor meter setup******************************************************* if(digitalRead(A1)==LOW) {rememberSetting=1; pinMode(P1_PIN, OUTPUT); // connection for charging the capacitor. digitalWrite(P1_PIN, LOW); lcd.clear(); lcd.setCursor(0,0); lcd.print("Capacitor meter"); lcd.setCursor(5,1); lcd.print("version 1.1"); delay(wait); lcd.clear(); } //esr meter setup************************************************************* if(digitalRead(A2)==LOW) {rememberSetting=2; if (FASTADC) { sbi(ADCSRA,ADPS2); //seting prescaler to 32 for faster adc (500khz) cbi(ADCSRA,ADPS1); //at 500khz results are still looking good (same values as if 250khz ADC clock) sbi(ADCSRA,ADPS0); // the shorter the pulse on a small value capacitor it has no time to charge and denaturate the result } pinMode(ESR_PIN, INPUT); //reading milliVolts pinMode(PULSE_HighRange_PIN, OUTPUT); digitalWrite(PULSE_HighRange_PIN,HIGH); //low enables Q1 pinMode(PULSE_LowRange_PIN, OUTPUT); digitalWrite(PULSE_LowRange_PIN,HIGH); //low enables Q2 pinMode(DISCHARGE_PIN, OUTPUT); digitalWrite(DISCHARGE_PIN,HIGH); //high enables Q3 pinMode(BUTTON_PIN,INPUT); //setting up for a button (for zeroing) digitalWrite(BUTTON_PIN,HIGH); //enabling the pull up on the button, when button pressed to the ground zeroes out the cable analogReference(INTERNAL); //setting vRef to internal reference 1.1V lcd.clear(); lcd.setCursor(0,0); lcd.print("ESR meter"); lcd.setCursor(5,1); lcd.print("version 1.1"); delay(wait); lcd.clear(); eeprom_read_block((void*)&esrCalib, (void*)0, sizeof(esrCalib)); //reading calibration value, it will be ok if already calibrated, else it might be bogus depends on the content of EEPROM but will be ok after first calibration lcd.clear(); } //frequency meter setup******************************************************* if(digitalRead(A3)==LOW) {rememberSetting=3; pinMode(5,INPUT); lcd.clear(); lcd.setCursor(0,0); lcd.print("Frequency meter"); lcd.setCursor(5,1); lcd.print("version 1.1"); delay(wait); lcd.clear(); } } // three different loops, selected by the switch S2. (A1, A2 and A3). void loop() { //capacitor meter loop********************************************************* if(digitalRead(A1)==LOW) { if (rememberSetting !=1) //force a reset. {softReset();} if(digitalRead(A4)==LOW) {softReset();} // reset via reset button on A4. Discharge(); // discharge the capacitor until zero voltage. lcd.setCursor(0,1); lcd.blink(); // cursor blinks during measuring. digitalWrite(P1_PIN, HIGH); // charge the capacitor. tStart = millis(); while(analogRead(ANALOG_PIN)<647){}; nDuur = abs(millis()-tStart); MicroFarad = (nDuur/22000.0) * 1000; // calculate the value of the capacitor. delay(wait); lcd.noBlink(); // show the calculated value on the lcd screen: if(MicroFarad >= 1.0){ // show the value in microFarad: lcd.clear(); lcd.print(" "); lcd.print(MicroFarad,1); lcd.print(" "); lcd.print((char)228); // print the greek character mu. lcd.print("F "); delay(wait); } else{ // show if no capacitor is connected or the value is too low to be accurate: if (MicroFarad < 0.07) {lcd.clear(); lcd.print(" no capacitor");} // show the value in nanoFarad: else{ lcd.clear(); lcd.print(" "); lcd.print(MicroFarad*1000,1); lcd.print(" "); lcd.print("nF "); delay(wait); } } } //esr meter loop************************************************************** if(digitalRead(A2)==LOW) { if (rememberSetting !=2) //force a reset. {softReset();} esrSamples = measureESR(); milliVolts = (esrSamples * vRef) / 16.384; //Divide by 16.384 due to a 14-bit oversampling Rm = Rs / ((Vin / milliVolts) - 1); //voltage divider (R2=R1(U2/(U1-U2))) Rm = Rm - esrCalib; //Compensate cable resistance if (Rm < 0) Rm = 0; //Do not show eventual negative values if (stabilizer == 3) { //Make three measurements before autoranging the ESR. This is hysteresis to avoid oscillation when autoranging. if (Rm < 5.5) { Rs = 100.2; //If needed, adjust this value for low range measurement (Below 6 Ohms) esrRange = false; } else if (Rm > 5.8) { Rs = 1003.0; //If needed, adjust this value for high range measurement (Above 6 Ohms and below 50 Ohms) esrRange = true; } stabilizer = 0; } lcd.clear(); lcd.setCursor(0,0); lcd.print("Vout = "); lcd.print(milliVolts); lcd.print("mV"); lcd.setCursor(1,1); //If measurement is less than 50 Ohms show the result, otherwise show
    (Overload) on the display. if (Rm <= 50){ lcd.print("ESR = "); lcd.print(Rm,2); lcd.print((char)244); // print the greek character ohm. } else { lcd.print("ESR =
      "); } //for zeroing the cables, this can be quite a big resistance compared to the values we intend to measure //so it is a good idea to try to reduce in any way posible this influence (short cables, soldering the cables, etc) if(!digitalRead(BUTTON_PIN)){ lcd.clear(); lcd.print("Zeroing..."); esrCalib = milliVolts/milliAmps; lcd.print(" done!"); lcd.setCursor(0,1); eeprom_write_block((const void*)&esrCalib, (void*)0, sizeof(esrCalib)); lcd.print("saved to EEPROM"); delay(wait); } } // frequency meter loop****************************************************** if(digitalRead(A3)==LOW) { if (rememberSetting !=3) //force a reset. {softReset();} if(digitalRead(A4)==LOW) {softReset();} // reset via reset button on A4. float floatfrq; int range; long frq; measurement(1000); // 1000ms standard gate time while (measurement_ready==false); frq=frequency; floatfrq=frq; // type conversion (required!!) if ((frq >= 0)&& (frq < 10)) {range=0;}; // Hertz if ((frq >= 10)&& (frq < 100)) {range=1;}; if ((frq >= 100)&& (frq < 1000)) {range=2;}; if ((frq >= 1000)&& (frq < 10000)) {range=3; floatfrq=floatfrq/1000;}; // KHz if ((frq >= 10000)&& (frq < 100000)) {range=4; floatfrq=floatfrq/1000;}; if ((frq >= 100000)&& (frq < 1000000)) {range=5; floatfrq=floatfrq/1000;}; if (frq >= 1000000) {range=6; floatfrq=floatfrq/1000000;};// MHz lcd.setCursor(0, 0); lcd.print("Freq: "); switch(range) { case 0: if (frq!=0) {lcd.setCursor(6, 0);lcd.print(frq); lcd.setCursor(13,0);lcd.print("Hz ");} else {lcd.clear();lcd.setCursor(0, 0); lcd.print("No signal... ");} break; case 1: // 10Hz thru 100Hz lcd.setCursor(6, 0); lcd.print(frq); lcd.setCursor(13,0); lcd.print("Hz "); break; case 2: // 100Hz thru 1KHz lcd.setCursor(6, 0); lcd.print(frq); lcd.setCursor(13,0); lcd.print("Hz "); break; case 3: // 1KHz thru 10KHz lcd.setCursor(6, 0); lcd.print(floatfrq,3); lcd.setCursor(13,0); lcd.print("kHz"); break; case 4: // 10KHz thru 100KHz lcd.setCursor(6, 0); lcd.print(floatfrq,2); lcd.setCursor(13,0); lcd.print("kHz"); break; case 5: // 100KHz thru 1MHz lcd.setCursor(6, 0); lcd.print(floatfrq,1); lcd.setCursor(13,0); lcd.print("kHz"); break; case 6: // above 1MHz lcd.setCursor(6, 0); lcd.print(floatfrq,3); lcd.setCursor(13,0); lcd.print("MHz"); break; } } } // Subroutines: // discharge capacitor subroutine********************************** void Discharge() { digitalWrite(P1_PIN, LOW); pinMode(R8_PIN, OUTPUT); digitalWrite(R8_PIN, LOW); while(analogRead(ANALOG_PIN)>0){} ; pinMode(R8_PIN, INPUT); delay(wait); } // reset subroutine************************************************* void softReset(){ lcd.clear(); lcd.setCursor(0,0); lcd.print("please reset...."); while(digitalRead(A4)!=LOW){} ; asm volatile (" jmp 0"); } // esr measure subroutine******************************************** unsigned long measureESR() { unsigned long accumulator = 0; unsigned int sample = 0; int i = 0; // oversampling 256 times (for 14 bit is 4^(desiredResolution - ADCresolution) where is 4^(14-10) = 4^4 = 256) while ( i++ < 256 ) { digitalWrite(DISCHARGE_PIN, HIGH); // discharge the capacitors delayMicroseconds(600); // discharge wait time digitalWrite(DISCHARGE_PIN, LOW); // disable discharging if (esrRange == false) { digitalWrite(PULSE_LowRange_PIN,LOW); // start milliAmps pulse delayMicroseconds(5); // on the scope it appears that after enabling the pulse a small delay is needed for oscillations to fade away sample = analogRead(ESR_PIN); // read ADC value digitalWrite(PULSE_LowRange_PIN, HIGH); // stop milliAmps pulse } if (esrRange == true) { digitalWrite(PULSE_HighRange_PIN,LOW); // start milliAmps pulse delayMicroseconds(5); // on the scope it appears that after enabling the pulse a small delay is needed for oscillations to fade away sample = analogRead(ESR_PIN); // read ADC value digitalWrite(PULSE_HighRange_PIN, HIGH); // stop milliAmps pulse } accumulator += sample; // accumulate sampling values } esrSamples = accumulator >> 4; // decimate the accumulated result stabilizer ++; return esrSamples; } // frequency measure subroutine******************************************** void measurement(int ms) { bitClear(TIMSK0,TOIE0); // disable counter0 in order to disable millis() and delay() // this will prevent extra interrupts that disturb the measurement delayMicroseconds(66); // wait for other interrupts to finish gate_time=ms; // usually 1000 (ms) // setup of counter 1 which will be used for counting the signal impulses TCCR1A=0; // reset timer/counter1 control register A TCCR1B=0; // reset timer/counter1 control register B TCCR2A=0; // reset timer/counter1 control register A TCCR2B=0; // reset timer/counter2 control register A // setup of counter2 which will be used to create an interrupt every millisecond (used for gate time) TCCR2B |= B00000101; // set prescale factor of counter2 to 128 (16MHz/128 = 125000Hz) // by setting CS22=1, CS21=0, CS20=1 bitSet(TCCR2A,WGM21) ; // set counter2 to CTC mode // WGM22=0, WGM21=1, WGM20=0 OCR2A = 124; // CTC divider will divide 125Kz by 125 measurement_ready=0; // reset time_so_far=0; // reset bitSet(GTCCR,PSRASY); // reset the prescaler TCNT1=0; // set frequency counter1 to 0 TCNT2=0; // set gate time counter2 to 0 bitSet(TIMSK2,OCIE2A); // enable counter2 interrupts TCCR1B |= B00000111; // set CS12, CS11 and CS10 to "1" which starts counting // on Arduino pin D5 }