To build a reliable, low-latency breath sensor Arduino project, use the NXP MPX5010GP piezoresistive pressure sensor paired with an Arduino Nano V3 (ATmega328P). Human exhalation generates between 1 and 5 kPa of gauge pressure. The MPX5010GP measures 0–10 kPa, outputting a clean analog voltage that perfectly captures breath dynamics without the thermal latency of hot-wire sensors or the condensation failures of raw humidity modules.

This guide provides the exact hardware stack, a critical moisture-protection assembly step that prevents sensor death, and fully compilable C++ code with built-in error handling for ADC saturation and baseline drift.

Sensor Selection Decision Path

Makers frequently attempt to use environmental sensors for breath detection, leading to frustrating latency and calibration issues. Use this decision matrix to select the right transducer for your specific breath-sensing application.

Sensor Type / Model Measures Response Time Moisture Tolerance Verdict & Application
NXP MPX5010GP Gauge Pressure (0-10 kPa) < 1 ms Low (Requires PTFE trap) DEFAULT PICK. Best for spirometry, MIDI breath controllers, and flow measurement.
Bosch BME680 VOC, Humidity, Temp, Pressure ~1-3 seconds (Gas) High Choose only for detecting breath presence via VOC spikes, not for measuring flow or force.
10k NTC Thermistor Thermal Transfer ~50-200 ms Medium Choose for simple anemometer builds where digital I2C/Analog pressure sensors are unavailable.
Sensirion SHT41 Humidity & Temperature ~100 ms High Choose for condensation-based breathing rate monitors (e.g., sleep apnea masks).

The Decision: If your project requires measuring the force, volume, or velocity of a breath (like blowing out a candle or playing a digital wind instrument), terminate your search and buy the MPX5010GP. Environmental sensors simply cannot track the rapid pressure envelope of human exhalation.

Exact Parts List and Pin Mapping

The MPX5010GP requires a stable 5V reference. Do not attempt to run this specific sensor on a 3.3V logic board (like an ESP32 or Arduino Due) without an external 5V supply and a voltage divider on the output, as the sensor's internal bridge is ratiometric to its supply voltage.

Bill of Materials (BOM)

  • Microcontroller: Arduino Nano V3 (ATmega328P, 5V/16MHz variant)
  • Sensor: NXP MPX5010GP (DIP-6 package, gauge pressure)
  • Tubing: 4mm ID / 6mm OD silicone vacuum tubing
  • Moisture Trap: 13mm 0.22µm PTFE syringe filter (Critical for preventing saliva ingress)
  • Passives: Two 100nF (0.1µF) ceramic decoupling capacitors
  • Mouthpiece: 3D-printed T-piece or standard medical Luer slip adapter

Pin Mapping Table

MPX5010GP Pin Function Arduino Nano V3 Connection Notes
1 Vout (Analog Out) A0 Route via shielded cable if >10cm to avoid 50/60Hz noise.
2 GND GND Connect directly to Nano GND, not via breadboard power rail.
3 VCC (Vs) 5V Must be exactly 5.0V. Place 100nF cap between Pin 3 and Pin 2.
4, 5, 6 No Connect (NC) None Leave floating. Do not ground these pins.

Assembly and Moisture Protection

The number one cause of failure in DIY breath sensors is condensation and saliva ingress. When you blow into a tube, warm, saturated air hits the cooler sensor diaphragm, condenses, and shorts the internal piezoresistive bridge. Once wet, the MPX5010GP will output a permanently skewed baseline and eventually corrode.

Callout: The PTFE Moisture Trap Rule
You must install a hydrophobic 0.22µm PTFE syringe filter inline between the mouthpiece and the sensor port. PTFE (Teflon) allows air to pass freely but blocks liquid water and aerosolized saliva. Standard cotton or mesh filters will absorb moisture and create a pneumatic bottleneck, ruining your pressure readings.

