John Bradnam
Published © GPL3+

Electromagnetic Field Meter (EMF meter)

A small portable EMF meter to detect and measure magnetic fields

IntermediateFull instructions provided8 hours659
Electromagnetic Field Meter (EMF meter)

Things used in this project

Hardware components

Microchip ATtiny1614 Microprocessor
×1
AH3503 - Ratiometric hall effect sensor
×1
Tactile Switch, Top Actuated
Tactile Switch, Top Actuated
6mm Shaft with Button Top
×1
J5019 Battery Charge Micro USB Module
×1
Buzzer
Buzzer
Active buzzer
×1
OLED Display 128x32 0.91 inches with I2C Interface
DIYables OLED Display 128x32 0.91 inches with I2C Interface
×1
Li-Ion Battery 100mAh
Li-Ion Battery 100mAh
120mAh battery with right-angle through hole socket
×1
LED (generic)
LED (generic)
3mm Red
×1
Passive components
1 x 330R 0805 resistor, 1 x 0.1uF 0805 ceramic capacitor
×1
Pin header
1 x 10 pin straight male header
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Custom parts and enclosures

STL Files

STL files for 3D printing

Schematics

Schematic

PCB

Eagle files

Schematic & PCB in Eagle format

Code

MagneticFieldMeterV2.ino

Arduino
/**************************************************************************
 Magnetic Field Meter

 Modified from: Magnetic Field Meter, by Marco Zonca, 3/2021 - https://www.hackster.io/marcozonca/magnetic-field-meter-range-200mt-millitesla-67fac5
 
 Schematic & PCB at https://www.hackster.io/john-bradnam/electromagnetic-field-meter-emf-meter-b6c9ea
 
 2024-01-29 V2 John Bradnam (jbrad2089@gmail.com)
   Update program for ATtiny1614 and AH3503 Ratiometric Linear Hall Effect Sensor
   Changed display library to U8g2 to fit in ATtiny1614

 --------------------------------------------------------------------------
 Arduino IDE:
 --------------------------------------------------------------------------
  BOARD: ATtiny1614/1604/814/804/414/404/214/204
  Chip: ATtiny1614
  Clock Speed: 20MHz
  millis()/micros(): "Enabled (default timer)"
  Programmer: jtag2updi (megaTinyCore)

  ATTiny1614 Pins mapped to Ardunio Pins
 
              +--------+
          VCC + 1   14 + GND
  (SS)  0 PA4 + 2   13 + PA3 10 (SCK)
        1 PA5 + 3   12 + PA2 9  (MISO)
  (DAC) 2 PA6 + 4   11 + PA1 8  (MOSI)
        3 PA7 + 5   10 + PA0 11 (UPDI)
  (RXD) 4 PB3 + 6    9 + PB0 7  (SCL)
  (TXD) 5 PB2 + 7    8 + PB1 6  (SDA)
              +--------+
  
 --------------------------------------------------------------------------
  Magnetic Field Meter, output in milliTesla (mT)
 --------------------------------------------------------------------------
 
 from sensor +- 1.3 mV = +- 1 Gauss = +- 0.1 mT
 from sensor 2.5V = 0 (0-2.5V = -2500 to 0 mV, 2.5-5V = 0 to 2500mV)  
 
 **************************************************************************/

#include <Wire.h>
#include <U8g2lib.h>
#include <avr/sleep.h>

#define HALL_PIN 9  //PA2 D9
#define SPK_PIN 0   //PA4 D0
#define BTN_PIN 1   //PA5 D1
#define LED_PIN 2   //PA6 D2

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

float aVal = 0;
float aZero = 0;
float aRead = 0;
float VL = 0;
float Gauss = 0;
float mTesla = 0;
float fTot = 0;
float fNr = 0;
float fMax = 0;
float fMin = 0;
float fAvr = 0;

unsigned long updateTimeout = 0;
unsigned long sleepTimeout = 0;

#define UPDATE_TIMEOUT 250     // update display and average calc every 0.25 seconds
#define SLEEP_TIMEOUT 20000    // automatic shutdown

char line1[32]; 
char line2[32]; 

//----------------------------------------------------------
// Handle pin change interrupt when SELECT button is pressed
//----------------------------------------------------------

void SwitchInterrupt()
{
}

//----------------------------------------------------------
// Setup hardware
//----------------------------------------------------------

void setup() 
{
  //Serial.begin(9600);

  //analogWrite on the DAC pin (PA6) will provide a voltage
  //on that pin that is from zero volts to the reference voltage.
  DACReference(INTERNAL4V34);
  
  pinMode(HALL_PIN, INPUT);
  pinMode(BTN_PIN, INPUT_PULLUP);
  pinMode(SPK_PIN,OUTPUT);
  pinMode(LED_PIN,OUTPUT);
  
  u8g2.begin();
  showSplashScreen();

  //Used to wake up CPU
  attachInterrupt(digitalPinToInterrupt(BTN_PIN),SwitchInterrupt,CHANGE);
  
  updateTimeout = millis() + UPDATE_TIMEOUT;
  sleepTimeout = millis() + SLEEP_TIMEOUT;
  
  //Set zero on powerup
  aZero = 0;
}

