Arduino Uno code for voltage sensor

I have made a voltage sensor circuit for 230 volt ac by using step down transformer (230/6),bridge rectifier,zener diode,filter capacitor and voltage divider circuit.The output of the voltage divider circuit(10Kohm) to Arduino Uno is 4.75V DC.Can anyone please help me to write a code so that i can sense the mains supply voltage??

Have you tried to use analogRead()?

Next question is what you want to do with the values that you read.

I am doing a project on Direct torque control for induction motor. I have sensed current using hall effect current sensor but voltage sensor i have designed the circuit as said above.after that i need to calculate the flux and torque using the estimated instanteneous voltage and current values.

Vinie:
I have made a voltage sensor circuit for 230 volt ac by using step down transformer (230/6),bridge rectifier,zener diode,filter capacitor and voltage divider circuit.The output of the voltage divider circuit(10Kohm) to Arduino Uno is 4.75V DC.Can anyone please help me to write a code so that i can sense the mains supply voltage??

Did you bias your voltage sense circuit at 2.5V? You are going to want it to be at half your ADC range when the voltage is 0V, so that it can swing positive and negative.

I gave a response here that explained how to get an accurate RMS reading of an AC signal here -> Help needed with school project using Arduino! - #3 by BigBobby - Project Guidance - Arduino Forum

It seems you're at 50Hz, however? You should then modify my example from 60Hz to 50Hz.

@BigBobby I need the RMS value of the voltage each time.

@ BigBobby Thank you for your response... Can you please send me the codefor this in Aurdino Uno as i am having only hardware background and new to Aurdino Uno..

OK, well...here's the part of the code that I can make quickly since I've done it many times on other processors.

Note that this code isn't very good, however, as it doesn't leave much execution time left to do anything but measure the RMS voltage. I have 3 todos in there to fix that, but I'd need to read the AVR/avr-gcc/Arduino documentation to see how to address them. I'm sure other people on the forum would know how to do the todos off the top of their head, but it seems the forum prefers to help people help themselves as opposed to writing the code for them.

I obviously don't have your circuit here, so I simulated your AD samples assuming that you were turning 230V/50Hz into an isolated signal at the ADC offset by 2.5V and 4.75Vpp.

#include "Arduino.h"

// General Constants
#define US_PER_SEC                1000000
#define SQRT2                     1.41421356237

// A/D Constants
#define AD_VOLTAGE_MAX            5                     // V
#define AD_BITS                   (10)                  // bits
#define AD_VALUE_MAX              ((1 << AD_BITS) - 1)  // counts

// Utility Voltage Constants
#define UTILITY_VOLTAGE           230       // Vrms
#define UTILITY_FREQUENCY         50        // Hz

// Measurement Circuit Constants
#define SAMPLES_PER_CYCLE         64        // samples
#define SQRT_SAMPLES_PER_CYCLE    8         // samples
#if SQRT_SAMPLES_PER_CYCLE*SQRT_SAMPLES_PER_CYCLE != SAMPLES_PER_CYCLE
  #error SQRT_SAMPLES_PER_CYCLE must = sqrt(SAMPLES_PER_CYCLE)
#endif
#define SAMPLE_PERIOD             US_PER_SEC/(UTILITY_FREQUENCY*SAMPLES_PER_CYCLE)    // us
#define AD_OFFSET_V               2.5       // V
#define AD_VOLTAGE_PEAK_TO_PEAK_V 4.75      // Vpp

// Measurement Circuit Conversion Factors
#define AD_OFFSET                 ((uint16_t)(AD_VALUE_MAX * AD_OFFSET_V / AD_VOLTAGE_MAX + 0.5)) // counts
#define AD_SCALE                  ((uint16_t)((float)(1L << 16) * AD_VOLTAGE_MAX / AD_VALUE_MAX * UTILITY_VOLTAGE * SQRT2 / (AD_VOLTAGE_PEAK_TO_PEAK_V / 2) / SQRT_SAMPLES_PER_CYCLE  + 0.5)) // V/counts

