Monday, April 8, 2013

An funny thing happened on the way to the forum...

I'm posting videos on YouTube now, got three videos posted so far.  The latest one is related to the JeeNode Moteino Kill-A-Watt project.  That video describes how I'm using the 60 Hz (or 50 Hz as the case may be) square-wave as an interrupt, so my sketch can accurately measure true RMS voltage and current.  

But the sketch wasn't working as expected.  I did the usual Arduino style debug with the Serial Monitor, but that was getting me nowhere fast.  Debug can be fun, but in this case, it was getting tedious, especially having to disconnect the AC line before I could plug in the USB cable to the computer.  And even using an oscilloscope is risky, since the project's "ground" is riding on a few volts 60 Hz.  So no ground connection allowed between the scope and the project!  That meant using a special high voltage diff probe, but I only had one and needed to view two signals.

Finally it dawned on me: set the KAW aside for a while, and simulate the project on a breadboard.  Running on USB power in the usual way, everything properly grounded, safe to connect to PC and scope.  I used an AFG3000 series function generator trigger output to create the 60 Hz square-wave for the interrupt.  Ch1 and Ch2 simulate the voltage and current waveforms, with adjustable phase to simulate power factor.


Within minutes, I was able to "see" what was going on inside my sketch, using my trusty MDO4000 series scope, with respect to the  60 Hz square-wave.

Here's an abbreviated version of the sketch:


const int LED    = 6; // D6 LED to ground
const int HzSENS = 3; // D3 60Hz square-wave
const int HzINT  = 1; // INT1

void setup () {

  pinMode(HzSENS,INPUT); // D3 is INT1 
  pinMode(LED,OUTPUT);
  digitalWrite(LED,LOW); // off
}

volatile int unsigned measState; // global variable to count line cycles

void loop () {
  measState = 0;
  attachInterrupt(HzINT,AC60HZ,RISING);
  while(measState < 1); // skip partial cycle
  digitalWrite(LED,HIGH);
  while(measState < 2){  
    // do measurements over exactly one AC line cycle
  }
  digitalWrite(LED,LOW);
  // do other stuff, like compute and transmit results
  delay(1000); // wait a second
}


void AC60HZ(){ 
  measState++;
}

After attachInterrupt, measState should increment on every rising edge of the interrupt signal.  measState will be 0 for some unknown amount of time.  Then the 1st interrupt should increment it to 1. The second while statement block should execute during the time between the 1st and 2nd interrupts, one AC line cycle.

As shown in the code above, the LED should come on just after the 1st interrupt, and go off just after the 2nd one.  But the scope showed a different picture!  The LED came on a few milliseconds after the interrupt.  It turned off at the proper time, so the net effect was measurements made over about 3/4 of a cycle.  Not good!

(Sharp-eyed readers may notice the photo above shows lots of pulses during the measurement period.  That photo was taken with the digitalWrites inside the measurement loop: high before the three analogReads, then low, then just enough delay to give a net 50 measurements per cycle).

I fixed the problem by waiting for the second interrupt to start taking measurements.  I changed the 1 above to 2, and the 2 to 3.

I don't know why this change was needed.  It seems that the Arduino interrupt handler latency is several milliseconds, but only on the first interrupt after attachInterrupt?  

If anyone knows more about this, please post a comment, thanks!