60 Hz digital Arduino filter does the job!

10 Jan

60 Hz filter performance measured using function generator

To test the digital filter, I removed the EMF sensing resistor and antenna, and then plugged in a 1V peak-to-peak signal from a sine wave generator at different frequencies, up to the maximum frequency of 250 Hz (half the 500 Hz sampling frequency). It gets a little bumpy at low frequency, probably because we’re averaging over a small number of cycles (less than 3).

For a DC input of 5 volts, the Arduino reports an output of 1023. So the filter is working to extract the 1V amplitude, which is 1/5 *1023, or approximately 200. The signal drops to about half the maximum within +/- 15 Hz of 60 Hz.  Nice!

I made a version of the EMF sensor that reports the 60Hz voltage as a LED output on pin 11, and “everything BUT 60 Hz” on pin 3, where I put a green LED. The red LED lights near active wires and cords, while the green one goes nuts near compact-fluorescent bulbs (CFL). Two colors will help us distinguish more different kinds of events. To wire it up, just add a 3- to 8 Meg resistor between pin A5 and ground, then stick about a 4 inch wire from pin A5 hanging off into space as in the original EMF detector. The Arduino code is after the jump, and there’s also a list of 50 Hz filter coefficients if you live in one of those regions. You’ll have to truncate those and plug them into your code as we did with the 60 Hz, but you won’t need MATLAB to generate them.

int inPin = 5; //analog 5
int LED_60_Hz=11; //60Hz level indicator light
int LED_EverythingElse=3;//grn unfiltered indicator light on digital pin 3
float redval; //value to write to pin 11
float OtherLedval; //value to write pin 3
volatile int isNewVal=0; //flag for new read, variables changed in an interrupt handler should be declared volatile
volatile int val; //where to store info from analog 5
//FilterCoefficients for 32 Tap 60HzFilter, I multiplied all of these by 10000 to have ints, this was necessary for calc to be fast enough for 500 Hz
int b[]={ -61, 105, -152, -270, -237, -7, 354, 637, 602, 171, -471,-944,-914,-330,505,1104,1104,505,-330,-914,-944,-471,171,602,637,354,-7,-237,-270,-152,105,-61};
long latestval[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //Accumulate latest readings here, need longint or it wraps around
long myval;//this will hold the latest value of the filtered signal
float mrec[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};//holds latest 3 cycles of filtered 60Hz signal at 500Hz (25 samples)
float myamp=0; //latest amplitude of the 60Hz filtered, rectified and moving-averaged signal
float allamp=0;//latest amplitude of UNFILTERED rectified moving-averaged signal
int myintamp; //this will be an integer version of myamp for sending over serial.
int myintallamp;//an integer version of the unfiltered amplitude for sending over serial.
unsigned int mytime=0;//used for checking how long it takes to do things in the loop

void setup()//set up interrupts for 500Hz done by Chris Isert

{

//Serial.begin(115200); //when I check the time delay it’s 2.0 – 2.2 ms
Serial.begin(230400); //works better when I check the time delay, I get 2 ms
// initialize timer1

noInterrupts(); // disable all interrupts

TCCR1A = 0;

TCCR1B = 0;

TCNT1 = 0;

OCR1A = 32000; // compare match register 16MHz/1/500Hz

TCCR1B |= (1 << WGM12); // CTC mode

TCCR1B |= (1 << CS10); // divide-by-1 prescaler

TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt

interrupts(); // enable all interrupts

}

ISR(TIMER1_COMPA_vect) // timer compare interrupt service routine

{

val = analogRead(inPin);//latest EMF sensor reading
isNewVal=1;//this is a flag telling the loop that there’s a new value

}

void loop()

{
if (isNewVal){
for (int i=0;i<31;i++){
latestval[i]=latestval[i+1]; //slide the latest 32 raw values back by one in the array
}
latestval[31]=val; //and add the newest raw value on the end
//now do the convolution with the filter coefficients in b, to get the latest filtered value
allamp=allamp+(abs(latestval[31])-abs(latestval[6]))*0.126; //calculate pi*latest moving average of the last 25 values of the unfiltered signal, for comparing with the 60-hz filtered amplitude in myintamp
myintallamp=(int)allamp;
myval=0;
for (int i=0; i<32; i++){
myval=myval+latestval[i]*b[i];
}
myamp=myamp-mrec[0]; //set up for moving average by removing oldest element
for(int i=0;i<24;i++){
mrec[i]=mrec[i+1];//shift the latest 24 filtered and rectified values back by one in preparation for adding the newest one on the end
}
//then calculate the most recent filtered and rectified value
mrec[24]=abs(0.126*myval)/10000;//rectified (absolute value) signal, times pi/25, it gives the amplitude of the positive-only ac signal scaled for moving avg of latest 25 pts
//divided by 10000 to get back the original filter coefficients which were not integers
myamp=myamp+mrec[24];//latest moving avg over 3 cyc of 60 hz at 500 hz sampling rate
myintamp=(int)myamp;//this conversion to int makes it much faster to send the value over the serial connection than a float, and code size is smaller
//Serial.print(val);
//Serial.print(“,”);
//Serial.print(myintamp); //send the amplitude of the 60Hz signal averaged over the past 25 samples (0.05 s at 500 Hz)
//Serial.print(“,”);
//Serial.println(myintallamp);//send the amplitude of the unfiltered signal avgd over the past 25 samples (0.05 s at 500 Hz)
redval=map(constrain(myintamp,0,1000),0,1000,0,255);//scale the 60hz level to 0-255; 3222 is the max value the unfiltered signal reports at 5Vdc
OtherLedval=map(constrain(myintallamp-myintamp,0,300),0,300,0,255);//show the value of “everything else” not 60
analogWrite(LED_EverythingElse,OtherLedval);
isNewVal=0;//clear new value flag until next interrupt
mytime=micros(); //check whether we are staying at 500Hz, cant spend too much time number crunching between readings
Serial.println(mytime,DEC);
analogWrite(LED_60_Hz,redval);//uses PWM

}
}

THE END. Stop pasting into your code at this point! Here are the filter coefficients for 50 Hz if needed:

-0.009771673180555
0.003778122029470
-0.015005194228270
-0.003497019847082
0.022799807713452
0.044907466778555
0.051491692579272
0.036533832128465
-0.000003031352469
-0.047522139403626
-0.085634763080617
-0.093553336126602
-0.061913085704003
0.000027019286784
0.067303490781392
0.110639882323431
0.110639882323431
0.067303490781392
0.000027019286784
-0.061913085704003
-0.093553336126602
-0.085634763080617
-0.047522139403626
-0.000003031352469
0.036533832128465
0.051491692579272
0.044907466778555
0.022799807713452
-0.003497019847082
-0.015005194228270
0.003778122029470
-0.009771673180555


					
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: