We all know about voltmeters. Besides multimeters, there are some panel meters that show voltage, current, etc. Making such an AC voltmeter is sometimes becomes very helpful. Here in this article, we are going to learn how we can make our own single-phase AC voltmeter using a PIC16F76 micro-controller and capacitor power supply. So let’s start!

Disclaimer: Electricity is always dangerous. Proper skill is required to work with electricity. Do work at your own risk. The author will not be responsible for any misuse or harmful act or any mistake you make. The contents of this website are unique and copyright protected. Kindly don’t do any nonsensical act copying and claiming it as yours. Most of the articles published here are kept as open-source to help you. Take the knowledge for free and use it, but if you are interested you can buy the ready resources offered here. If you need any help or guide feel free to comment below, the author will try to help you. Thanks.

Warning!: The circuit uses direct AC. Connect the phase and neutral as like the circuit. Also, the circuit may carry live voltages. Do work on your own responsibility. Always check the phase using a tester before touching any part directly.

Before we start, if you missed the article on seven segments How to interface 4×1 7segment display with PIC micro-controller and Transformerless power supply design guide in detail with a calculator then I’ll request you to read those article first and if you already read those, thanks.

As theories are explained in those articles, so I’m escaping those and starting the circuit.

## Circuit diagram:

Here, C1 is a high voltage capacitor, used for our capacitor power supply with other components like R2, R1, D1, D2, U2 & C2 & 3. This is a kind of capacitor power supply where we kept the neutral line common with the circuit GND pin. That is why you must ensure that you connected the phase and neutral correctly. Otherwise, high voltage shock is very easy to get.

Anyway, don’t be afraid. Here, R4 & R5 are used as the sensing resistors and R6 is used to protect the ADC pin although the ADC pins are internally protected. We just reduced the current flow. The other part on the right side is the display side. No, I did not use any resistor network but you can use it. Or just use 8 different resistors.

Note that, R1 gets a little bit warm. I tested this and found that it is over 40’C after 8hrs of a run. But no problem was found and the meter is running continuously since 27-06-2021, 3 PM.

## MikroC coding:

The code is written in mikroC Pro for PIC. And of course, I always use valid licenses.

```/*******************************************************************************
Program for, Simple AC voltmeter
Program written by_ Engr. Mithun K. Das; mithun060@gmail.com
MCU:PIC16F76; X-Tal:8MHz. C: mikroC pro for PIC v7.6.0
Date: 28-06-2021; Original file: 03-11-2013
*******************************************************************************/

{
switch (num)
{
case 0 : return 0xC0;
case 1 : return 0xF9;
case 2 : return 0xA4;
case 3 : return 0xB0;
case 4 : return 0x99;
case 5 : return 0x92;
case 6 : return 0x82;
case 7 : return 0xF8;
case 8 : return 0x80;
case 9 : return 0x90;
case 10: return 0x40;
case 11: return 0x79;
case 12: return 0x24;
case 13: return 0x30;
case 14: return 0x19;
case 15: return 0x12;
case 16: return 0x02;
case 17: return 0x78;
case 18: return 0x00;
case 19: return 0x10;
case 20: return 0x8E; //F
}
}
unsigned short  portb_index,type;
unsigned int digit;
unsigned short portb_array;
unsigned int k = 0;
unsigned int max_point = 0,temp = 0;

void InitTimer1()
{
T1CON         = 0x01;
TMR1IF_bit    = 0;
TMR1H         = 0xD8;
TMR1L         = 0xF0;
TMR1IE_bit    = 1;
INTCON        = 0xC0;
}

void Interrupt() iv 0x0004 ics ICS_AUTO
{
if (TMR1IF_bit)
{
TMR1IF_bit = 0;
TMR1H         = 0xD8;
TMR1L         = 0xF0;
if(type)
{
PORTB = ~(portb_array[portb_index]);
if(portb_index==2)
{
RC7_bit = 0;
RC6_bit = 1;
RC5_bit = 1;
}
if(portb_index==1)
{
RC7_bit = 1;
RC6_bit = 0;
RC5_bit = 1;
}
if(portb_index==0)
{
RC7_bit = 1;
RC6_bit = 1;
RC5_bit = 0;
}
}
if(!type)
{
PORTB = (portb_array[portb_index]);
if(portb_index==2)
{
RC7_bit = 1;
RC6_bit = 0;
RC5_bit = 0;
}
if(portb_index==1)
{
RC7_bit = 0;
RC6_bit = 1;
RC5_bit = 0;
}
if(portb_index==0)
{
RC7_bit = 0;
RC6_bit = 0;
RC5_bit = 1;
}
}

portb_index ++ ;
if (portb_index > 2)portb_index = 0;
}
}

void Get_AC_Voltage(void);

void Display_AC(void)
{
digit = (number / 1u) % 10u;
digit = (number / 10u) % 10u;
digit = number / 100u;
}

void main()
{

type = 0; //segment type

TRISA = 0xFF;
TRISB = 0x00;
PORTB = 0xff;
TRISC= 0x00;
PORTC = 0x00;

// tiemr1 settings...
InitTimer1();

digit = 0;
portb_index = 0;
number = 0;

Display_AC(void);

while(1)
{

Get_AC_Voltage(void);

}//Endless loop;
}//End.

void Get_AC_Voltage(void)
{
int mm;
max_point = 0;//clear all data
for(mm=0;mm<60;mm++)
{
for(k=0;k<1000;k++)
{
{
max_point = temp;
}
}

max_point = 0;//clear all data
temp = 0;
}
number = adc_rd0; // Convert the result in millivolts
Display_AC(void);
max_point = 0;//clear all data
}

// end```

