Difference between revisions of "Arduino EMF"

From YobiWiki
Jump to navigation Jump to search
m (Created page with 'Back to Arduino page. Based on [http://www.aaronalai.com/emf-detector Aaron's detector], here using a 2x16 LCD, actually my VFD. <br>Upper row gives the averaged value on 10…')
 
m (Reverted edits by JasonAnderson (talk) to last revision by PhilippeTeuwen)
 
(5 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
Back to [[Arduino]] page.
 
Back to [[Arduino]] page.
   
Based on [http://www.aaronalai.com/emf-detector Aaron's detector], here using a 2x16 LCD, actually my VFD.
+
Based on [http://www.aaronalai.com/emf-detector Aaron's detector], here using a 2x16 LCD, actually my Samsung 16T202DA1J VFD (Vacuum Fluorescent Display).
 
<br>Upper row gives the averaged value on 10 samples
 
<br>Upper row gives the averaged value on 10 samples
<br>Lower row gives the signal between the minimal and maximal values observed in the 10 samples.
+
<br>Lower row gives the signal between the minimal and maximal values observed within those 10 samples.
<br>It's also a good example of a smooth bargraph on a LCD, with custom "chars" if needed.
+
<br>For real use the lower row is probably enough, here I show both to see the difference.
  +
<br>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.
<br>It is using the library
 
  +
* http://www.arduino.cc/en/Tutorial/LiquidCrystal
 
  +
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 [{{#file: testEMF.pde}} as testEMF.pde]
 
  +
 
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 [http://www.flickr.com/photos/doegox/4027089863 in action (video)] and [http://www.flickr.com/photos/doegox/4027089931 (picture)]
  +
  +
It is using the LiquidCrystal library so we'll include it:
  +
{{#fileanchor: testEMF.pde}}
 
<source lang=c>
 
<source lang=c>
// Doegox EMF detector
+
// Doegox EMF detector v1.0
 
// Based on Aaron ALAI EMF Detector April 22nd 2009 VERSION 1.0
 
// Based on Aaron ALAI EMF Detector April 22nd 2009 VERSION 1.0
   
 
#include <LiquidCrystal.h>
 
#include <LiquidCrystal.h>
   
  +
</source>
int inPin = 5; // analog 5
 
  +
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}}
  +
<source lang=c>
 
#define MAXN 10 // samples per cycle
 
#define MAXN 10 // samples per cycle
#define FREQ 50 // cycle in Hz, 60 for US folks
+
#define FREQ 50 // cycle in Hz
  +
</source>
  +
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}}
  +
<source lang=c>
 
#define DARKBARS true // extrapolate 2 bars between digits? smoother but we loose info
 
#define DARKBARS true // extrapolate 2 bars between digits? smoother but we loose info
   
  +
</source>
  +
Hardware part I:
  +
<br>We'll use the same analog pin 5 with a wire as antenna as in the original design.
  +
{{#fileanchor: testEMF.pde}}
  +
<source lang=c>
 
int inPin = 5; // analog 5
  +
</source>
  +
  +
Hardware part II:
  +
<br>We'll hook the LCD on the standard pins, cf the [http://www.arduino.cc/en/Tutorial/LiquidCrystal LiquidCrystal tutorial] and initialize the LCD as usual.
  +
{{#fileanchor: testEMF.pde}}
  +
<source lang=c>
 
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
 
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
   
Line 26: Line 52:
 
}
 
}
   
  +
</source>
  +
  +
Main loop:
  +
<br>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}}
  +
<source lang=c>
 
void loop() {
 
void loop() {
 
int val;
 
int val;
Line 31: Line 63:
 
int val_min = 1024; // will store and keep min 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 val_max = 0; // will store and keep max value of analog 5
int LCD1[16]; // will store temporarely content of 2nd LCD row
+
int LCD1[16]; // will store temporarily content of 2nd LCD row
   
 
for (int n=0; n<MAXN; n++) {
 
for (int n=0; n<MAXN; n++) {
Line 41: Line 73:
 
}
 
}
 
val_avg/=MAXN;
 
val_avg/=MAXN;
  +
</source>
  +
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}}
  +
<source lang=c>
 
val_avg = map(val_avg, 0, 1023, 1, 16*(DARKBARS ? 7 : 5));
 
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_min = map(val_min, 0, 1023, 1, 16*(DARKBARS ? 7 : 5));
 
val_max = map(val_max, 0, 1023, 1, 16*(DARKBARS ? 7 : 5));
 
val_max = map(val_max, 0, 1023, 1, 16*(DARKBARS ? 7 : 5));
   
  +
</source>
  +
On the upper row we simply display the average value with columns of dots.
  +
<br>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}}
  +
<source lang=c>
 
lcd.setCursor(0,0);
 
lcd.setCursor(0,0);
 
for (int i=0; i<16;i++) {
 
for (int i=0; i<16;i++) {
Line 57: Line 98:
 
}
 
}
 
}
 
}
  +
</source>
  +
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.
  +
<br>Here values are first prepared in a buffer then flushed out to the LCD, but it doesn't really matter.
  +
{{#fileanchor: testEMF.pde}}
  +
<source lang=c>
 
for (int i=0; i<16;i++) {
 
for (int i=0; i<16;i++) {
 
if (val_min>=6) {
 
if (val_min>=6) {
Line 103: Line 149:
 
}
 
}
 
</source>
 
</source>
  +
As said above, you can download the code [{{#filelink: testEMF.pde}} as testEMF.pde].

Latest revision as of 17:02, 2 March 2016

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].