//----------------------------------------------------------
// Main program loop
//----------------------------------------------------------

void loop() 
{
  aRead = analogRead(HALL_PIN);
  if (aZero == 0 || digitalRead(BTN_PIN) == LOW)
  {
    aZero = aRead;  // initial auto zero
    sleepTimeout = millis() + SLEEP_TIMEOUT;
  }
  aVal = (map(aRead,0,1023,0,5000) - map(aZero,0,1023,0,5000)) / 1.3;  // to milliVolt, to Gauss
  if (aVal != 0) 
  {
    fTot = fTot + aVal;
    fNr = fNr + 1;
  }
  if (aVal > fMax)
  {
    fMax = aVal;  // mem max
  }
  if (aVal < fMin)
  {
    fMin = aVal;  // mem min
  }

  if (millis() >= updateTimeout)
  {
    showVal();
    updateTimeout = millis() + UPDATE_TIMEOUT;
  }

  //Sleep after SLEEP_TIMEOUT or button pressed
  if (millis() >= sleepTimeout)
  {
    systemSleep();
    setup();
  }
}

//----------------------------------------------------------
// Show splash screen
//----------------------------------------------------------

void showSplashScreen()
{
  //Splash screen 1
  displayLines("ATtiny1614", "EMF Meter");
  delay(3000);
}

//----------------------------------------------------------
// Display line buffers
//----------------------------------------------------------

void displayLines()
{
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_helvB12_tr); // helvetica bold
  int width = u8g2.getStrWidth(line1);
  u8g2.drawStr((128-width)>>1,14,line1);
  width = u8g2.getStrWidth(line2);
  u8g2.drawStr((128-width)>>1,29,line2);
  u8g2.sendBuffer();
}

//----------------------------------------------------------
// Display line buffers
//----------------------------------------------------------

void displayLines(char const* line1, char const* line2)
{
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_helvB12_tr); // helvetica bold
  int width = u8g2.getStrWidth(line1);
  u8g2.drawStr((128-width)>>1,14,line1);
  width = u8g2.getStrWidth(line2);
  u8g2.drawStr((128-width)>>1,29,line2);
  u8g2.sendBuffer();
}

//----------------------------------------------------------
// Display the current magnetic field
//----------------------------------------------------------

void showVal() 
{
  if (fMax != 0) 
  {
    fTot = fTot - fMax;  // average without max
    fNr = fNr - 1;    
  }
  if (fMin != 0)
  {
    fTot = fTot - fMin;  // average without min
    fNr = fNr - 1;    
  }
  if (fNr > 0) 
  {
    aVal = fTot / fNr;  // average value
  } 
  else 
  {
    aVal = 0;
  }
  if (aVal != aZero) 
  {
    Gauss = aVal;
    mTesla = Gauss / 10;  // to milliTesla
    VL = abs((aVal) / 10);
    if (VL > 255) 
    {
      VL = 255;
    }
    analogWrite(LED_PIN,VL);  // lights led
    analogWrite(SPK_PIN,VL);  // beeps buz
  } 
  else 
  {
    Gauss = 0;
    mTesla = 0;
    analogWrite(LED_PIN,0);  // off led
    analogWrite(SPK_PIN,0);  // off buz
  }

  //If there is a reading greater than 5mT, reset sleep timeout
  if (abs(mTesla) > 5.0)
  {
    sleepTimeout = millis() + SLEEP_TIMEOUT;
  }

  //Display the magnetic field in mT
  strcpy(line1, "EMF Meter");
  int t = (int)(round(mTesla * 10));
  sprintf(line2,"%d.%d mT",t/10,abs(t)%10);
  displayLines();
  
  fTot = 0;
  fNr = 0;
  fMin = 0;
  fMax = 0;
}

//----------------------------------------------------------
// Shut down OLED and put ATtiny to sleep
// Will wake up when LEFT button is pressed
//----------------------------------------------------------

void systemSleep() 
{
  Gauss = 0;
  mTesla = 0;
  fTot = 0;
  fNr = 0;
  fMin = 0;
  fMax = 0;
  analogWrite(LED_PIN,0);     // off led
  analogWrite(SPK_PIN,0);     // off buz
  
  u8g2.clearBuffer();         // clear the internal memory
  u8g2.sendBuffer();          // transfer internal memory to the display
  //u8g2.setPowerSave(true);
  //cbi(ADCSRA,ADEN);                   // switch Analog to Digitalconverter OFF
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // sleep mode is set here
  sleep_enable();
  sleep_mode();                         // System actually sleeps here
  sleep_disable();                      // System continues execution here when watchdog timed out
  //sbi(ADCSRA,ADEN);                   // switch Analog to Digitalconverter ON
  //u8g2.setPowerSave(false);
  while (digitalRead(BTN_PIN) == LOW) ; //Wait for button release
}

Credits

John Bradnam

John Bradnam

141 projects • 170 followers
Thanks to Marco Zonca.

Comments