ESP32 with BMP388 Barometric/Altimeter Sensor (Arduino IDE)

In this guide, you’ll learn how to use the BMP388 pressure sensor with the ESP32 board using Arduino IDE. The BMP388 is a tiny and precise absolute barometric pressure sensor. Because of its precision, it is often used to estimate altitude in drone applications. It can also be used in indoor/outdoor navigation, GPS applications, and others. The sensor communicates with a microcontroller using I2C or SPI communication protocols.

ESP32 with BMP388 Barometric Altimeter Sensor Arduino IDE

In this tutorial, we cover:

We have a similar tutorial for the ESP8266 board: ESP8266 NodeMCU with BMP388 Barometric/Altimeter Sensor (Arduino)

Introducing BMP388 Barometric Sensor

The BMP388 is a precise, low-power, low-noise absolute barometric pressure sensor that measures absolute pressure and temperature. Because pressure changes with altitude, we can also estimate altitude with great accuracy. For this reason, this sensor is handy for drone and navigation applications. You can also use it for other applications:

  • vertical velocity calculation;
  • internet of things;
  • weather forecast and weather stations;
  • health care applications;
  • fitness applications;
  • others…

We’re using the BMP388 sensor as a module, as shown in the figure below. It is also available in other different formats.

Front Pins BMP388 Sensor Module Altitude Pressure Temperature

The following picture shows the other side of the sensor.

Part Back Pins BMP388 Sensor Module Altitude Pressure Temperature

BMP388 Technical Data

The following table shows the key features of the BMP388 sensor. For more information, consult the datasheet.

Operation range300 to 1250 hPa (pressure)
-40 to +85ºC (temperature)
InterfaceI2C and SPI
Average typical current consumption3.4 µA @ 1Hz
Absolute accuracy pressure (typ.)
P=900 …1100 hPa (T=25 … 40°C)
±0.5 hPa
Relative accuracy pressure (typ.)
P=900…1100 hPa (T=25 … 40°C)
±0.08 hPa
Noise in pressure (lowest bandwidth, highest resolution)0.03 Pa
Maximum sampling rate200 Hz

BMP388 Pinout

Here’s the pinout of the BMP388 module we’re using—it might be slightly different for other modules.

VINPowers the sensor (5V)
3V3Powers the sensor (3V3)
GNDCommon GND
SCKSCL pin for I2C communication
SCK pin for SPI communication
SDOSDO (MISO) pin for SPI communication
SDISDI (MOSI) pin for SPI communication
SDA pin for I2C communication
CSChip select pin for SPI communication
INTInterrupt pin

BMP388 Interface

As mentioned previously, the BMP388 sensor supports I2C and SPI interfaces.

ESP32 BMP388 Sensor Altitude Pressure Temperature Arduino

BMP388 I2C

To use I2C communication protocol, use the following pins:

BMP388ESP32
SDI (SDA)GPIO 21
SCK (SCL)GPIO 22

GPIO 22 (SCL) and GPIO 21 (SDA) are the default ESP32 I2C pins. You can use other pins as long as you set them properly in the code.

Recommended reading: ESP32 I2C Communication: Set Pins, Multiple Bus Interfaces and Peripherals (Arduino IDE)

BMP388 SPI

To use SPI communication protocol, use the following pins:

BMP388ESP32
SCKGPIO 18
SDI (MOSI)GPIO 23
SDO (MISO)GPIO 19
CS (Chip Select)GPIO 5

These are the default ESP32 SPI pins. You can use other pins as long as you set them properly in the code.

Parts Required

ESP32 BMP388 Sensor Module Altitude Pressure Temperature

To complete this tutorial you need the following parts:

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

Schematic – ESP32 with BMP388

The BMP388 can communicate using I2C or SPI communication protocols.

ESP32 with BMP388 using I2C

Follow the next schematic diagram to wire the BMP388 to the ESP32 using the default I2C pins.

ESP32 BMP388 Wiring Circuit Diagram I2C with Arduino

ESP32 with BMP388 using SPI

Alternatively, you may want to use the SPI communication protocol instead. In that case, follow the next schematic diagram to wire the BMP388 to the ESP32 using the default SPI pins.

ESP32 BMP388 Wiring Circuit Diagram SPI

Preparing Arduino IDE

We’ll program the ESP32 board using Arduino IDE. So, make sure you have the ESP32 add-on installed. Follow the next tutorial:

If you want to use VS Code with the PlatformIO extension, follow the next tutorial instead to learn how to program the ESP32:

Installing the Adafruit BMP3XX Library

There are different libraries compatible with the BMP388 sensor and the ESP32. In this tutorial, we’ll use the Adafruit BMP3XX library.

