Arduino EMF

From YobiWiki
Jump to navigation Jump to search

Back to Arduino page.

Based on Aaron's detector, here using a 2x16 LCD, actually my Samsung 16T202DA1J VFD (Vacuum Fluorescent Display).
Upper row gives the averaged value on 10 samples
Lower row gives the signal between the minimal and maximal values observed within those 10 samples.
For real use the lower row is probably enough, here I show both to see the difference.
It's also a good example of a smooth bargraph on a LCD, with custom "chars" if needed, i.e. when both min and max are to be shown on a single 5x7 digit display.

Why didn't I simply display the raw values? Because you cannot read that fast or I would have to refresh the value something like only once per second and moreover IMHO numbers don't give such an immediate quantitative feeling that a bargraph does and without calibration numbers are quite meaningless.

You can download the code [{{#filelink: testEMF.pde}} as testEMF.pde]

But before having a look to the code you want to see my EMF detector in action (video) and (picture)

It is using the LiquidCrystal library so we'll include it: {{#fileanchor: testEMF.pde}}

// Doegox EMF detector v1.0
// Based on Aaron ALAI EMF Detector April 22nd 2009 VERSION 1.0

#include <LiquidCrystal.h>

Some constants: let's decide to take 10 samples every 1/50s so this means around one sample every 2ms ("around" because we're not using timers, just delays). The idea is to do average and min/max on a complete 50Hz cycle as this is (in Europe) the most common EMF noise source. {{#fileanchor: testEMF.pde}}

#define MAXN 10            // samples per cycle
#define FREQ 50            // cycle in Hz

And another one: the LCD display is not a continuous matrix and is we want to only use the visible columns, we've a jumping effect when going from one to another digit. I prefer to do such as if there were two extra columns between the 5x7 digits. {{#fileanchor: testEMF.pde}}

#define DARKBARS true      // extrapolate 2 bars between digits? smoother but we loose info

Hardware part I:
We'll use the same analog pin 5 with a wire as antenna as in the original design. {{#fileanchor: testEMF.pde}}

int inPin = 5;             // analog 5

Hardware part II:
We'll hook the LCD on the standard pins, cf the LiquidCrystal tutorial and initialize the LCD as usual. {{#fileanchor: testEMF.pde}}

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup() {
  lcd.begin(2, 16); 
  lcd.clear();
}

Main loop:
We sample 10 times with the proper delays and we accumulate the sampled values as following: the min in val_min, the max in val_max and the average in val_avg as the sum divided by the number of samples. {{#fileanchor: testEMF.pde}}

void loop() { 
  int val;
  int val_avg = 0;           // will store and average value of analog 5
  int val_min = 1024;        // will store and keep min value of analog 5
  int val_max = 0;           // will store and keep max value of analog 5
  int LCD1[16];              // will store temporarily content of 2nd LCD row

  for (int n=0; n<MAXN; n++) {
    delayMicroseconds(1000000/FREQ/MAXN);
    val = analogRead(inPin);
    val_avg += val;
    val_min = min(val_min, val);
    val_max = max(val_max, val);
  }
  val_avg/=MAXN;

Then we remap the sampled values from 0..1023 range to the number of available columns in the LCD. Remember that we'll consider two extra invisible columns if DARKBARS mode. {{#fileanchor: testEMF.pde}}

  val_avg = map(val_avg, 0, 1023, 1, 16*(DARKBARS ? 7 : 5));
  val_min = map(val_min, 0, 1023, 1, 16*(DARKBARS ? 7 : 5));
  val_max = map(val_max, 0, 1023, 1, 16*(DARKBARS ? 7 : 5));

On the upper row we simply display the average value with columns of dots.
0x10 char has the first column turned on, 0x11 has the two first... and 0x14 char is fully illuminated. 0x20 is dark as it's the space char. {{#fileanchor: testEMF.pde}}

  lcd.setCursor(0,0);
  for (int i=0; i<16;i++) {
    if (val_avg>=5) {
      lcd.print(0x14, BYTE);
      val_avg-=min((DARKBARS ? 7 : 5),val_avg);
    } else if (val_avg>=1) {
      lcd.print(0x0F + val_avg, BYTE);
      val_avg=0;
    } else {
      lcd.print(0x20, BYTE);
    }
  }

On the lower row it's a bit tricky: we'll light up the columns from min to max values to have a better feeling of the dynamic of the captured signal.
Here values are first prepared in a buffer then flushed out to the LCD, but it doesn't really matter. {{#fileanchor: testEMF.pde}}

  for (int i=0; i<16;i++) {
    if (val_min>=6) {
      LCD1[i]=0x20;
      val_min-=min((DARKBARS ? 7 : 5),val_min);
      val_max-=min((DARKBARS ? 7 : 5),val_max);
    } else if (val_min>=1) {
      if (val_max>=5) {
        LCD1[i]=0x13 + val_min;
        val_max-=min((DARKBARS ? 7 : 5),val_max);
      } else {
        // tricky part, both min & max on same digit
        int mm=0;
        uint8_t mmchr[8];
        for (int k=4; k>=0; k--) {
          if ((k + val_min <= 5) && (k + val_max >=5 ))
            mm+=1<<k;
        }
        for (int k=0; k<7; k++)
          mmchr[k]=mm;
        mmchr[7]=0x00;
        // here we risk to manipulate again a char being displayed
        // for critical apps, better to work on an unused char
        // e.g. toggle between CG-RAM 0x00 and CG-RAM 0x01
        lcd.createChar(0x00, mmchr);
        LCD1[i]=0x00;
        val_max=0;
      }
      val_min=0;
    } else {
      if (val_max>=5) {
        LCD1[i]=0x14;
        val_max-=min((DARKBARS ? 7 : 5),val_max);
      } else if (val_max>=1) {
        LCD1[i]=0x0F + val_max;
        val_max=0;
      } else {
        LCD1[i]=0x20;
      }
    }
  }
  lcd.setCursor(0,1);
  for (int i=0; i<16;i++) {
    lcd.print(LCD1[i], BYTE);
  }
}

As said above, you can download the code [{{#filelink: testEMF.pde}} as testEMF.pde].