Arduino UNO Voltage Measurement with 100:1 Divider - Detailed Example

This example demonstrates how to measure voltage using an Arduino UNO and a 100:1 voltage divider. The code can handle both AC and DC voltage inputs and provides the following outputs:

The divider allows the measurement of high voltages (up to 500V peak) while keeping the input within the Arduino's 0-5V range.

Detailed Steps

Arduino Code


/*
* Arduino UNO with 100:1 Voltage Divider
* Measures AC/DC voltage, calculates peak, RMS, and frequency for AC signals.
* © 2024 Copyright Peter I. Dunne, all rights reserved
* Prepared for educational use
* The ADC is 10 bit, this is of relatively low accuracy, use professional test equipment for accuracy
* Released under the Mozilla Public License
*/

const int voltagePin = A0;      // Voltage input connected to A0
const float voltageDividerRatio = 200.0;  // 200:1 divider
const float offset = 2.5;       // 2.5V offset for AC signals
const float VRef = 5.0;         // Reference voltage
const int maxADCValue = 1023;   // 10-bit ADC resolution
const int threshold = 512;
const unsigned long interval = 500; // 500ms hold period if AC is detected

// Variables for AC calculations
unsigned long lastZeroCrossingTime = 0;
float sumSquaredVoltage = 0;
int sampleCount = 0;
bool polarity =false; // used by AC, DC detection
bool zcd = false; // zero cross detection, part of the AC component
unsigned long previousACmillis = 0;    // Stores the last time the action was taken
int zcp = 512; // zero crossing point

unsigned long previousMillis = 0;    // Stores the last time the action was taken
float frequency = 0;

void setup() {
   Serial.begin(115200);  // Start serial communication
   Serial.println("Arduino DMM by Peter Ivan Dunne, ©2024, all rights reserved");
   Serial.println("Released under the Mozilla Public License");
   Serial.println("https://jazenga.com/educational");
   Serial.println("Purpose: to demonstrate use of ADC system as a voltage meter for both AC and DC voltages");
   Serial.println("Auto detection of AC and measurement of AC frequency");
}

void loop() {
   // Read the analog value from the voltage sensor
   int adcValue = analogRead(voltagePin);
   
   // Convert ADC value to voltage
   float voltage = (adcValue / float(maxADCValue)) * VRef;
   // Adjust for the 2.5V offset
   float adjustedVoltage = voltage - offset;
   // Calculate the actual input voltage considering the voltage divider
   float inputVoltage = adjustedVoltage * voltageDividerRatio;

  unsigned long currentMillis = millis();
  unsigned long currentMicros = micros();
 // phase change detection 
  // if AC, it can detect the change at any moment
  // if AC it assumes AC for not less than the interval, initially set at 500ms
  // the timer is reset every time a 0v crossing occurs to ensure it stays in AC mode for as long as AC is present on the input
  if (polarity!=adcValue>zcp){ 
   polarity = adcValue>zcp; 
    if (polarity){
   // add hysterisis to prevent false triggering
    zcp=threshold-10; 
    // period is calculated only on the positive edge for greater accuracy
     previousACmillis = currentMillis;
     unsigned long period = currentMicros - lastZeroCrossingTime;  // Time between zero crossings
     lastZeroCrossingTime = currentMicros;
     // Calculate frequency in Hz
     if (period>0){
     frequency = 1000000.0 / (period);  // Period is in microseconds
     } else {
   // add hysterisis to prevent false triggering
       zcp=threshold+10;
       }   
    zcd = true;
    }
   }
 if ((currentMillis - previousACmillis) <= interval){
       // AC signal processing: track peak voltage and sum squared voltage for RMS calculation
       sumSquaredVoltage += inputVoltage * inputVoltage;  // Sum of squares for RMS calculation
       sampleCount++;

       // Detect zero crossing (when the voltage crosses 2.5V)
       if (zcd && polarity) {
           // Output AC parameters
           if ((currentMillis - previousMillis) >= interval) {
            // Save the last time action was taken
             previousMillis = currentMillis;
          // Calculate RMS voltage
             float rmsVoltage = sqrt(sumSquaredVoltage / sampleCount);
             Serial.print(" RMS Voltage: ");
             Serial.print(rmsVoltage, 2);
             Serial.print(" V, Frequency: ");
             Serial.print(frequency, 2);
             Serial.println(" Hz");
             sumSquaredVoltage = 0;
             sampleCount = 0;  
          }
       }
   } else {
       // For DC, output the voltage directly
       // update the output every 500ms
  if ((currentMillis - previousMillis) >= interval) {
   // Save the last time action was taken
   previousMillis = currentMillis;
       Serial.print(" DC Voltage: ");
       Serial.print(inputVoltage);
       Serial.println(" V");
    }
   }
}

How It Works