Reading AC voltage in short time

Hi All,

I have an ongoing project for spot welder where i am trying to read the ac voltage induced in a transformer at its secondary. The open circuit voltage of the MOT(Microwave oven transformer) is 2.6vac.

I have two taps on the secondary 40 cm apart to read a voltage when the secondary shorted or in other words when a weld happens. My current sketch looks like this but i need some help to adjust to required voltage. after which i could calculate the current using ohms law.

const int buttonPin  = 9;  // Weld button
const int TRIAC      = A1; // Load
const int Isense     = A2; // Weld Current Sense
const int ISamples   = 30; // Sample rate
int btnState;

void setup()
{
  pinMode (buttonPin, INPUT_PULLUP);
  pinMode (TRIAC, OUTPUT);
  pinMode (A2, INPUT);
  Serial.begin(115200);
}

float ReadCurrent()
{
  int iSense;
  int i;
  int reading;
  for (i = 0; i < ISamples; i++)
  {
    reading = analogRead(A2);
  }
  iSense = reading / ISamples; // get average value
  iSense = iSense*1023; // 
  iSense = iSense/0.00003;
  return (iSense);
}

void loop()
{
  btnState = digitalRead(buttonPin);
  if (btnState == LOW)
  {
    digitalWrite(TRIAC, HIGH);
    Serial.println(analogRead(A2));
    Serial.println(ReadCurrent());
  } else {
    digitalWrite(TRIAC, LOW);
  }
}

In the schematic the voltage is level-shifted-up from the center to a safe voltage that arduino can handle.

Thanks in Advance
Capture.JPG

Do you mean calculate wattage not current to measure current you use a current transducer

So you're looking at the distance of wire for a non resistance you're looking at the voltage across that section of wire to get voltage and resistance from which to then calculate amps have you been able to get any readings of voltage at all across that section when you're actually doing welding

yes you have understood it correct, on the Serial monitor i get the following

Analogue : 289
Calculated: 17.93

that was after sampling at 100 with following calculation to determine the calculated value. I got the formula from the arduino reference The Calculated value changes as i change the sample count.

iSense = reading / ISamples; // get average value
  iSense = ((iSense*2.76)/1023); 2.76 is the open voltage of the MOT
  iSense = (iSense/0.0003); // 0.0003 is wire secondary wire resistance en-to-end using kelvin method
  return (iSense);

Just noticed that even without the secondary shorted there seem to be constant value which is not correct. The schematic would be wrong i guess.

Would it be ok to pass this voltage into the arduino directly ? Would this damage the IC ?

You have an AC voltage, while an Arduino can only read positive voltages. So you have to bias your AC signal so that the lowest is >0V, then add a resistor divider to make sure the highest peak is <5V.

Your 2.6V AC gives a peak-peak of at least 5.2V which is too much already. If that 2.6V is the rms value of a pure sine wave, the actual peak-peak is about 7.4V even. So you have to bring that voltage down. Now you can start sampling the signal to read the waveform, and do whatever you like with this.

A second option, much simpler but only providing the peak value, is to use a diode to rectify the signal (half wave is enough), and add a resistor and capacitor to smooth the signal. Now you get a 3.7V signal (assuming 2.6V RMS so 3.7V peak) which is safe to supply to the Aduino pin.

So that’s the voltage part taken care of.

A similar thing you can do with the signal coming out of your current sensor, which I assume is a voltage signal, but that should be a much smaller voltage so some amplification is needed in that case.

Thanks you for the reply. but adding a diode would have a voltage drop. The voltage at the taps are about in mV.

I would need to have a look at my sketch again to see why the value is always a constant and does not change when the voltage is increased (by increase the delay time after the MOT is switched ON) also noticed that the voltage is the same when the secondary are not shorted.

I get the result i want when i simulate the circuit mentioned in the first post.

FullWave_CS.zip (907 Bytes)

Are you sure that output is what you want? I see a large phase shift there. The output really lags the input, which I suppose is caused by C3. Did you also try to simulate with different input voltages?