Follow the next steps to install the library in your Arduino IDE:

Open your Arduino IDE and go to Sketch Include Library > Manage Libraries. The Library Manager should open.

Search for “adafruit bmp3xx ” in the search box and install the library.

Install Adafruit bmp3xx library Arduino IDE

Installing the Adafruit_Sensor Library

To use the BMP3XX library, you also need to install the Adafruit_Sensor library. Follow the next steps to install the library in your Arduino IDE:

Go to Sketch Include Library > Manage Libraries and type “Adafruit Unified Sensor” in the search box. Scroll all the way down to find the library and install it.

Installing Adafruit Unified Sensor Driver library

After installing the libraries, restart your Arduino IDE.

Code – Reading BMP388 Pressure, Altitude and Temperature

The best way to get familiar with a new sensor is to start with a basic example provided by the library.

After installing the BMP3XX library, open the Arduino IDE and go to File > Examples > Adafruit BMP3XX Library > bmp3XX_simpletest. We’ve made a few changes to make it fully compatible with the ESP32.

/***************************************************************************
  This is a library for the BMP3XX temperature & pressure sensor. Designed specifically to work with the Adafruit BMP388 Breakout ----> http://www.adafruit.com/products/3966
  These sensors use I2C or SPI to communicate, 2 or 4 pins are required to interface. Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!
  Written by Limor Fried & Kevin Townsend for Adafruit Industries. BSD license, all text above must be included in any redistribution
 ***************************************************************************/

// Complete project details: https://RandomNerdTutorials.com/esp32-bmp388-arduino/

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include "Adafruit_BMP3XX.h"

#define BMP_SCK 18
#define BMP_MISO 19
#define BMP_MOSI 23
#define BMP_CS 5

#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BMP3XX bmp;

void setup() {
  Serial.begin(115200);
  while (!Serial);
  Serial.println("Adafruit BMP388 / BMP390 test");

  if (!bmp.begin_I2C()) {   // hardware I2C mode, can pass in address & alt Wire
  //if (! bmp.begin_SPI(BMP_CS)) {  // hardware SPI mode  
  //if (! bmp.begin_SPI(BMP_CS, BMP_SCK, BMP_MISO, BMP_MOSI)) {  // software SPI mode
    Serial.println("Could not find a valid BMP3 sensor, check wiring!");
    while (1);
  }

  // Set up oversampling and filter initialization
  bmp.setTemperatureOversampling(BMP3_OVERSAMPLING_8X);
  bmp.setPressureOversampling(BMP3_OVERSAMPLING_4X);
  bmp.setIIRFilterCoeff(BMP3_IIR_FILTER_COEFF_3);
  bmp.setOutputDataRate(BMP3_ODR_50_HZ);
}

void loop() {
  if (! bmp.performReading()) {
    Serial.println("Failed to perform reading :(");
    return;
  }
  Serial.print("Temperature = ");
  Serial.print(bmp.temperature);
  Serial.println(" *C");

  Serial.print("Pressure = ");
  Serial.print(bmp.pressure / 100.0);
  Serial.println(" hPa");

  Serial.print("Approx. Altitude = ");
  Serial.print(bmp.readAltitude(SEALEVELPRESSURE_HPA));
  Serial.println(" m");

  Serial.println();
  delay(2000);
}

View raw code

Sea Level Pressure

To get more accurate results for pressure and altitude, we recommend that you adjust the sea level pressure for your location in the SEALEVELPRESSURE_HPA variable:

#define SEALEVELPRESSURE_HPA (1013.25)

The standard value is 1013.25 hPa. For more accurate results, check the sea level pressure at your location. You can use this website to check sea level pressure.

How the Code Works

Continue reading this section to learn how the code works, or skip to the Demonstration section.

Libraries

The code starts by including the needed libraries: the Wire library to use I2C, the SPI library (if you want to use SPI instead of I2C), the Adafruit_Sensor, and Adafruit_BMP3XX libraries to interface with the BMP388 sensor.

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include "Adafruit_BMP3XX.h"

SPI communication

We prefer to use the I2C communication protocol with the sensor. However, the code is prepared if you want to use SPI. The following lines of code define the SPI pins.

#define BMP_SCK 18
#define BMP_MISO 19
#define BMP_MOSI 23
#define BMP_CS 5

Sea level pressure

A variable called SEALEVELPRESSURE_HPA is created.

#define SEALEVELPRESSURE_HPA (1013.25)

This variable saves the pressure at the sea level in hectopascal (is equivalent to milibar). This variable is used to estimate the altitude for a given pressure by comparing it with the sea level pressure. This example uses the default value, but for accurate results, replace the value with the current sea level pressure at your location. You can use this website to check sea level pressure.

