Time sensitive sine wave representation values

Hi there

First I will discuss the HW.

Im using an arduino uno with a 30A ACS712 sensor and a ZMPT101B voltage module to "catch" each value from a "X" amount of time.

With that, I´m using EXCEL graph funcions to represent the sine wave for voltage and current. And then I can find out the phase shift of both (sorry for my english).

I have this skecth that I did on the fly and retrofitted from a watt metter that I´m also coding using both sensors, so it´s ugly but in its primal way does the job

Here is the code

double volt_neg = 1024;
double volt_pos = 0;
double volt_rms;
double amp_pos = 0;
double amp_neg = 1024;
double amp_rms;
int var = 0;

void setup() {
  Serial.begin(115200);
}
void loop()
{
  /*Voltaje();
  Corriente();*/

  //unsigned int start = 0;
  while (var < 20)
  {
    Voltaje();
    Corriente();
    var++;
    delay(1);
  }
  /*var = 0;

    Serial.println("Voltaje MAX");
    Serial.println(volt_pos);
    Serial.println("Voltaje MIN");
    Serial.println(volt_neg);
    Serial.println("Voltaje Eficaz");
    Serial.print(volt_rms);
    Serial.println("V");*/
}

void Voltaje()
{
  int volt = analogRead(A0);
  volt = map(volt, 285, 740, -335, 335);
  Serial.print(volt/1.414);Serial.println("V");
  /*if ( volt > volt_pos) {
    volt_pos = volt;
  } else if (volt < volt_neg) {
    volt_neg = volt;
  }
  volt_rms = ((volt_pos + (-volt_neg)) / 2) / sqrt(2);*/
}

void Corriente()
{
  int amp = analogRead(A5); //lectura del sensor
  amp = map(amp, 106, 916, -30, 30);
  Serial.print(amp/1.414);Serial.println("A");
  /*if ( amp > amp_pos) {
    amp_pos = amp;
  } else if (amp < amp_neg) {
    amp_neg = amp;
  }
  amp_rms = ((amp_pos + (-amp_neg)) / 2) / sqrt(2); //Ecuación  para obtener la corriente*/
}

I have noticed that serial print have some sort of "delay" because I wanted to take 20 samples, each at 1ms apart (corresponding to 50Hz that is the Frequency of my country)

However, I have intervals of 1ms, 2ms, 4ms, 7 ms, and I think is the serial print that is doing that

I thought of store the values in an array, and then, after the sampling is done and there is no more time sensitive demand, print the 20 values of AMP, VOLT and MILLIS()

However, im not very good at array manipulation.

So, if anyone is interested, care to debug the code?

Also maybe someone can find this proyect interesting.

Here is a picture of my sinewave graph. Blue is voltage and orange is current (you can notice that the sketch skipped a reading).

Regards

Printing takes a variable amount of time, that can't be predicted.

So, if you want to sample voltage and current at regular intervals, use millis() for timing, store the results in an array, and print after collecting all the data.

Great tutorials on safely monitoring the household AC line at How to build an Arduino energy monitor - measuring mains voltage and current — OpenEnergyMonitor 0.0.1 documentation

jremington:
Printing takes a variable amount of time, that can't be predicted.

So, if you want to sample voltage and current at regular intervals, use millis() for timing, store the results in an array, and print after collecting all the data.

Great tutorials on safely monitoring the household AC line at How to build an Arduino energy monitor - measuring mains voltage and current — OpenEnergyMonitor 0.0.1 documentation

Thanks and no offense, but I grasped those concepts. I just don´t do well with manipulating an array.

I know that I need to use millis() and the array, and after the array is full with the amount of samples I want, THEN print in the serial monitor.

But, can you spare some toughts on how to store in the array at each increment?
}
Thanks

Why go to all the bother of taking samples just to work out the phase shift. Simply implement a zero crossing detect for the voltage and current signals and measure the time between them.

Grumpy_Mike:
Why go to all the bother of taking samples just to work out the phase shift. Simply implement a zero crossing detect for the voltage and current signals and measure the time between them.

The thing is that I need to represent in a graphic the sine wave of each component to be measured.

Your approach is good in terms of phase shift. But I also need to check for the quality of the sine wave delivered from the power supplier.

I like the attempt of zero crossing detection in software. As soon as you get consecutive values below and above zero, the zero crossing time can be computed. Add time stamps to the values, and interleave measuring the voltage and current signals, and you're almost done. With free running analog samples at sufficiently high frequency the equivalence of sin(x)=x near x=0 can simplify the zero crossing time calculation.

But, can you spare some toughts on how to store in the array at each increment?

float volts[20],amps[20];

//take 20 samples during a full cycle of a 50 Hz signal (period=20 milliseconds)

for (byte i=0; i<20; i++) {
volts[i] = read_volts();
amps[i] = read_amps();
delayMicroseconds(1000-delta); //adjust delta to take into account time to read volts and amps
}

But I also need to check for the quality of the sine wave delivered from the power supplier.

You will not see much in the way of harmonic distortion even if some is present sampling at that speed and resolution.
Is this an assignment where actual usefulness comes second to fulfilling some brief?

This is similar to post #6, except I've done the delay a bit differently. Also I would recommend storing the raw analogRead values in the array and move the floating point conversions outside of the data collection loop for two reasons. First floating point operations are slow, but more importantly the execution time may be dependent upon the values causing sample jitter.

If your interest is in the relative phase of the two signals, for instance if you are using these reading to calculate power factor, then recognize that there is a phase offset component between the Voltage and Current samples introduced by the sequential ADC sampling.

Code compiled, but not tested:

#define samples 20
#define voltPin A0
#define currPin A5