It's also a rather complex circuit if all you want is applying an offset, which can be done with as little as a single capacitor and two resistors.

Since the voltage at the input is in the range mV. There would be chances of errors in the reading and more over with the input voltage is varying because the weld current is varied.

biasing you mean like this i guess


but i doubt it would be possible for my application though it would can be simulated. I would give your suggestion a try but a concern would be the signal being mV would it be detectable for the Arduino but then hope is that the ADC would detect that.

So after a while with the code i had to use an LCD to see the delay() part of the code because the serial would just scroll down and taking a reading of the weld current would be way back in the scroll.

#include <PinChangeInterrupt.h>
#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>
hd44780_I2Cexp lcd;
const int buttonPin  = 9;  // Weld button
const int TRIAC      = A1; // Load
const int Isense     = A2; // Weld Current Sense
const int ISamples   = 50; // Sample rate
int reading;
int btnState;
int prevbtnState;
unsigned long TimeStamp;
unsigned long buttonPinpressDuration;
unsigned long delayTime = 50;
const int ENC_PinA   = 6;  // PIN A of Encoder
const int ENC_PinB   = 8;  // PIN B of Encoder
int ENC_PinAState = LOW;
int ENC_PinALastState = LOW;
int Counter1;
int WeldTime;

void setup()
{
  lcd.begin(16, 2);
  lcd.clear();
  pinMode (buttonPin, INPUT_PULLUP);
  pinMode (ENC_PinA, INPUT_PULLUP);
  pinMode (ENC_PinB, INPUT_PULLUP);
  pinMode (TRIAC, OUTPUT);
  pinMode (A2, INPUT);
  attachPCINT(digitalPinToPCINT(ENC_PinA), rotaryEncoder, CHANGE);
}

void rotaryEncoder()
{
  ENC_PinAState = digitalRead(ENC_PinA);
  if (digitalRead(ENC_PinB) != ENC_PinAState)
  {
    Counter1--;
  } else {
    Counter1++;
  }
}

int readEncoder()
{
  noInterrupts();
  int copyCounter = Counter1;
  interrupts();
  return (copyCounter) >> 1;
}

int ReadCurrent()
{
  float iSense;
  int i;
  for (i = 0; i < ISamples; i++)
  {
    reading = analogRead(A2);
  }
  iSense = reading / ISamples; // get average value
  iSense = ((iSense * 2.76) / 1023);
  iSense = (iSense / 0.00003);
  return (iSense);
}

void loop()
{
  WeldTime = (readEncoder() % 251) * 2;
  lcd.setCursor(0, 0);
  lcd.print("Weld Time:");
  lcd.print(WeldTime);
  lcd.print("ms");
  btnState = digitalRead(buttonPin);
  if ((btnState == LOW) && prevbtnState == HIGH)
  {
    delay(20);
    btnState = digitalRead(buttonPin);
    if ((btnState == LOW) && prevbtnState == HIGH)
    {
      TimeStamp = millis();
    }
  }

  if (btnState == HIGH && prevbtnState == LOW)
  {
    delay(20);
    btnState = digitalRead(buttonPin);
    if (btnState == HIGH && prevbtnState == LOW)
    {
      buttonPinpressDuration = (millis() - TimeStamp);
    }
  }
  if (buttonPinpressDuration > 0 && buttonPinpressDuration >= delayTime)
  {
    buttonPinpressDuration = 0;
    digitalWrite(TRIAC, HIGH);
    lcd.setCursor(0, 1);
    lcd.print("Current:");
    lcd.print(ReadCurrent());
    lcd.print("A");
    delay(WeldTime);
    reading = 0;
    digitalWrite(TRIAC, LOW);
  }
  prevbtnState = btnState;
  reading = 0;
}

No i noticed that that increasing the sampling rate would change the reading but which one would be more true/precise or near to precise. Also varying the weld time does not have any effect on the weld current as seen here.

Would anyone know why ?

So what is the signal level coming from that current sensor?

On the Arduino you can set the ADC to use the internal 1.1V reference, making it a lot more sensitive (and probably more stable).

anishkgt:

int ReadCurrent()