setup()

In the setup() start a serial communication.

Serial.begin(115200);

Init BMP388 Sensor I2C

This example uses I2C communication protocol by default. The following line starts an Adafruit_BMP3XX object called bmp on the default ESP32 I2C pins: GPIO 22 (SCL), GPIO 21 (SDA).

if (!bmp.begin_I2C()) { // hardware I2C mode, can pass in address & alt Wire

To use SPI, you need to comment this previous line and uncomment one of the following lines for hardware SPI (use the default SPI pins and choose the CS pin) or software SPI (use any pins).

//if (! bmp.begin_SPI(BMP_CS)) {  // hardware SPI mode  
//if (! bmp.begin_SPI(BMP_CS, BMP_SCK, BMP_MISO, BMP_MOSI)) {  // software SPI mode

Set up the following parameters (oversampling and filter) for the sensor.

// Set up oversampling and filter initialization
bmp.setTemperatureOversampling(BMP3_OVERSAMPLING_8X);
bmp.setPressureOversampling(BMP3_OVERSAMPLING_4X);
bmp.setIIRFilterCoeff(BMP3_IIR_FILTER_COEFF_3);
bmp.setOutputDataRate(BMP3_ODR_50_HZ);

To increase the resolution of the raw sensor data, it supports oversampling. We’ll use the default oversampling parameters, but you can change them.

  • setTemperatureOversampling(): set temperature oversampling.
  • setPressureOversampling(): set pressure oversampling.

These methods can accept one of the following parameters:

  • BMP3_NO_OVERSAMPLING
  • BMP3_OVERSAMPLING_2X
  • BMP3_OVERSAMPLING_4X
  • BMP3_OVERSAMPLING_8X
  • BMP3_OVERSAMPLING_16X
  • BMP3_OVERSAMPLING_32X

The setIIRFilterCoeff() function sets the coefficient of the filter (in samples). It can be:

  • BMP3_IIR_FILTER_DISABLE (no filtering)
  • BMP3_IIR_FILTER_COEFF_1
  • BMP3_IIR_FILTER_COEFF_3
  • BMP3_IIR_FILTER_COEFF_7
  • BMP3_IIR_FILTER_COEFF_15
  • BMP3_IIR_FILTER_COEFF_31
  • BMP3_IIR_FILTER_COEFF_63
  • BMP3_IIR_FILTER_COEFF_127

Set the output data rate with the setOutputDataRate() function. It can accept one of the following options:

BMP3_ODR_200_HZ, BMP3_ODR_100_HZ, BMP3_ODR_50_HZ, BMP3_ODR_25_HZ,BMP3_ODR_12_5_HZ, BMP3_ODR_6_25_HZ, BMP3_ODR_3_1_HZ, BMP3_ODR_1_5_HZ, BMP3_ODR_0_78_HZ, BMP3_ODR_0_39_HZ,BMP3_ODR_0_2_HZ, BMP3_ODR_0_1_HZ, BMP3_ODR_0_05_HZ, BMP3_ODR_0_02_HZ, BMP3_ODR_0_01_HZ, BMP3_ODR_0_006_HZ, BMP3_ODR_0_003_HZ, or BMP3_ODR_0_001_HZ

loop()

In the loop(), we’ll get measurements from the BMP388 sensor. First, tell the sensor to get new readings with bmp.performReading().

if (! bmp.performReading()) {
  Serial.println("Failed to perform reading :(");
  return;
}

Then, get and print the temperature, pressure and altitude readings as follows:

Serial.print("Temperature = ");
Serial.print(bmp.temperature);
Serial.println(" *C");

Serial.print("Pressure = ");
Serial.print(bmp.pressure / 100.0);
Serial.println(" hPa");

Serial.print("Approx. Altitude = ");
Serial.print(bmp.readAltitude(SEALEVELPRESSURE_HPA));
Serial.println(" m");

You get each specific reading as follows:

  • bmp.temperature: returns temperature reading
  • bmp.pressure: returns pressure reading
  • bmp.readAltitude (SEALEVELPRESSURE_HPA): returns altitude estimation

Demonstration

After inserting the sea level pressure for your location, you can upload the code to your board. In your Arduino IDE, go to Tools > Boards and select the board you’re using. Then, in Tools > Port, select the COM port.

After uploading, open the Serial Monitor at a baud rate of 115200. The readings will be printed in the Serial Monitor.

BMP388 Altitude Pressure Display Serial Monitor

Notice that if you increase the sensor’s altitude, it will be reflected in the altitude reading. The altitude estimation is pretty accurate. It can detect small changes in the centimeters or inches range. You can check it by comparing the altitude you’re getting with the altitude of your location. To get your location’s altitude, you can use this website.


ESP32 Web Server with BMP388

In this section, we provide an example of web server that you can build with the ESP32 to display BMP388 readings on a web page.

ESP32 BMP388 Sensor Altitude Pressure Temperature Arduino Web Server

Installing Libraries – Async Web Server

To build the web server you need to install the following libraries. Click the links below to download the libraries.

These libraries aren’t available to install through the Arduino Library Manager, so you need to copy the library files to the Arduino Installation Libraries folder. Alternatively, in your Arduino IDE, you can go to Sketch Include Library > Add .zip Library and select the libraries you’ve just downloaded.

Code

Then, upload the following code to your board (type your SSID and password).

/*********
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-bmp388-arduino/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*********/

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP3XX.h>
#include <WiFi.h>
#include <ESPAsyncWebServer.h>

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

//Uncomment if using SPI
/*#define BMP_SCK 18
#define BMP_MISO 19
#define BMP_MOSI 23
#define BMP_CS 5*/

#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BMP3XX bmp;

float temp;
float pres;
float alt;

AsyncWebServer server(80);
AsyncEventSource events("/events");

unsigned long lastTime = 0;  
unsigned long timerDelay = 30000;  // send readings timer

void getBMPReadings(){
  if (! bmp.performReading()) {
    Serial.println("Failed to perform reading :(");
    return;
  }
  temp = bmp.temperature;
  pres = bmp.pressure / 100.0;
  alt = bmp.readAltitude(SEALEVELPRESSURE_HPA);
}

String processor(const String& var){
  getBMPReadings();
  //Serial.println(var);
  if(var == "TEMPERATURE"){
    return String(temp);
  }
  else if(var == "PRESSURE"){
    return String(pres);
  }
 else if(var == "ALTITUDE"){
    return String(alt);
  }
}

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <title>BMP388 Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
  <link rel="icon" href="data:,">
  <style>
    html {font-family: Arial; display: inline-block; text-align: center;}
    p {  font-size: 1.2rem;}
    body {  margin: 0;}
    .topnav { overflow: hidden; background-color: #0F7173; color: white; font-size: 1.4rem; }
    .content { padding: 20px; }
    .card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); }
    .cards { max-width: 700px; margin: 0 auto; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
    .reading { font-size: 2rem; }
    .card.temperature { color: #272932; }
    .card.altitude { color: #D8A47F; }
    .card.pressure { color: #F05D5E; }
  </style>
</head>
<body>
  <div class="topnav">
    <h3>BMP388 WEB SERVER</h3>
  </div>
  <div class="content">
    <div class="cards">
      <div class="card pressure">
        <h4><i class="fas fa-angle-double-down"></i> PRESSURE</h4><p><span class="reading"><span id="pres">%PRESSURE%</span> hPa</span></p>
      </div>
      <div class="card altitude">
        <h4><i class="fas fa-long-arrow-alt-up"></i> ALTITUDE</h4><p><span class="reading"><span id="alt">%ALTITUDE%</span> m</span></p>
      </div>
      <div class="card temperature">
        <h4><i class="fas fa-thermometer-half"></i> TEMPERATURE</h4><p><span class="reading"><span id="temp">%TEMPERATURE%</span> &deg;C</span></p>
      </div>
    </div>
  </div>
<script>
if (!!window.EventSource) {
 var source = new EventSource('/events');
 
 source.addEventListener('open', function(e) {
  console.log("Events Connected");
 }, false);
 source.addEventListener('error', function(e) {
  if (e.target.readyState != EventSource.OPEN) {
    console.log("Events Disconnected");
  }
 }, false);
 
 source.addEventListener('message', function(e) {
  console.log("message", e.data);
 }, false);
 
 source.addEventListener('temperature', function(e) {
  console.log("temperature", e.data);
  document.getElementById("temp").innerHTML = e.data;
 }, false);
 
 source.addEventListener('pressure', function(e) {
  console.log("pressure", e.data);
  document.getElementById("pres").innerHTML = e.data;
 }, false);
 
 source.addEventListener('altitude', function(e) {
  console.log("altitude", e.data);
  document.getElementById("alt").innerHTML = e.data;
 }, false);
}
</script>
</body>
</html>)rawliteral";

void setup() {
  Serial.begin(115200);

  WiFi.mode(WIFI_STA);
  
  // Set device as a Wi-Fi Station
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Setting as a Wi-Fi Station..");
  }
  Serial.print("Station IP Address: ");
  Serial.println(WiFi.localIP());
  Serial.println();

  // Init BMEP388 sensor
  if (!bmp.begin_I2C()) {   // hardware I2C mode, can pass in address & alt Wire
  //if (! bmp.begin_SPI(BMP_CS)) {  // hardware SPI mode  
  //if (! bmp.begin_SPI(BMP_CS, BMP_SCK, BMP_MISO, BMP_MOSI)) {  // software SPI mode
    Serial.println("Could not find a valid BMP3 sensor, check wiring!");
    while (1);
  }
  // Set up oversampling and filter initialization
  bmp.setTemperatureOversampling(BMP3_OVERSAMPLING_8X);
  bmp.setPressureOversampling(BMP3_OVERSAMPLING_4X);
  bmp.setIIRFilterCoeff(BMP3_IIR_FILTER_COEFF_3);
  bmp.setOutputDataRate(BMP3_ODR_50_HZ);

  //Get readings when initializing
  getBMPReadings();

  // Handle Web Server
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, processor);
  });

  // Handle Web Server Events
  events.onConnect([](AsyncEventSourceClient *client){
    if(client->lastId()){
      Serial.printf("Client reconnected! Last message ID that it got is: %u\n", client->lastId());
    }
    // send event with message "hello!", id current millis
    // and set reconnect delay to 1 second
    client->send("hello!", NULL, millis(), 10000);
  });
  server.addHandler(&events);
  server.begin();
}