Here, the first block converts the PORTB for each digit of the seven-segment display.

```unsigned short mask(int num)
{
switch (num)
{
case 0 : return 0xC0;
case 1 : return 0xF9;
case 2 : return 0xA4;
case 3 : return 0xB0;
case 4 : return 0x99;
case 5 : return 0x92;
case 6 : return 0x82;
case 7 : return 0xF8;
case 8 : return 0x80;
case 9 : return 0x90;
case 10: return 0x40;
case 11: return 0x79;
case 12: return 0x24;
case 13: return 0x30;
case 14: return 0x19;
case 15: return 0x12;
case 16: return 0x02;
case 17: return 0x78;
case 18: return 0x00;
case 19: return 0x10;
case 20: return 0x8E; //F
}
}```

Then the interrupt part shows the digits sequentially and trigger the data pins sequentially. This sequence and data pattern are predefined for each type of segment (CA/CC).

```void Interrupt() iv 0x0004 ics ICS_AUTO
{
if (TMR1IF_bit)
{
TMR1IF_bit = 0;
TMR1H         = 0xD8;
TMR1L         = 0xF0;
if(type)
{
PORTB = ~(portb_array[portb_index]);
if(portb_index==2)
{
RC7_bit = 0;
RC6_bit = 1;
RC5_bit = 1;
}
if(portb_index==1)
{
RC7_bit = 1;
RC6_bit = 0;
RC5_bit = 1;
}
if(portb_index==0)
{
RC7_bit = 1;
RC6_bit = 1;
RC5_bit = 0;
}
}
if(!type)
{
PORTB = (portb_array[portb_index]);
if(portb_index==2)
{
RC7_bit = 1;
RC6_bit = 0;
RC5_bit = 0;
}
if(portb_index==1)
{
RC7_bit = 0;
RC6_bit = 1;
RC5_bit = 0;
}
if(portb_index==0)
{
RC7_bit = 0;
RC6_bit = 0;
RC5_bit = 1;
}
}

portb_index ++ ;
if (portb_index > 2)portb_index = 0;
}
}```

Code is written in a simple way so that you can understand it easily. After that, you can short it yourself as I do for myself.

``` void Display_AC(void)
{
digit = (number / 1u) % 10u;
digit = (number / 10u) % 10u;
digit = number / 100u;
}```

The Display_AC function simply converting the number into PORTB pin configuration.

And here in Get_AC_voltage() ?

```void Get_AC_Voltage(void)
{
int mm;
max_point = 0;//clear all data
for(mm=0;mm<60;mm++)
{
for(k=0;k<1000;k++)
{
{
max_point = temp;
}
}

max_point = 0;//clear all data
temp = 0;
}
number = adc_rd0; // Convert the result in millivolts
Display_AC(void);
max_point = 0;//clear all data
}```

First, we are seeking the peak point of each cycle of the AC signal. Then multiplying with a constant 137/100 (I kept this way to reduce RAM consumption rather than writing 1.37). After each peak, we just calculated the RMS voltage of each cycle. But to get a pure and stable result we must get an average value from multiple peak points. That is why we took an average value of 60 of those.

This is very simple. If you have any problem understanding, feel free to comment below. I’ll reply at a suitable time.

## PCB:

Most of the time, I make PCBs for each project or product. Here is the PCB for our AC voltmeter using a PIC16F76 micro-controller and capacitor power supply. Note that I kept it multifunctional so that I can show how we can use the same design for different purposes. For this project, just follow the circuit diagram given above.

## Test result:

I used resistors which is 5% intolerance. Using 1% resistors will improve the measuring performance.

## Conclusion:

This project was very simple and useful if you use it for good. By minor changes, this can be a good product too. I think this article will help you. Don’t forget to subscribe, more projects are coming soon. Thanks for reading. JLCPCB – Only \$2 for PCB Prototype (Any Color)

24 Hours fast turnaround, Excellent quality & Unbeatable prices

\$18 Welcome Bonus for new registrations Now!!! https://jlcpcb.com

Check this: 6V Lead-Acid battery Charger Circuits.

#### MKDas

I'm Mithun from Bangladesh. Electronics is my passion, love to work with electronics. This is my personal blog. Besides my regular works, I write some articles here. Most of them are open-source and everything is presented in the easiest way so that anyone can understand and learn it. Thanks. #### RK Hamy · June 29, 2021 at 3:58 am

Many Thanks for the excellent project.

As per my understanding…
you takes 1000 samples from AC waveform while finds maximum (adc) value in that 1000 numbers.
then you calculate rms voltage value of that maximum number. after collecting 60 RMS voltage samples You divide it by 60 to get average RMS AC Voltage.

Can you kindly explain the RMS calculation part
what is 137/100 or 1.37 (I cannot understand)

Again many thanks #### max · June 30, 2021 at 12:15 am

thank you sir  