{
  float iSense;
  int i;
  for (i = 0; i < ISamples; i++)
  {
    reading = analogRead(A2);
  }
  iSense = reading / ISamples; // get average value
  iSense = ((iSense * 2.76) / 1023);
  iSense = (iSense / 0.00003);
  return (iSense);
}




No i noticed that that increasing the sampling rate would change the reading but which one would be more true/precise or near to precise. Also varying the weld time does not have any effect on the weld current as seen here.

Would anyone know why ?

There’s a lot wrong with your code.

  1. variables types: many are signed while they can never become negative. Make them unsigned.
  2. you always use int while often you can use byte (as they don’t get that large), such as for the pin numbers and ISamples.
  3. reading is global - why? Make that local. There are probably more such globals that should be local.

Then the snippet where you read the current. So many issues I’ll just correct them and add comments in the code itself:

float ReadCurrent() // You actually return a float (iSense), not an int. 
{
  float iSense; // Here this float gets declared.
  unsigned int reading; // local to this function, and the proper type: the maximum possible is ISamples*1024 = just over 50,000. Doesn't fit in a (signed) int.
  for (byte i = 0; i < ISamples; i++) // a byte is big enough for i, and it can be local to this loop.
  {
    reading += analogRead(A2); // You surely want to add up the readings!
  }
  iSense = (float)reading / (float)ISamples; // get average value - cast to float or you get an int as result, losing a lot of precision.
  iSense = ((iSense * 2.76) / 1023);
  iSense = (iSense / 0.00003);
  return (iSense);
}

But it still won’t work. Every analogRead takes about 100 us. Your period lasts 20 ms, so that would require 200 samples to read a complete period and get a single wave (obviously you have to change the type of reading to store the sum of 200 samples to unsigned long).

However when you measure a complete wave, and take the average, what you get is of course the offset value, not the peak to peak value of the wave!

What you really want to know is the peaks of the wave: the highest point, and the lowest point, and then the difference between the two. That number varies with the actual wave. The way to accomplish that is to sample a few waves, record the highest and lowest values you got, and use that to calculate the current.

float ReadCurrent() 
{
  unsigned int maxReading = 0;
  unsigned int minReadnig = 1024;
  for (unsigned int i = 0; i < 1000; i++) // This should sample just over 5 complete waves.
  {
    unsigned int reading = analogRead(A2);
    if (reading < minReading) minReading = reading;
    if (reading > maxReading) maxReading = reading;
  }
  float iSense;
  unsigned int readingDifference = maxReading - minReading;
  // calculate current with the difference between the peaks.
  return (iSense);
}

Now i don’t see anything on the serial. I guess its time to quite this part of the project.

here is the scope view of the output that reaches the arduino.
scope.jpg

#include <PinChangeInterrupt.h>
#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>
hd44780_I2Cexp lcd;
byte zCd = 2;  // Zero Crossing Detect
const int ENC_PinA   = 6;  // PIN A of Encoder
const int ENC_PinB   = 8;  // PIN B of Encoder
const int buttonPin  = 9;  // Weld button
const int TRIAC      = A1; // Load
const int Isense     = A2; // Weld Current Sense
const int ISamples   = 100; // Sample rate
int btnState = HIGH;
int prevbtnState = LOW;
unsigned long TimeStamp;
unsigned long buttonPinpressDuration;
unsigned long delayTime = 20;
unsigned int maxReading;
unsigned int minReading;
int ENC_PinAState = LOW;
int ENC_PinALastState = LOW;
int Counter1;
int WeldTime;
int prevWeldTime = 0;
const int tgr_dly = 5084;
volatile boolean zeroCrossingFlag = false;

void setup()
{
  lcd.clear();
  lcd.begin(16, 2);
  pinMode (buttonPin, INPUT_PULLUP);
  pinMode (ENC_PinA, INPUT_PULLUP);
  pinMode (ENC_PinB, INPUT_PULLUP);
  pinMode (TRIAC, OUTPUT);
  pinMode (A2, INPUT);
  attachPCINT(digitalPinToPCINT(ENC_PinA), rotaryEncoder, CHANGE);
  pinMode (zCd, INPUT_PULLUP);
  attachInterrupt(0, setFlag, FALLING);//zero cross
  Serial.begin(115200);
}

