Fab Academy 2026

Week 11: Networking and Communications

Exploring communication protocols and integrating biometric sensors using I2C communication.

Main Protocol

I2C communication

Main Hardware

ESP32-C6 and MAX30102

Main Goal

Establish communication between embedded devices and sensors.

Assignments

Group Assignment

Individual Assignment

Group Assignment

Group Assignment – Networking and Communications

During the group assignment we explored different communication protocols commonly used in embedded systems and IoT projects.

We analyzed how each protocol works, its advantages, limitations and possible applications depending on the project requirements.

Understanding these protocols helped me decide which communication method was the most suitable for my wearable biometric project.

Communication Protocols

Different communication protocols offer different advantages depending on speed, distance, power consumption and system complexity.

During this week I researched and tested several common protocols used in embedded systems and IoT applications.

Different standard communication protocols

MonoFab PCB

Wiring

Connect the two boards like this:

  • Controller (SDA) to Target (SDA)
  • Controller (SCL) to Target (SCL)
  • Controller GND to Target GND
  • 4.7 kOhm pull-up from SDA to 3.3 V
  • 4.7 kOhm pull-up from SCL to 3.3 V

Main code steps

Controller

  • Include Wire.h.
  • Start I2C with Wire.begin(SDA_PIN, SCL_PIN, frequency).
  • Send data using Wire.beginTransmission(address) and Wire.write(...).
  • Finish transmission with Wire.endTransmission().
  • Request data from the Target using Wire.requestFrom(...).
  • Read the response using Wire.read().

Target

  • Include Wire.h.
  • Create a receive callback using Wire.onReceive(...).
  • Create a request callback using Wire.onRequest(...).
  • Start slave mode with Wire.begin(address, SDA_PIN, SCL_PIN, frequency).
  • Store received data in a variable.
  • Return data when the Controller requests it.

When to Use I2C

I2C is recommended in situations where you need to connect multiple low-speed devices using only two wires (SDA and SCL). It is especially useful in embedded systems where pin availability is limited.

  • When connecting multiple sensors or modules to a single microcontroller.
  • When you need a simple communication bus between a controller and several target devices.
  • When wiring simplicity is important, since I2C only uses two communication lines.
  • For short-distance communication on the same PCB or within the same device enclosure.
  • When devices support addressing, allowing many components to share the same bus.

I2C is not ideal for high-speed or long-distance communication. In those cases, protocols like SPI or UART may be more suitable.

Workflow Overview

1. Protocol Research

Study different communication protocols and compare their characteristics.

2. Hardware Integration

Connect the MAX30102 sensor to the ESP32-C6 using I2C communication.

3. Programming and Testing

Validate stable communication and biometric data acquisition.

Why I Chose I2C

Since the MAX30102 sensor communicates using SDA and SCL pins and the ESP32-C6 board is physically close to the sensor, I2C communication was the best option for this project.

I2C simplifies wiring, reduces the number of required pins and provides stable communication between embedded devices.

These are the pinouts used for the connection:

ESP32-C6 board
MAX30102 module

Programming

The code initializes the MAX30102 sensor using I2C communication and continuously processes biometric heart rate data.

The program also filters noise, calculates BPM averages and activates physical outputs depending on heart rate variations.


#include <Wire.h>
#include "MAX30105.h"
#include "heartRate.h"

MAX30105 particleSensor;

const int LED_PIN = D1;
const int MOTOR_PIN = D2;

const byte RATE_SIZE = 12;

byte rates[RATE_SIZE];

byte rateSpot = 0;

long lastBeat = 0;

float beatsPerMinute;

int beatAvg;

bool baseEstablecida = false;

int muestrasCalibracion = 0;

const int TOTAL_MUESTRAS_CAL = 20;

float sumaCalibracion = 0;

float pulsoBase = 0;

int altosConsecutivos = 0;

const int MINIMO_PULSOS_ALTO = 3;

bool alertaActiva = false;