uint32_t sample_time_last;      // Schedules sample time (bad method...see todo below).
uint8_t sample_num;             // Counts samples.
uint32_t sample_accum;          // Accumulates the sum of the squared-samples.
uint16_t voltage_rms;           // The RMS voltage in Volts.

// Table to simulate A/D sampling of utility voltage since I don't have an actual circuit attached to my Arduino.
// These samples were generated assuming a circuit takes UTILITY_VOLTAGE and scales it so that it has AD_VOLTAGE_PEAK_TO_PEAK_V range
// as the AD pin, offset by AD_OFFSET_V;
uint16_t AD_Samples[SAMPLES_PER_CYCLE] = {512,  559,  606,  653,  697,  741,  781,  820,
                                          855,  887,  916,  940,  960,  977,  988,  995,
                                          997,  995,  988,  977,  960,  940,  916,  887,
                                          855,  820,  781,  741,  697,  653,  606,  559,
                                          512,  464,  417,  370,  326,  282,  242,  203,
                                          168,  136,  107,  83,   63,   46,   35,   28,
                                          26,   28,   35,   46,   63,   83,   107,  136,
                                          168,  203,  242,  282,  326,  370,  417,  464};

void setup()
{
 Serial.begin(9600);
 sample_time_last = micros() - SAMPLE_PERIOD; // Prime to start sampling immediatley.
 sample_num = 0;
 sample_accum = 0;
}

void loop()
{
  // Schedule periodic samples of the AC voltage.
  // Note scheduling this in loop() will result in inconsistent sample interval if you're doing anything other than monitoring the AC voltage.
  // todo - use a timer interrupt to start AD samples at a consistent interval.
  if(micros() - sample_time_last > SAMPLE_PERIOD)
  {
    // Read sample of the AC voltage.
    // Note that this takes ~100us, which is very wasteful since SAMPLE_PERIOD = ~300us.  You'll spend 1/3 of your time doing nothing but waiting for the AD samples!
    // todo - use an A/D interrupt to read the AD samples when they're ready.
    //int16_t sample = analogRead(A0);   // Sample the AC voltage (in real system, I don't have an analog circuit here).
    int16_t sample = AD_Samples[sample_num++]; // Since I don't have a circuit, simulate the AC voltage samples
    sample -= AD_OFFSET; // subtract offset.
    sample_accum += (int32_t)sample*sample; // Accumulate squared samples.

    // When all samples are taken for this cycle, calculate the RMS voltage.
    if(sample_num >= SAMPLES_PER_CYCLE)
    {
      // Calculate RMS and print to the screen.
      voltage_rms = ((uint32_t)AD_SCALE * isqrt(sample_accum) + (1L << 15)) >> 16;
      Serial.println(voltage_rms);  // debug only - delete this in your actual code.

      // Reset for next RMS calculation.
      sample_accum = 0;
      sample_num = 0;
    }
    sample_time_last += SAMPLE_PERIOD;
  }
}

// Function created from example here -> https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Example_2
// Seems to take ~50us on Arduino Uno. This can be made faster at the expense of larger function size.
uint16_t isqrt(uint32_t squared_val) {
  uint32_t res = 0;
  uint32_t bit = 1L << 30; // The second-to-top bit is set.
  uint32_t num = squared_val;

  // "bit" starts at the highest power of four <= the argument.
  while (bit > num) bit >>= 2;

  while (bit != 0)
  {
    if (num >= res + bit)
    {
      num -= res + bit;
      res = (res >> 1) + bit;
    }
    else
    {
      res >>= 1;
    }
    bit >>= 2;
  }

  // The calculated sqrt won't be rounded up.  Check if we should round up.
  {
    uint32_t res_check = (res << 1) + 1; // (res + 0.5) * 2.
    if((res_check*res_check) <= (((uint64_t)squared_val) << 2))
    {
      // If squaring (res + 0.5) is less than the original number, then round up.
      res++;
    }
  }
  return res;
}

