Extremely Low Frequency Electro Magnetic Field Sensing (Part 2)

Part 1234, 5

The proposed application features a few ideas which may be reused for all sorts of applications. As usual, the idea is to build the most exciting Arduino application with the least additional components. In this sense, MicroELFEMF is a serious challenger, as it requires… no additional components!

What this application will do is to change its blinking rate according to the proximity of electro magnetic fields. Because these fields may vary from palce to place, this application contains a simple but powerfull auto tuning section.

After signal acquisiton and fast Fourier transform, we will care about the intensity of the signal belonging to the AC frequency bin, using this sort of trivial code (Note: 50Hz AC! Apply changes according to your own AC frequency)

	_signalStrength = int16_t(_vReal[6] + _vReal[7]); /* 250 to 1000 counts */

And here goes the self tuning

	/* Self tuning */
	if (_signalStrength < _minStrength) {
		_minStrength = _signalStrength;
	} 
	if (_signalStrength > _maxStrength) {
		_maxStrength = _signalStrength;	
	}

Plain simple! After that, we will translate the signal strength in one sort of normalized signal strength (Normalized to the full EMF swing)

	double normalizedSignalStrength = double(_signalStrength - _minStrength) / (_maxStrength - _minStrength);

Blink the control LED is almost as simple. I decided to keep a constant duty cycle (50/50), thus the repeated last lines of code

	/* Timing */
	uint16_t interval = acquisitionDuration + (_maxInterval * normalizedSignalStrength);
	while (millis() < (_lastToggleTime + interval));
	_lastToggleTime = millis();
	PORTB ^= (1 << _pinCtrlLed); /* Toggle control led on */
	while (millis() < (_lastToggleTime + interval));
	_lastToggleTime = millis();
	PORTB ^= (1 << _pinCtrlLed); /* Toggle control led on */

Here is a full picture of the sketch, enjoy it!

/*

	MicroELFEMF: Extremely Low Frequency Electro Magnetic Field Sensing
	A piece of wire must be attached to analog port 0
	This wire detects the magnetic fields generated by the ambiant AC
	This signal is sampled in scan mode, filtered through a FFT.
	The 50 Hz signal is quantified and transformed in a blinking signal
	The sketch features self tuning of signal strength

	Copyright (C) 2012 Didier Longueville

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#include 
#include 
PlainADC PADC; /* Create ADC object */
PlainFFT FFT; /* Create FFT object */
/* Data acquisition parameters */
const uint16_t _samples = 64;
const double _samplingFrequency = 500.0;
const uint16_t _adcChannel = 0; /* From 0 to 5 on ATmega328 powered Arduinos */
const uint16_t _refVoltage = ADC_REF_VOL_DEFAULT; /* VCC: 5V */
/* Constants */
#define SCL_INDEX 0x00
#define SCL_TIME 0x01
#define SCL_FREQUENCY 0x02
/* Data acquisition vector */
uint8_t *_vBuffer;
/* FFT vectors */
double *_vReal;
double *_vImag;
/* Local variables */
uint8_t _pinCtrlLed = PINB5;
volatile uint8_t *_portCtrlLed = &PORTB;
uint32_t _lastToggleTime = millis();
int16_t _signalStrength = 0;
int16_t _minStrength = 32767;
int16_t _maxStrength = 0;
int16_t _maxInterval = 1000;

void setup() 
{ 
	Serial.begin(115200);
	/* Set data acquisition parameters */
	_vBuffer = PADC.SetAcquisitionEngine(_adcChannel, _refVoltage,  _samplingFrequency, _samples, ADC_DAT_FMT_DBL);	
	/* Size imaginary vector */
	_vImag = (double*)malloc(_samples * sizeof(double));
	DDRB |= (1 << _pinCtrlLed);
};

void loop() 
{	
	uint32_t startAcquisition = millis();
	/* Acquire data */
	PADC.GetScanData();
	/* Cast data in a doubles vector */
	_vReal = reinterpret_cast(_vBuffer);
	/* Print raw data (optional) */
	/* PrintVector(_vReal, _samples, SCL_TIME); */
	/* Suppress data offset */
	FFT.SuppressOffset(_vReal, _samples);
	/* Clear imaginary vector */
	FFT.ClearVector(_vImag, _samples);
	/* Window data: optional */
	FFT.Windowing(_vReal, _samples, FFT_WIN_TYP_HAMMING, FFT_FORWARD);	
	/* Compute FFT */
	FFT.Compute(_vReal, _vImag, _samples, FFT_FORWARD);	
	/* Compute magnitudes */
	FFT.ComplexToReal(_vReal, _vImag, _samples, FFT_SCL_TYP_AMPLITUDE);
	uint32_t acquisitionDuration = (millis() - startAcquisition);
	/* Get data corresponding to the 50Hz bin */
	_signalStrength = int16_t(_vReal[6] + _vReal[7]); /* 250 to 1000 counts */
	/* Self tuning */
	if (_signalStrength < _minStrength) {
		_minStrength = _signalStrength;
	} 
	if (_signalStrength > _maxStrength) {
		_maxStrength = _signalStrength;	
	}
	double normalizedSignalStrength = double(_signalStrength - _minStrength) / (_maxStrength - _minStrength);
	Serial.println((normalizedSignalStrength * 100.0), 1);
	/* PlotBarGraph(normalizedSignalStrength); */
	/* Timing */
	uint16_t interval = acquisitionDuration + (_maxInterval * normalizedSignalStrength);
	while (millis() < (_lastToggleTime + interval));
	_lastToggleTime = millis();
	PORTB ^= (1 << _pinCtrlLed); /* Toggle control led on */
	while (millis() < (_lastToggleTime + interval));
	_lastToggleTime = millis();
	PORTB ^= (1 << _pinCtrlLed); /* Toggle control led on */
};

void PlotBarGraph(double signalStrength) 
{
	static uint8_t counts;
	counts += 1;
	counts %= 2;
	if (counts == 0) {
		/* Print mimits*/
		Serial.print(_minStrength);
		Serial.print(":");
		Serial.print(_maxStrength);
		Serial.print(" ");
		/* Print signal strength as bar*/
		for (uint8_t i = 0; i < uint8_t(signalStrength * 50); i++) {
			Serial.print("-");
		}
		Serial.print(" ");
		Serial.print(_signalStrength);
		Serial.println();	
	}
};

void PrintVector(double *vData, uint8_t bufferSize, uint8_t scaleType) 
{	
	for (uint16_t i = 0; i < bufferSize; i++) {
		double abscissa;
		/* Print abscissa value */
		switch (scaleType) {
		case SCL_INDEX:
			abscissa = double(i);
			break;
		case SCL_TIME:
			abscissa = (i / _samplingFrequency);
			break;
		case SCL_FREQUENCY:
			abscissa = ((i * _samplingFrequency) / _samples);
			break;
		}
		Serial.print(abscissa, 6);
		Serial.print("t");
		Serial.print(vData[i], 4);
		Serial.println();
	}
	Serial.println();
};

Next post on same subject

2 Comments

  1. newbieuser says:

    I am a complete and utter newbie with this, but Im assuming the reason Im getting errors about FFT and PADC is because Im missing those particular libraries. Is this one correct – http://code.google.com/p/makefurt/source/browse/trunk/arduino-libraries/PlainFFT/?r=11 ?

    • Didier says:

      This code has been placed there without my consent at the users risk.
      Once again, may I repeat that my policy is clearly stated here, for the benefits of all parts.

Leave a Reply

You must be logged in to post a comment.