void setFlag()
{
  zeroCrossingFlag = true; //interrupt sets flag true
}

void rotaryEncoder()
{
  ENC_PinAState = digitalRead(ENC_PinA);
  if (digitalRead(ENC_PinB) != ENC_PinAState)
  {
    Counter1--;
  } else {
    Counter1++;
  }
}

int readEncoder()
{
  noInterrupts();
  int copyCounter = Counter1;
  interrupts();
  return (copyCounter) >> 1;
}

float ReadCurrent()
{
  unsigned long reading;
  float iSense;
  unsigned int i;
  for (i = 0; i < ISamples; i++)
  {
    delayMicroseconds(1);
    reading = analogRead(A2);
    if (reading > maxReading)
    {
      maxReading = minReading;
    }
    if (reading < minReading)
    {
      minReading = minReading;
    }
  }
  iSense = ((maxReading - minReading) / ISamples);
  iSense = ((iSense * 2.6) / 1024);
  iSense = (iSense / 0.0003);
  return (iSense);
}

void loop()
{
  //analogReference(INTERNAL);
  WeldTime = (readEncoder() % 251) * 2;
  lcd.setCursor(0, 0);
  lcd.print("Weld Time:");
  lcd.print(WeldTime);
  lcd.print("ms");
  btnState = digitalRead(buttonPin);
  if ((btnState == LOW) && prevbtnState == HIGH)
  {
    delay(50);
    btnState = digitalRead(buttonPin);
    if ((btnState == LOW) && prevbtnState == HIGH)
    {
      TimeStamp = millis();
    }
  }

  if (btnState == HIGH && prevbtnState == LOW)
  {
    delay(50);
    btnState = digitalRead(buttonPin);
    if (btnState == HIGH && prevbtnState == LOW)
    {
      buttonPinpressDuration = (millis() - TimeStamp);
    }
  }
  if (buttonPinpressDuration > 0 && buttonPinpressDuration >= delayTime)
  {
    int readi = 0;
    buttonPinpressDuration = 0;
    zeroCrossingFlag = false; //set flag false and wait for next zero crossing
    while (!zeroCrossingFlag)
    {
    };
    delayMicroseconds(tgr_dly);
    digitalWrite(TRIAC, HIGH);
    readi = ReadCurrent();
    lcd.setCursor(0, 1);
    lcd.print("I:");
    lcd.print(readi);
    lcd.print("A");
    zeroCrossingFlag = false;
    delay(WeldTime);
    digitalWrite(TRIAC, LOW);;
    Serial.print("maxReading:");
    Serial.println(readi);
  }
  prevbtnState = btnState;
  //minReading = 0;
  //maxReading = 0;
}

anishkgt:

    if (reading > maxReading)

{
      maxReading = minReading;
    }
    if (reading < minReading)
    {
      minReading = minReading;
    }

This won’t work well.

  if (buttonPinpressDuration > 0 && buttonPinpressDuration >= delayTime)

This looks weird, that first statement is unnecessary.

I amm kinda lost with the sampling part of the code

I see in the arduino reference that the PinMode() for adc is not set why is that ? just defined.Secondly i also noticed that the MinReading is set to 1024 and the MaxReading is 0, shouldn’t it be the other way round ?

#include <PinChangeInterrupt.h>
#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>
hd44780_I2Cexp lcd;
byte zCd = 2;  // Zero Crossing Detect
const int ENC_PinA   = 6;  // PIN A of Encoder
const int ENC_PinB   = 8;  // PIN B of Encoder
const int buttonPin  = 9;  // Weld button
const int TRIAC      = A1; // Load
const int Isense     = A2; // Weld Current Sense
const int ISamples   = 50; // Sample rate
int btnState = HIGH;
int prevbtnState = LOW;
unsigned long TimeStamp;
unsigned long buttonPinpressDuration;
unsigned long delayTime = 50;
unsigned int maxReading = 0;
unsigned int minReading = 1024;
int ENC_PinAState = LOW;
int ENC_PinALastState = LOW;
int Counter1;
int WeldTime;
int prevWeldTime = 0;
int Cal = 0;
int reading;
const int tgr_dly = 5084;
volatile boolean zeroCrossingFlag = false;