void loop() {
  if ((millis() - lastTime) > timerDelay) {
    getBMPReadings();
    Serial.printf("Pressure = %.2f hPa \n", pres);
    Serial.printf("Altitude = %.2f m \n", alt);
    Serial.printf("Temperature = %.2f ºC \n", temp);
    Serial.println();

    // Send Events to the Web Server with the Sensor Readings
    events.send("ping",NULL,millis());
    events.send(String(pres).c_str(),"pressure",millis());
    events.send(String(alt).c_str(),"altitude",millis());
    events.send(String(temp).c_str(),"temperature",millis());
    
    lastTime = millis();
  }
}

View raw code

Demonstration

After uploading, open the Serial Monitor at a baud rate of 115200 to get the ESP32 IP address.

Open a browser and type the IP address. You should get access to the web server with the latest sensor readings. You can access the web server on your computer, tablet, or smartphone in your local network.

ESP32 ESP8266 Simple Web Server BMP388 Altimeter

The readings are updated automatically on the web server using Server-Sent Events. You can check the Server-Sent Events tutorial to have an idea of how it works.

Wrapping Up

The BMP388 is a small and very precise pressure sensor that allows you to estimate altitude with great precision. The sensor also measures temperature. It is great for outdoor/indoor navigation, drones, weather stations, and other applications.