// todo - A real system would be better off measuring the frequency of the incoming AC and adjusting the sample
//        rate so that SAMPLES_PER_CYCLE are evenly spaced in one AC period as opposed to assuming UTILITY_FREQUENCY.
//        Note that you should just need to frequency lock the samples to the AC period.  Phase locking isn't necessary.
//        Ways to do that:
//           1. If you don't want to mess around, you can use a circuit to make a square wave that is high when the AC is positive
//              and low when the AC is negative.  You could then easily use an interrupt pin to measure the actual AC frequency.
//           2. I think that the AVR might have a comparator module for the AD inputs.  If so, then you could set the comparator to
//              AD_OFFSET and use that instead.
//           3. If I'm wrong about the comparator module and you really don't want to make a circuit, you can look for zero crossings
//              in the AD samples, however this is much less effective.
//void frequency_measurement_interrupt(void)
//{
//}

The output:

230
230
230
230
230
230
230
230
230
230
230
230
230
...

Recognize that unless you measure the voltage and current at the same time, if there is any phase shift (which induction motors are notorious for), your calculations will be wrong. In a purely resistive load, the voltage and current are in phase (the current peaks at the same time the voltage does), but in an inductive situation, they current and voltage are NOT in phase with each other so you will need to consider that in your calculations.

@gpsmikey whenever my motor will start my current measurment unit and voltage measurment unit will instantenously measure both isn't it??

Vinie:
@gpsmikey whenever my motor will start my current measurment unit and voltage measurment unit will instantenously measure both isn't it??

Nope - from your initial description with a transformer and rectifier/smoothing, you are approximating the RMS voltage (or peak depending on just how you calibrate things), but you are not measuring the voltage at a specific point in time during the cycle. The transformer/rectifier/filter combination will only give you an average value. To get an instantaneous value, you would basically need the transformer (for isolation/step down) and then a reference (zero crossing etc.) that would allow you to measure the voltage at a particular time on the waveform (you also would need to address the issue of not letting the voltage go below zero on the input of the arduino - you risk letting the "magic smoke" out :o )

const int currentPin = 3;
const unsigned long sampleTime = 100000UL;                           // sample over 100ms, it is an exact number of cycles for both 50Hz and 60Hz mains
const unsigned long numSamples = 250UL;                               // choose the number of samples to divide sampleTime exactly, but low enough for the ADC to keep up
const unsigned long sampleInterval = sampleTime/numSamples;  // the sampling interval, must be longer than then ADC conversion time
const int adc_zero = 510;                                                     // relative digital zero of the arudino input from ACS712 (could make this a variable and auto-adjust it)

void setup()
{
 Serial.begin(9600);
}

void loop()
{
 unsigned long current = 0;
 unsigned int count = 0;
 unsigned long prevMicros = micros() - sampleInterval ;
 while (count < numSamples)
 {
   if (micros() - prevMicros >= sampleInterval)
   {
     long adc_raw = analogRead(currentPin) - adc_zero;
     current += (unsigned long)(adc_raw * adc_raw);
     ++count;
     prevMicros += sampleInterval;
   }
 }
 
 float rms = sqrt((float)currentAcc/(float)numSamples) * (50.0000/ 1024.0);
 Serial.println(rms);
}

Yikes...it seems I should have read more closely about your circuit in your initial post. Rectifying and filtering isn't a good way to measure an RMS voltage. You won't be able to get a true RMS measurement that way, and changes in the voltage will be reflected very slowly by your circuit.

As for the code you just posted, did you have that before or did you modify my code?

They pretty much do the same thing, although they both suffer from the same issues where it would be hard for your Arduino to do anything but the RMS calculation. You should really use interrupts for your sampling to get around that.

Also, while sampling over 100ms does address an input at either 50 or 60Hz, that's not the only reason why you'd want to frequency-lock your RMS code to your input waveform. The Arduino clock frequency is not precise since it's using an inexpensive resonator, so sampleInterval needs to be something slightly different from sampleTime/numSamples to have the desired number of samples per cycle. The effect of not being locked is that your RMS measurement will appear to have a low frequency ripple given a steady input.

Converting to floating point to use the builtin sqrt() function is unnecessary too, since it will use more execution time than the equivalent integer calculation. You'd be better off keeping everything in integer math if you can.