void setup()
{
  lcd.clear();
  lcd.begin(16, 2);
  pinMode (buttonPin, INPUT_PULLUP);
  pinMode (ENC_PinA, INPUT_PULLUP);
  pinMode (ENC_PinB, INPUT_PULLUP);
  pinMode (TRIAC, OUTPUT);
  pinMode (A2, INPUT);
  attachPCINT(digitalPinToPCINT(ENC_PinA), rotaryEncoder, CHANGE);
  pinMode (zCd, INPUT_PULLUP);
  attachInterrupt(0, setFlag, FALLING);//zero cross
  Serial.begin(115200);
}

void setFlag()
{
  zeroCrossingFlag = true; //interrupt sets flag true
}

void rotaryEncoder()
{
  ENC_PinAState = digitalRead(ENC_PinA);
  if (digitalRead(ENC_PinB) != ENC_PinAState)
  {
    Counter1--;
  } else {
    Counter1++;
  }
}

int readEncoder()
{
  noInterrupts();
  int copyCounter = Counter1;
  interrupts();
  return (copyCounter) >> 1;
}

float ReadCurrent()
{
  unsigned long reading;
  float iSense;
  unsigned int i;
  for (i = 0; i < ISamples; i++)
  {
    reading = analogRead(A2);
    iSense = (maxReading - minReading); // ISamples)
    iSense = (iSense * 5 / 1023);
    iSense = (iSense / 0.0003);
    return (iSense);
  }
}

void loop()
{
  if ((Cal == 0) && btnState == HIGH)
  {
    unsigned long currentTime = millis();
    unsigned long prevTime;
    Serial.println("Calibrating.....");
    for (unsigned int i = 0; i < 500; i++)
    {
      digitalWrite(TRIAC, HIGH);
      reading = analogRead(A2);
      if (reading > minReading) maxReading = minReading;
      if (reading < maxReading) minReading = minReading;
    }
    Cal = 1;
    digitalWrite(TRIAC, LOW);

    /*if (currentTime - prevTime >= 500)
      {
      Cal = 1;
      digitalWrite(TRIAC, LOW);
      } else {
      digitalWrite(TRIAC, LOW);
      }*/
    Serial.print("Min: ");
    Serial.println(minReading);
    Serial.print("Max: ");
    Serial.println(maxReading);
    prevTime = currentTime;
  }

  //analogReference(INTERNAL);
  WeldTime = (readEncoder() % 251) * 2;
  lcd.setCursor(0, 0);
  lcd.print("Weld Time:");
  lcd.print(WeldTime);
  lcd.print("ms");
  btnState = digitalRead(buttonPin);
  if ((btnState == LOW) && prevbtnState == HIGH)
  {
    delay(50);
    btnState = digitalRead(buttonPin);
    if ((btnState == LOW) && prevbtnState == HIGH)
    {
      TimeStamp = millis();
    }
  }

  if (btnState == HIGH && prevbtnState == LOW)
  {
    delay(50);
    btnState = digitalRead(buttonPin);
    if (btnState == HIGH && prevbtnState == LOW)
    {
      buttonPinpressDuration = (millis() - TimeStamp);
    }
  }
  if (buttonPinpressDuration >= delayTime)
  {
    //int readi = 0;
    buttonPinpressDuration = 0;
    zeroCrossingFlag = false; //set flag false and wait for next zero crossing
    while (!zeroCrossingFlag)
    {
    };
    delayMicroseconds(tgr_dly);
    digitalWrite(TRIAC, HIGH);
    Serial.print("maxReading:");
    Serial.println(maxReading);
    zeroCrossingFlag = false;
    lcd.setCursor(0, 1);
    lcd.print("I:");
    lcd.print(ReadCurrent());
    lcd.print("A");
    delay(WeldTime);
    digitalWrite(TRIAC, LOW);;
  }
  prevbtnState = btnState;
  //minReading = 0;
  //maxReading = 0;
}