int VoltArray[samples] ;
int CurrArray[samples] ;
unsigned long nextMillis ;

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

void loop() {
  nextMillis = millis() + 1 ;
  for (int k = 0; k < samples; k++) {
    // Data collection in this loop
    while (millis()+1 == nextMillis) {} ;    // Spin until next millisecond
    VoltArray[k] = analogRead(voltPin) ;
    CurrArray[k] = analogRead(currPin) ;
    nextMillis++ ;
  }

  for (int k = 0; k < samples; k++) {
    // Do unit conversions and output in this loop
    Serial.print(VoltArray[k]) ;
    Serial.print(", ") ;
    Serial.print(CurrArray[k]) ;
  }
}

If this is mains with a fixed frequency you can compute the phasor of V and I relative
to an internally generated quadrature signal of the same frequency, and take the phase difference
of the two phasors.

With any sampled system its vital to sample at regular intervals with minimum jitter.

If you arrange the sampling rate to be an integer multiple of the frequency in question, you sine table
can be small too.

All the correlation calculations can be integer/fixed point, you only need floats for the atan2() call.

The most common distortion on a mains supply , is third Harmonic - 150hz.
You need to sample at more than 300hz to see that . If you so measure current and voltage at this rate , your Arduino will be very busy ...esp if you want to calculate phase and power.
Being delivered a supply with significant distortion would, I think, be very rare /unlikely due to the damage it can create. So measure zero crossing or as I’ve said many times here, buy an electricity meter , which takes care of everything and will work out safer/cheaper

Hi thanks all for the valuable insight

The other day I started slow after making the post with the array manipulation given the KnightRider demo on the community and came up with this.

#include "avdweb_AnalogReadFast.h"

#define sensVolt A0
#define sensAmp A5
#define samples 40

int voltArray[samples];
float ampArray[samples];
unsigned long timeArray[samples];
unsigned long startTime;
bool runned = false;

void setup()
{
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println("Time;Voltage;Amperage");
}

void loop()
{
  //startTime = millis();
  if (runned == false)
  {
    sampling();
    runned = true;
  }
  else
  {
    reading();
    delay(50000);
  }
}//Loop()

void reading()
{
  int i = 0;
  for (i = 0; i < samples; i++)
  {
    Serial.print(timeArray[i]);
    Serial.print(";");
    Serial.print(map(voltArray[i], 285, 740, (-335/sqrt(2)), (335/sqrt(2))));
    Serial.print(";");
    Serial.print(map(ampArray[i], 106, 918, 21*28, -21*28));
    Serial.println("");
   /* Serial.print(voltArray[i]); Serial.println("Voltaje");
    Serial.print(ampArray[i]); Serial.println("Amperaje");
    Serial.print(timeArray[i]); Serial.println("Milisegundos");*/
  }


}

void sampling()
{
  int count = 0;
  for (count = 0; count < muestras; count++)
  {
    voltArray[count] = analogReadFast(A0); //volt = map(volt, 285, 740, -335, 335);
    ampArray[count] = analogReadFast(A5); //lectura del sensor //amp = map(amp, 106, 916, -30, 30);
    timeArray[count] = millis()/*/ - startTime*/;
    delay(2); //Delay 1ms
  }
}//Sampling

This gives me a pretty decent sine wave represtation in excel (also I rigged the serial monitor output so that I can copy to a notepad and save it as CSV for further excel manipulation)

However now that you guys mention it would be nice to add more precision (more on that later) making the 0 point cross for V and I in order to retrieve the phase shift in milliseconds

How do you guys recomend this approach, would it be a isr() function on INT2 and INT 3 (V and I respectively) with the CHANGING condition? (that was the quickliest way I´ve envisioned this concept)

Coming back to precision, I had to take samples at each 2 ms and import the library analogReadFast because in the array sometimes some milliseconds went skipped or other times 2 readings were taken on the same millisecond.

Fabius_rex:
Coming back to precision, I had to take samples at each 2 ms and import the library analogReadFast because in the array sometimes some milliseconds went skipped or other times 2 readings were taken on the same millisecond.

Using the Arduino "micros()" function instead of "millis()" would be better.

If you have sine waves for two signals stored in excel you can calculate the phase shift by plotting one sine vs the other which produces a lissajous pattern. You can look up the formula for phase difference which depends on the lissajous x intercepts and x min/max. You would need to adjust signals so that amplitudes are equal.

MrMark:
Using the Arduino "micros()" function instead of "millis()" would be better.

I agree but I think that also the time it takes to perform each instruction had to do. For example I was able to get smooth samples once I used analogReadFast, but I will use the micros() approach

However if you take samples below 2ms spacing you can see the jitter in the sine wave.

charliesixpack:
If you have sine waves for two signals stored in excel you can calculate the phase shift by plotting one sine vs the other which produces a lissajous pattern. You can look up the formula for phase difference which depends on the lissajous x intercepts and x min/max. You would need to adjust signals so that amplitudes are equal.

Thanks I followed your advice and I´m making progress

I also added an PC814 AC optocoupler to the rig attached to the PIN2 (interrupt) So I can know the momento
the V=0

However I´m trying to make the same approach on the I=0 but I´m scratching my head on that. How Can I turn the I (amp) sine wave into a voltage reference so the arduino can see it on the D3 pin each time the intensity crosses cero?

Im trying to make a schmitt trigger attached to the ACS712 signal output so whenever the voltage rises or falls belos 2.5V (0A reference) the trigger gives me an output. But this only serves for one side of the sine wave.

Diodes, inductor current sensor, voltaje divider? Brainstorm would be good

Thanks!