You’ve learned how to use the sensor with the ESP32 development board and Arduino IDE in this tutorial. We hope you found this getting started guide useful. In addition, we have guides for other popular sensors:

Learn more about the ESP32 with our resources:

Thanks for reading.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

15 thoughts on “ESP32 with BMP388 Barometric/Altimeter Sensor (Arduino IDE)”

      • I translate from Portuguese to English by google translator.
        Hello, I set up your project and it worked, but I would like to leave it with a house after the comma, which field changes?

        the codes below where it was 2f I changed it to 1f, but it only changed on the serial monitor, it didn’t change on the cell phone screen. I couldn’t find where it changes on the cell phone screen.
        Serial.printf(“Pressure = %.2f hPa \n”, pres);
        Serial.printf(“Altitude = %.1f m \n”, alt);
        Serial.printf(“Temperature = %.1f ºC \n”, temp);

        Reply
        • Try this 4 dec digits

          // Send Events to the Web Server with the Sensor Readings
          events.send(“ping”,NULL,millis());

          char buffer[80];

          // events.send(String(pres).c_str(),”pressure”,millis());
          sprintf(buffer, “%.4f”, pres);
          events.send(String(buffer).c_str(),”pressure”,millis());

          //events.send(String(alt).c_str(),”altitude”,millis());
          sprintf(buffer, “%.4f”, alt);
          events.send(String(buffer).c_str(),”altitude”,millis());

          //events.send(String(temp).c_str(),”temperature”,millis());
          sprintf(buffer, “%.4f”, temp);
          events.send(String(buffer).c_str(),”temperature”,millis());

          Reply
  1. It is possible to read the actual sea level pressure, in a prefixed weather station, so to substitute in
    <#define SEALEVELPRESSURE_HPA (1013.25)>
    the actual sea level pressure?
    So the altitude will be always accurate.

    Thanks
    Renzo

    Reply

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.