void setup() {

  Serial.begin(115200);

  delay(1000);

  pinMode(LED_PIN, OUTPUT);

  pinMode(MOTOR_PIN, OUTPUT);

  digitalWrite(LED_PIN, LOW);

  digitalWrite(MOTOR_PIN, LOW);

  Wire.begin(22, 23);

  if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) {

    Serial.println("MAX30102 no encontrado");

    while (1);

  }

  byte ledBrightness = 60;

  byte sampleAverage = 1;

  byte ledMode = 2;

  int sampleRate = 200;

  int pulseWidth = 411;

  int adcRange = 4096;

  particleSensor.setup(
    ledBrightness,
    sampleAverage,
    ledMode,
    sampleRate,
    pulseWidth,
    adcRange
  );

}

void loop() {

  long irValue = particleSensor.getIR();

  if (irValue < 50000) {

    digitalWrite(LED_PIN, LOW);

    digitalWrite(MOTOR_PIN, LOW);

    delay(500);

    return;

  }

  if (checkForBeat(irValue) == true) {

    long delta = millis() - lastBeat;

    lastBeat = millis();

    beatsPerMinute = 60 / (delta / 1000.0);

    if (beatsPerMinute < 140 &&
        beatsPerMinute > 40) {

      rates[rateSpot++] =
      (byte)beatsPerMinute;

      rateSpot %= RATE_SIZE;

      int sum = 0;

      for (byte x = 0; x < RATE_SIZE; x++) {

        sum += rates[x];

      }

      beatAvg = sum / RATE_SIZE;

      if (!baseEstablecida) {

        sumaCalibracion += beatsPerMinute;

        muestrasCalibracion++;

        if (muestrasCalibracion >=
            TOTAL_MUESTRAS_CAL) {

          pulsoBase =
          sumaCalibracion /
          TOTAL_MUESTRAS_CAL;

          baseEstablecida = true;

        }

      }

      else {

        float dif =
        beatAvg - pulsoBase;

        if (dif >= 4.0) {

          altosConsecutivos++;

          if (altosConsecutivos >=
              MINIMO_PULSOS_ALTO)

            alertaActiva = true;

        }

        else {

          altosConsecutivos = 0;

          alertaActiva = false;

        }

        if (alertaActiva) {

          digitalWrite(LED_PIN, HIGH);

          digitalWrite(MOTOR_PIN, HIGH);

        }

        else {

          digitalWrite(LED_PIN, LOW);

          digitalWrite(MOTOR_PIN, LOW);

        }

      }

    }

  }

}

Code Explanation

Sensor Initialization

Initializes I2C communication and prepares the MAX30102 sensor for biometric readings.

BPM Filtering

Uses averaging and filtering methods to reduce noise and stabilize BPM measurements.

Calibration

Establishes a baseline heart rate before entering monitoring mode.

Alert System

Activates the LED and vibration motor when BPM exceeds the baseline threshold.

Testing the Code

During testing I monitored the BPM values through the Serial Monitor to validate stable communication and heart rate detection.

Debugging and Problems

Motion Artifacts

Wrist movement still generates false BPM peaks during measurements.

Unstable Calibration

Noisy readings can still affect the baseline BPM calculation.

IR Signal Sensitivity

The infrared readings are highly sensitive to pressure and movement.

Communication Stability

Incorrect wiring or unstable connections can interrupt I2C communication.

Connection to My Final Project

This week is directly related to my wearable biometric project because communication between the sensor and the microcontroller is essential for real-time monitoring.

I2C communication allows stable biometric data transfer while keeping the wearable system compact and efficient.

In future iterations I may integrate BLE or ESP-NOW to send biometric information wirelessly to external devices.

Real-Time Monitoring

Stable communication is critical for reliable biometric sensing.

Wearable Integration

I2C simplifies the internal wiring of the wearable system.

Future Wireless Features

Wireless communication protocols could expand the project capabilities.

Downloadable Files