Numbered Assembly Steps

  1. Prepare the Sensor: Solder header pins to the MPX5010GP. Solder the 100nF decoupling capacitor directly across Pin 2 (GND) and Pin 3 (VCC) on the underside of the sensor to suppress high-frequency noise.
  2. Wire the Analog Path: Connect Pin 1 (Vout) to Arduino A0. Keep this trace under 5cm if on a breadboard. If using a ribbon cable, twist the Vout wire with a GND wire to reject common-mode EMI.
  3. Construct the Pneumatic Manifold: Cut a 15cm length of 4mm silicone tubing. Insert the 13mm PTFE syringe filter into one end. Attach the other end to the MPX5010GP's Port 1 (the top metal nub).
  4. Attach the Mouthpiece: Connect your T-piece adapter to the PTFE filter. The T-piece allows you to blow across the sensor path rather than directly into it, reducing the velocity of aerosolized droplets hitting the filter.
  5. Verify Power: Plug the Nano into USB. Use a multimeter to verify exactly 5.0V (±0.1V) between the sensor's VCC and GND pins before proceeding to code.

Compilable Arduino Code with Error Handling

This firmware targets the Arduino Nano V3 (ATmega328P). It implements an Exponential Moving Average (EMA) digital low-pass filter to smooth out acoustic turbulence in the tubing, converts the raw 10-bit ADC value to kilopascals (kPa), and includes explicit error handling for hardware faults.


// Breath Sensor Arduino Firmware
// Target: Arduino Nano V3 (ATmega328P, 5V Logic)
// Sensor: NXP MPX5010GP (0-10 kPa Gauge Pressure)

#define SENSOR_PIN A0
#define BAUD_RATE 115200

// Calibration Constants for MPX5010GP
// Vout = Vs * (0.09 * P + 0.1)  => P = ((Vout / Vs) - 0.1) / 0.09
const float V_SUPPLY = 5.0;
const float ADC_MAX = 1023.0;
const float FILTER_ALPHA = 0.15; // EMA filter coefficient (0.0 to 1.0)

// Error Thresholds
const int ADC_SATURATION_THRESHOLD = 1010; // ~4.93V (Max pressure exceeded or short to 5V)
const int BASELINE_DRIFT_THRESHOLD = 140;  // ~0.68V (Sensor wet or damaged, should be ~0.5V at 0kPa)

float filteredPressure_kPa = 0.0;
int baselineADC = 0;

void setup() {
  Serial.begin(BAUD_RATE);
  analogReference(DEFAULT); // Ensure 5V reference on Nano
  
  // Establish baseline (sensor at rest, no blowing)
  long sum = 0;
  for (int i = 0; i < 50; i++) {
    sum += analogRead(SENSOR_PIN);
    delay(10);
  }
  baselineADC = sum / 50;
  
  // Check for hardware fault on startup
  if (baselineADC > BASELINE_DRIFT_THRESHOLD) {
    Serial.println("ERR: BASELINE_DRIFT");
    Serial.println("Sensor diaphragm is likely wet or damaged. Replace PTFE filter and sensor.");
    while(1); // Halt execution
  }
  
  Serial.println("Breath Sensor Initialized. Baseline ADC: " + String(baselineADC));
}

void loop() {
  int rawADC = analogRead(SENSOR_PIN);
  
  // Error Handling: ADC Saturation
  if (rawADC >= ADC_SATURATION_THRESHOLD) {
    Serial.println("ERR: ADC_SATURATION");
    Serial.println("Pressure exceeds 10kPa or Vout is shorted to 5V rail.");
    delay(500);
    return;
  }
  
  // Apply Exponential Moving Average (EMA) Filter
  // Filters out high-frequency acoustic noise from the tubing
  filteredPressure_kPa = (FILTER_ALPHA * (rawADC - baselineADC)) + ((1.0 - FILTER_ALPHA) * filteredPressure_kPa);
  
  // Convert filtered ADC delta to Voltage, then to kPa
  // Using transfer function: P(kPa) = ((Vout / Vs) - 0.1) / 0.09
  // Note: We calculate delta from baseline to account for minor 5V rail fluctuations
  float voltageDelta = (filteredPressure_kPa / ADC_MAX) * V_SUPPLY;
  float pressure_kPa = voltageDelta / 0.45; // Simplified slope for delta calculation
  
  if (pressure_kPa < 0) pressure_kPa = 0; // Clamp negative noise floor
  
  // Output for Serial Plotter (Format: Raw, Filtered_kPa)
  Serial.print(rawADC);
  Serial.print(",");
  Serial.println(pressure_kPa, 3);
  
  delay(20); // 50Hz sample rate
}

