CapMeter - capacity meter from 10pF to 10mF
Capacity meter with automatic selection of the measurement range.
Components and supplies
2
Test Probe Connector, Hook
1
Resistor 1M ohm
1
Arduino UNO
1
OLED Display, Blue on Black
1
Through Hole Resistor, 180 ohm
1
Through Hole Resistor, 50 kohm
Apps and platforms
1
Arduino IDE
Project description
Code
CapMeter_V2.ino
c_cpp
1/* 2 CapMeter V2.0 - capacity meter for Arduino. 3 4 Capacity meter with automatic selection of the measurement range. 5 It measures the charging and discharging times of the capacitor, 6 and converts the measured values into capacitance. 7 8 The measured capacity is from 10pF to 10mF (10000uF). 9 10 A one-time meter calibration is required. For calibration, you need to turn on 11 the meter with the calibration button pressed. Then connect the reference 12 capacitors, the value of which is indicated on the display. The connection 13 of each capacitor must be confirmed by pressing the calibration button. 14 15 Information is output to an external display and to a serial port. Customize 16 the code for the display you are using. 17 18 Warning! The capacitor must be completely discharged before measurement. 19 Otherwise, the device may be damaged. 20 21 Version 2.0 - added calibration. 22 Version 1.0 - first release. 23 24 (c) NaLex Software 25 26 Blog: http://nalexsoft.blogspot.com 27 28 Contact: nalexsoft@gmail.com 29*/ 30 31#define U8X8_ON 1 // U8x8lib or LiquidCrystal 32 33#if U8X8_ON 34// library for working with graphic displays 35#include <U8x8lib.h> 36// create a display object 37// clock, data, cs, dc, reset 38//U8X8_ST7565_KS0713_4W_SW_SPI lcd(11, 12, 9, 10, U8X8_PIN_NONE); 39U8X8_ST7565_64128N_4W_SW_SPI lcd(11, 12, 9, 10, U8X8_PIN_NONE); 40// set display contrast 41#define CONTRAST 60 42 43#else 44// library for working with LCD 45#include <LiquidCrystal.h> 46// create a display object 47// RS, E, DB4, DB5, DB6, DB7 48LiquidCrystal lcd(12, 11, 10, 9, 8, 7); 49#endif 50 51 52#include <EEPROM.h> 53 54#define ADC_input A0 55#define ADC_max 255 // ADCH 8-bit 56#define numOuts 3 57#define AUTORANGE_ON 1 58 59enum controls 60{ 61 kSelectUp = 0, 62 kSelectDown, 63 64 kNum 65}; 66//bool swState[kNum]; 67bool swOn[kNum]; 68uint8_t swPin[kNum]; 69 70int input, lowLevel, highLevel, calibrationCap[numOuts]; 71long int counter, counterLimit[numOuts], corrCounter, resultCounter[2]; 72bool charge, measurementBreak, calibrationOn; 73uint8_t out[numOuts]; 74int8_t select_old = -1, select = 0; // start range, 0 for autorange 75float divider[numOuts], cap[2]; 76char* nominal[numOuts]; 77 78// the setup routine runs once when you press reset 79void setup() 80{ 81 // initialize serial communication at 9600 bits per second 82 Serial.begin(9600); 83 84 // set control pins 85 swPin[kSelectUp] = 6; 86 swPin[kSelectDown] = 5; 87 88 // set out pins 89 out[0] = 2; // 180 Ohms, uF 90 out[1] = 3; // 50k Ohms, nF 91 out[2] = 4; // 1M Ohms, pF 92 93 // set calibration caps 94 calibrationCap[0] = 47; // uF 95 calibrationCap[1] = 220; // nF 96 calibrationCap[2] = 1000; // pF 97 98 // set nominals 99 nominal[0] = "uF"; 100 nominal[1] = "nF"; 101 nominal[2] = "pF"; 102 103 // switches setup 104 for (int i = 0; i < kNum; i++) 105 { 106 //swState[i] = false; 107 swOn[i] = false; 108 pinMode(swPin[i], INPUT_PULLUP); 109 } 110 111 // ADC setup 112 ADMUX = 0x60 - A0 + ADC_input; // ADC input, 0x60 is A0, 0x61 is A1, ... 113 ADCSRA = 0xe2; 114 // clock divider, 0b010 -> 4, 0b011 -> 8, 0b100 -> 16 115 bitWrite(ADCSRA, 0, 0); 116 bitWrite(ADCSRA, 1, 0); 117 bitWrite(ADCSRA, 2, 1); 118 119 // thresholds setup 120 lowLevel = ADC_max / 3.; // 1/3 VCC like NE555 121 highLevel = ADC_max - lowLevel; // 2/3 VCC like NE555 122 123 // display setup 124#if U8X8_ON 125 lcd.begin(); 126 lcd.setContrast (CONTRAST); 127 //lcd.setFlipMode (0); // mirror 128 lcd.setFlipMode (1); // rotation 180 deg 129 // set font 130 lcd.setFont(u8x8_font_px437wyse700b_2x2_r); 131 //lcd.setFont(u8x8_font_amstrad_cpc_extended_f); 132#else 133 lcd.begin(16, 2); 134#endif 135 // clear memory 136 lcd.clear(); 137 138 setDividers(); 139 140 // if calibration 141 // check dividers to zero 142 float minDiv = divider[0]; 143 for (int i = 0; i < numOuts; i++) minDiv = min(minDiv, divider[i]); 144 //if (digitalRead(swPin[kSelectUp]) == 0 && digitalRead(swPin[kSelectDown]) == 0) calibration(); 145 if (digitalRead(swPin[kSelectUp]) == 0 || minDiv <= 0) calibration(); 146 calibrationOn = false; 147} 148 149// the loop routine runs over and over again forever 150void loop() 151{ 152 // switches 153 for (int i = 0; i < kNum; i++) 154 { 155 bool butt = digitalRead (swPin[i]); 156 // trigger 157 if (butt == 0 && !swOn[i]) 158 { 159 //swState[i] = !swState[i]; 160 swOn[i] = true; 161 // buttons action 162 if (i == kSelectUp) select++; 163 if (i == kSelectDown) select--; 164 // loop limits 165 if (select >= numOuts) select = 0; 166 if (select < 0) select = numOuts - 1; 167 } 168 if (butt == 1 && swOn[i]) swOn[i] = false; 169 } 170 171 selector(); 172 draw(); 173 delay (500); 174} 175 176void setDividers() 177{ 178 // set dividers 179 for (int i = 0; i < numOuts; i++) 180 EEPROM.get(i * sizeof(float), divider[i]); 181 182 // set counter limits 183 counterLimit[0] = divider[0] * 11e3; 184 counterLimit[1] = divider[1] * 5e3; 185 counterLimit[2] = divider[2] * 5e3; 186} 187 188void selector() 189{ 190 // pins setup 191 if (select_old != select) 192 { 193 select_old = select; 194 for (int i = 0; i < numOuts; i++) 195 if (i == select) pinMode(out[i], OUTPUT); 196 else pinMode(out[i], INPUT); 197 digitalWrite(out[select], LOW); 198 for (int i = 0; i < 2; i++) cap[i] = 0; 199 delay(100); 200 } 201 202 // read the input on analog pin, in range 0..1023 (10-bit) 203 input = ADCH; //analogRead(ADC_input); // A0 is pin 14, A1 is pin 15 204 205 // charging 206 if (input < lowLevel) 207 { 208 charge = true; 209 digitalWrite(out[select], HIGH); 210 measurement(0); 211 } 212 // discharging 213 else if (input > highLevel) 214 { 215 charge = false; 216 digitalWrite(out[select], LOW); 217 measurement(1); 218 } 219 else if (!calibrationOn && AUTORANGE_ON) 220 { 221 select--; 222 if (select < 0) select = 0; 223 } 224} 225 226void measurement(uint8_t i) 227{ 228 corrCounter = counter = 0; 229 measurementBreak = false; 230 if (charge) 231 while (ADCH < highLevel) 232 { 233 counter++; 234 if (ADCH < lowLevel) corrCounter++; 235 if (counter > counterLimit[select]) 236 { 237 measurementBreak = true; 238 break; 239 } 240 } 241 else 242 while (ADCH > lowLevel) 243 { 244 counter++; 245 if (ADCH > highLevel) corrCounter++; 246 if (counter > counterLimit[select]) 247 { 248 measurementBreak = true; 249 break; 250 } 251 } 252 resultCounter[i] = counter - corrCounter; 253 cap[i] = double(resultCounter[i]) / divider[select]; 254 if (!calibrationOn && AUTORANGE_ON) 255 { 256 if (measurementBreak) select = 0; //--; 257 else 258 { 259 // range auto detect 260 if (cap[i] < 2) select++; // limit setup 261 if (cap[i] > 2500) select--; // limit setup 262 //if (select >= numOuts || select < 0 || counter == 0 || cap[i] < 0) select = 0; 263 if (select >= numOuts || select < 0 || cap[i] < 0) select = 0; 264 } 265 } 266} 267 268void draw() 269{ 270 float capAverage = (cap[0] + cap[1]) / 2.; 271 272 lcd.clear(); 273 lcd.setCursor(0, 0); 274 /*char txt[20]; 275 sprintf(txt, "Cap,%s", nominal[select]);*/ 276 lcd.print("Cap," + String(nominal[select])); //txt); 277#if U8X8_ON 278 lcd.setCursor(0, 3); 279#else 280 lcd.setCursor(0, 1); 281#endif 282 lcd.print(!measurementBreak ? String(capAverage) : "OVER"); 283 284 // print to serial port 285#if 0 286 // debug 287 for (int i = 0; i < numOuts; i++) 288 Serial.println(divider[i]); 289 Serial.println("---"); 290 Serial.println(counterLimit[select]); 291 Serial.println(corrCounter); 292 Serial.println(String(counter)); 293 Serial.println("---"); 294 Serial.println(String(input)); 295 Serial.println(String(cap[0]) + " + " + String(cap[1])); 296 Serial.println("---"); 297#endif 298 Serial.println(!measurementBreak ? String(capAverage) + " " + String(nominal[select]) : "OVER"); 299 Serial.println(); 300} 301 302void dualPrint(String str) 303{ 304 lcd.print(str); 305 Serial.println(str); 306} 307 308void calibration() 309{ 310 calibrationOn = true; 311 lcd.setCursor(0, 0); 312 lcd.clear(); 313 dualPrint("Release"); 314 while (digitalRead(swPin[kSelectUp]) != 1 || digitalRead(swPin[kSelectDown]) != 1) delay (100); 315 for (int i = 0; i < numOuts; i++) 316 { 317 select = i; 318 counterLimit[i] = 1e5; 319 while (digitalRead(swPin[kSelectUp]) != 0 && digitalRead(swPin[kSelectDown]) != 0) 320 { 321 // measurement 322 selector(); 323 divider[i] = double(resultCounter[0] + resultCounter[1]) / calibrationCap[i] / 2.; // average 324 // draw 325 lcd.clear(); 326 lcd.setCursor(0, 0); 327 dualPrint(String(calibrationCap[i]) + String(nominal[select])); 328#if U8X8_ON 329 lcd.setCursor(0, 3); 330#else 331 lcd.setCursor(0, 1); 332#endif 333 dualPrint(String((cap[0] + cap[1]) / 2.)); // average 334 delay (500); 335 } 336 if (digitalRead(swPin[kSelectDown]) != 0) // else skip EEPROM write 337 EEPROM.put(i * sizeof(float), divider[i]); 338 lcd.clear(); 339 dualPrint("Release"); 340 while (digitalRead(swPin[kSelectUp]) != 1 || digitalRead(swPin[kSelectDown]) != 1) delay (100); 341 } 342 setDividers(); 343} 344
CapMeter_V2-1.ino
c_cpp
1/* 2 CapMeter V2.1 - capacity meter for Arduino. 3 4 Capacity meter with automatic selection of the measurement range. 5 It measures the charging and discharging times of the capacitor, 6 and converts the measured values into capacitance. 7 8 The measured capacity is from 10pF to 10mF (10000uF). 9 10 A one-time meter calibration is required. For calibration, you need to turn on 11 the meter with the calibration button pressed. Then connect the reference 12 capacitors, the value of which is indicated on the display. The connection 13 of each capacitor must be confirmed by pressing the calibration button. 14 15 Information is output to an external display and to a serial port. Customize 16 the code for the display you are using (U8X8_ON and I2C_LCD_ON macros). 17 18 Warning! The capacitor must be completely discharged before measurement. 19 Otherwise, the device may be damaged. 20 21 Version 2.1 - added support for various displays. 22 Version 2.0 - added calibration. 23 Version 1.0 - first release. 24 25 (c) NaLex Software 26 27 Blog: http://nalexsoft.blogspot.com 28 29 Contact: nalexsoft@gmail.com 30*/ 31 32#define U8X8_ON 0 // U8x8lib (1) or LiquidCrystal (0) 33#define I2C_LCD_ON 1 // Serial (1) or Parallel (0) LiquidCrystal 34 35#if U8X8_ON 36// library for working with graphic displays 37#include <U8x8lib.h> 38// create a display object 39// clock, data, cs, dc, reset 40//U8X8_ST7565_KS0713_4W_SW_SPI lcd(11, 12, 9, 10, U8X8_PIN_NONE); 41U8X8_ST7565_64128N_4W_SW_SPI lcd(11, 12, 9, 10, U8X8_PIN_NONE); 42// set display contrast 43#define CONTRAST 60 44 45#else 46#if I2C_LCD_ON 47// library for working with LCD 48#include <Wire.h> 49#include <LiquidCrystal_I2C.h> 50// create a display object 51LiquidCrystal_I2C lcd(0x27, 16, 2); 52 53#else 54// library for working with LCD 55#include <LiquidCrystal.h> 56// create a display object 57// RS, E, DB4, DB5, DB6, DB7 58LiquidCrystal lcd(12, 11, 10, 9, 8, 7); 59#endif 60#endif 61 62 63#include <EEPROM.h> 64 65#define ADC_input A0 66#define ADC_max 255 // ADCH 8-bit 67#define numOuts 3 68#define AUTORANGE_ON 1 69 70enum controls 71{ 72 kSelectUp = 0, 73 kSelectDown, 74 75 kNum 76}; 77//bool swState[kNum]; 78bool swOn[kNum]; 79uint8_t swPin[kNum]; 80 81int input, lowLevel, highLevel, calibrationCap[numOuts]; 82long int counter, counterLimit[numOuts], corrCounter, resultCounter[2]; 83bool charge, measurementBreak, calibrationOn; 84uint8_t out[numOuts]; 85int8_t select_old = -1, select = 0; // start range, 0 for autorange 86float divider[numOuts], cap[2]; 87char* nominal[numOuts]; 88 89// the setup routine runs once when you press reset 90void setup() 91{ 92 // initialize serial communication at 9600 bits per second 93 Serial.begin(9600); 94 95 // set control pins 96 swPin[kSelectUp] = 6; 97 swPin[kSelectDown] = 5; 98 99 // set out pins 100 out[0] = 2; // 180 Ohms, uF 101 out[1] = 3; // 50k Ohms, nF 102 out[2] = 4; // 1M Ohms, pF 103 104 // set calibration caps 105 calibrationCap[0] = 47; // uF 106 calibrationCap[1] = 220; // nF 107 calibrationCap[2] = 1000; // pF 108 109 // set nominals 110 nominal[0] = "uF"; 111 nominal[1] = "nF"; 112 nominal[2] = "pF"; 113 114 // switches setup 115 for (int i = 0; i < kNum; i++) 116 { 117 //swState[i] = false; 118 swOn[i] = false; 119 pinMode(swPin[i], INPUT_PULLUP); 120 } 121 122 // ADC setup 123 ADMUX = 0x60 - A0 + ADC_input; // ADC input, 0x60 is A0, 0x61 is A1, ... 124 ADCSRA = 0xe2; 125 // clock divider, 0b010 -> 4, 0b011 -> 8, 0b100 -> 16 126 bitWrite(ADCSRA, 0, 0); 127 bitWrite(ADCSRA, 1, 0); 128 bitWrite(ADCSRA, 2, 1); 129 130 // thresholds setup 131 lowLevel = ADC_max / 3.; // 1/3 VCC like NE555 132 highLevel = ADC_max - lowLevel; // 2/3 VCC like NE555 133 134 // display setup 135#if U8X8_ON 136 lcd.begin(); 137 lcd.setContrast (CONTRAST); 138 //lcd.setFlipMode (0); // mirror 139 lcd.setFlipMode (1); // rotation 180 deg 140 // set font 141 lcd.setFont(u8x8_font_px437wyse700b_2x2_r); 142 //lcd.setFont(u8x8_font_amstrad_cpc_extended_f); 143 144#else 145#if I2C_LCD_ON 146 lcd.init(); 147 lcd.backlight(); 148 149#else 150 lcd.begin(16, 2); 151#endif 152#endif 153 // clear memory 154 lcd.clear(); 155 156 setDividers(); 157 158 // if calibration 159 // check dividers to zero 160 float minDiv = divider[0]; 161 for (int i = 0; i < numOuts; i++) minDiv = min(minDiv, divider[i]); 162 //if (digitalRead(swPin[kSelectUp]) == 0 && digitalRead(swPin[kSelectDown]) == 0) calibration(); 163 if (digitalRead(swPin[kSelectUp]) == 0 || minDiv <= 0) calibration(); 164 calibrationOn = false; 165} 166 167// the loop routine runs over and over again forever 168void loop() 169{ 170 // switches 171 for (int i = 0; i < kNum; i++) 172 { 173 bool butt = digitalRead (swPin[i]); 174 // trigger 175 if (butt == 0 && !swOn[i]) 176 { 177 //swState[i] = !swState[i]; 178 swOn[i] = true; 179 // buttons action 180 if (i == kSelectUp) select++; 181 if (i == kSelectDown) select--; 182 // loop limits 183 if (select >= numOuts) select = 0; 184 if (select < 0) select = numOuts - 1; 185 } 186 if (butt == 1 && swOn[i]) swOn[i] = false; 187 } 188 189 selector(); 190 draw(); 191 delay (500); 192} 193 194void setDividers() 195{ 196 // set dividers 197 for (int i = 0; i < numOuts; i++) 198 EEPROM.get(i * sizeof(float), divider[i]); 199 200 // set counter limits 201 counterLimit[0] = divider[0] * 11e3; 202 counterLimit[1] = divider[1] * 5e3; 203 counterLimit[2] = divider[2] * 5e3; 204} 205 206void selector() 207{ 208 // pins setup 209 if (select_old != select) 210 { 211 select_old = select; 212 for (int i = 0; i < numOuts; i++) 213 if (i == select) pinMode(out[i], OUTPUT); 214 else pinMode(out[i], INPUT); 215 digitalWrite(out[select], LOW); 216 for (int i = 0; i < 2; i++) cap[i] = 0; 217 delay(100); 218 } 219 220 // read the input on analog pin, in range 0..1023 (10-bit) 221 input = ADCH; //analogRead(ADC_input); // A0 is pin 14, A1 is pin 15 222 223 // charging 224 if (input < lowLevel) 225 { 226 charge = true; 227 digitalWrite(out[select], HIGH); 228 measurement(0); 229 } 230 // discharging 231 else if (input > highLevel) 232 { 233 charge = false; 234 digitalWrite(out[select], LOW); 235 measurement(1); 236 } 237 else if (!calibrationOn && AUTORANGE_ON) 238 { 239 select--; 240 if (select < 0) select = 0; 241 } 242} 243 244void measurement(uint8_t i) 245{ 246 corrCounter = counter = 0; 247 measurementBreak = false; 248 if (charge) 249 while (ADCH < highLevel) 250 { 251 counter++; 252 if (ADCH < lowLevel) corrCounter++; 253 if (counter > counterLimit[select]) 254 { 255 measurementBreak = true; 256 break; 257 } 258 } 259 else 260 while (ADCH > lowLevel) 261 { 262 counter++; 263 if (ADCH > highLevel) corrCounter++; 264 if (counter > counterLimit[select]) 265 { 266 measurementBreak = true; 267 break; 268 } 269 } 270 resultCounter[i] = counter - corrCounter; 271 cap[i] = double(resultCounter[i]) / divider[select]; 272 if (!calibrationOn && AUTORANGE_ON) 273 { 274 if (measurementBreak) select = 0; //--; 275 else 276 { 277 // range auto detect 278 if (cap[i] < 2) select++; // limit setup 279 if (cap[i] > 2500) select--; // limit setup 280 //if (select >= numOuts || select < 0 || counter == 0 || cap[i] < 0) select = 0; 281 if (select >= numOuts || select < 0 || cap[i] < 0) select = 0; 282 } 283 } 284} 285 286void draw() 287{ 288 float capAverage = (cap[0] + cap[1]) / 2.; 289 290 lcd.clear(); 291 lcd.setCursor(0, 0); 292 /*char txt[20]; 293 sprintf(txt, "Cap,%s", nominal[select]);*/ 294 lcd.print("Cap," + String(nominal[select])); //txt); 295#if U8X8_ON 296 lcd.setCursor(0, 3); 297#else 298 lcd.setCursor(0, 1); 299#endif 300 lcd.print(!measurementBreak ? String(capAverage) : "OVER"); 301 302 // print to serial port 303#if 0 304 // debug 305 for (int i = 0; i < numOuts; i++) 306 Serial.println(divider[i]); 307 Serial.println("---"); 308 Serial.println(counterLimit[select]); 309 Serial.println(corrCounter); 310 Serial.println(String(counter)); 311 Serial.println("---"); 312 Serial.println(String(input)); 313 Serial.println(String(cap[0]) + " + " + String(cap[1])); 314 Serial.println("---"); 315#endif 316 Serial.println(!measurementBreak ? String(capAverage) + " " + String(nominal[select]) : "OVER"); 317 Serial.println(); 318} 319 320void dualPrint(String str) 321{ 322 lcd.print(str); 323 Serial.println(str); 324} 325 326void calibration() 327{ 328 calibrationOn = true; 329 lcd.setCursor(0, 0); 330 lcd.clear(); 331 dualPrint("Release"); 332 while (digitalRead(swPin[kSelectUp]) != 1 || digitalRead(swPin[kSelectDown]) != 1) delay (100); 333 for (int i = 0; i < numOuts; i++) 334 { 335 select = i; 336 counterLimit[i] = 1e5; 337 while (digitalRead(swPin[kSelectUp]) != 0 && digitalRead(swPin[kSelectDown]) != 0) 338 { 339 // measurement 340 selector(); 341 divider[i] = double(resultCounter[0] + resultCounter[1]) / calibrationCap[i] / 2.; // average 342 // draw 343 lcd.clear(); 344 lcd.setCursor(0, 0); 345 dualPrint(String(calibrationCap[i]) + String(nominal[select])); 346#if U8X8_ON 347 lcd.setCursor(0, 3); 348#else 349 lcd.setCursor(0, 1); 350#endif 351 dualPrint(String((cap[0] + cap[1]) / 2.)); // average 352 delay (500); 353 } 354 if (digitalRead(swPin[kSelectDown]) != 0) // else skip EEPROM write 355 EEPROM.put(i * sizeof(float), divider[i]); 356 lcd.clear(); 357 dualPrint("Release"); 358 while (digitalRead(swPin[kSelectUp]) != 1 || digitalRead(swPin[kSelectDown]) != 1) delay (100); 359 } 360 setDividers(); 361} 362
Downloadable files
CapMeter V2 Connection
CapMeter V2 Connection

CapMeter V2 Circuit
CapMeter V2 Circuit
Comments
Only logged in users can leave comments