Too much code for me to read now. Best is if you create a minimal example that demonstrates your problem, that makes it much easier for us to understand what you’re trying to do and where it’s going wrong.

For the pinMode: the default mode of all pins is INPUT. So if you want to read a pin as input, there is no need to set this explicitly. We often do this anyway, but it’s really just for clarity.

minReading and maxReading are initialised at 1024 and 0 respectively - that looks correct.

this part however does not look correct:

      if (reading > minReading) maxReading = minReading;

if (reading < maxReading) minReading = minReading;

The second line really doesn’t do anything useful for starters. What I think you need is:

      if (reading > maxReading) maxReading = reading;
      if (reading < minReading) minReading = reading;

Now you can also see why maxReading and minReading are initialised as they are!

Here are two parts of the code am concerned about

void loop()
{
  while (millis() < 500 && Cal == 0)
  {
    digitalWrite(TRIAC, HIGH);
    reading = analogRead(A2);
    if (reading > maxReading) maxReading = reading;
    if (reading < minReading) minReading = reading;
    Serial.println("Calibrating.....");
    Serial.print("Min: ");
    Serial.println(minReading);
    Serial.print("Max: ");
    Serial.println(maxReading);
  }
  digitalWrite(TRIAC, LOW);
  Cal = 1;

float ReadCurrent()
{
unsigned long reading;
float iSense;
unsigned int i;
for (i = 0; i < ISamples; i++)
{
reading = analogRead(A2);
iSense = (maxReading - minReading); // ISamples)
iSense = (iSense * 5 / 1024); // scaling to arduino limits
iSense = (iSense / 0.0003); // ohms law to find current
return (iSense);
}
}

Another issue in the first code: all those Serial.print statements take quite some time away from your 500 ms. You may get about a dozen readings that way.

Remove the Serial.print() statements and you'll get lots more readings (probably a couple thousand) and with it a much better calibration result.

No obvious issues with the second snippet; if there's a problem do explain.

Thank you. Things are looking better now.

Just a few problems in the reading. I realse i need to call ReadCurrent() function when i do a weld

int ReadCurrent()
{
  unsigned long reading;
  int iSense;
  unsigned int i;
  for (i = 0; i < 1000; i++)
  {
    reading = analogRead(A2);
    if (reading > maxReading) maxReading = reading;
    if (reading < minReading) minReading = reading;
    iSense = (maxReading - minReading); // ISamples)
    iSense = (iSense * 5 / 1024);
    iSense = (iSense / 0.0003);
    return (iSense);
  }
}

Should i have the calculation part separate ? at the moment i see various current readings when ever i do welds with the same time and the reading decrease as the weld Time increase and sometimes vice versa.

Previously i had switched on the MOT for the max time required and then sampled the readings. Should i use that value and scale any new reading within that range using the map function ?

Why is that return statement inside the for loop? This way it’ll run only once.

Like below makes much more sense to me. First take those 1000 samples, then calculate the current and return that value. Another thing: you declare iSense as int, but based on the calculations you do this should be a float value instead. So the function also has to return a float.

float ReadCurrent()
{
  unsigned long reading;
  float iSense;
  unsigned int i;
  for (i = 0; i < 1000; i++)
  {
    reading = analogRead(A2);
    if (reading > maxReading) maxReading = reading;
    if (reading < minReading) minReading = reading;
  }
  iSense = (maxReading - minReading);
  iSense = (iSense * 5 / 1024);
  iSense = (iSense / 0.0003);
  return (iSense);
}

1/ I take it the 2.6vac is for the filament of the magnetron - is it referenced to earth, or some other potential?

I’d expect it to be at about -2.5kV compared to ground - the anode of the magnetron is normally grounded.

Not easy (or safe) to measure.

2/ The circuit you posted is a Sallen and Key Butterworth lowpass filter - of what relevance is this to your needs?

Allan