Debugging: First Three Things to Check When It Fails

If your Serial Monitor throws an error or the readings are erratic, follow this ranked troubleshooting path. Do not skip steps.

1. Exact Error: ERR: BASELINE_DRIFT

What it means: On startup, the sensor output voltage is higher than 0.68V (ADC > 140). At atmospheric pressure, the MPX5010GP should output roughly 0.5V (ADC ~102).

  • Cause A (Most Likely): Moisture ingress. Saliva or heavy condensation has breached the PTFE filter and pooled on the silicon die, altering the piezoresistive bridge.
  • Cause B: Tubing kink or blockage. If the tube is bent or the filter is clogged with debris, ambient pressure changes can trap a vacuum/pressure inside the manifold.
  • Fix: Disconnect the tubing. Inspect the sensor port under a magnifying glass. If wet, the sensor is permanently compromised and must be replaced. Install a fresh PTFE filter.

2. Exact Error: ERR: ADC_SATURATION

What it means: The ADC is reading > 1010 (approx 4.93V), indicating the sensor is maxing out its 10 kPa range or the signal wire is shorted to the 5V rail.

  • Cause A: You are blowing into a sealed system. If the exhaust port of your mouthpiece is blocked, pressure builds infinitely until it exceeds the sensor's burst pressure.
  • Cause B: Breadboard short. The Vout trace on Pin 1 is physically touching the 5V rail on the breadboard.
  • Fix: Ensure your mouthpiece has an exhaust leak (a T-piece open to the atmosphere). Check wiring continuity between A0 and 5V with a multimeter while powered off.

3. Symptom: Extreme 50/60Hz Noise (Sine wave in Serial Plotter)

What it means: The analog signal is picking up electromagnetic interference from mains wiring or the PC's switching power supply.

  • Cause: High-impedance analog traces acting as antennas, or a missing decoupling capacitor.
  • Fix: Verify the 100nF capacitor is soldered directly to the sensor pins. If using long wires, wrap the signal and ground wires around each other (twisted pair) to reject common-mode noise. For a deep dive on Arduino ADC noise rejection, consult the official Arduino analogRead() documentation.

Extending or Simplifying the Build

Depending on your end goal, you can strip this project down to its bare essentials or scale it up into a medical-grade diagnostic tool.

How to Simplify (MIDI Breath Controller)

If you are building a MIDI wind instrument controller (e.g., an electronic saxophone), you do not need absolute kPa accuracy or heavy filtering.

  • Drop the EMA Filter: Remove the FILTER_ALPHA math. Musicians prefer raw, instantaneous response, even if it's slightly noisy.
  • Map to MIDI CC: Map the 0-1023 ADC range directly to MIDI Control Change (CC) messages (0-127) using the Arduino MIDI Library. Send the data over USB to your DAW to control synth filters or volume swells.

How to Extend (FEV1 Spirometry Calculator)

If you are building a health diagnostic tool to measure Forced Expiratory Volume in 1 second (FEV1), you must calculate the integral of flow over time.

  • Add a Flow Restrictor: Pressure alone does not equal volume. You must add a known pneumatic resistance (like a mesh screen or a specific orifice diameter) to the exhaust port. Using the NXP MPX5010 datasheet and Bernoulli's principle, calculate flow rate (Liters/second) from the pressure drop across the restrictor.
  • Implement Riemann Sum Integration: In the code, multiply the calculated flow rate by the time delta (dt) of each loop iteration, and accumulate this value in a totalVolume variable. Stop accumulating exactly 1000ms after the pressure crosses the exhalation threshold.
  • Add an OLED: Wire an SSD1306 128x64 I2C OLED display to A4/A5 to display the FEV1 volume locally without needing a PC.

By respecting the physics of pneumatic sensing and protecting the silicon from moisture, the MPX5010GP transforms the Arduino from a simple blinker of LEDs into a highly responsive, precision bio-signal acquisition tool.