Community project

Wristband Vibrotactile Receiver

ESP32
Photo of Wristband Vibrotactile Receiver
Generated with AI

Charles Aldus Cajayon

Published October 19, 2026

This wristband receiver delivers haptic feedback via a coin vibration motor, making it ideal for notification systems, gesture recognition, or immersive wearable applications. Built around an ESP32 microcontroller, the device combines a compact LiPo battery with intelligent power management and a haptic driver capable of playing dozens of pre-programmed vibration effects.

The guide provides a complete wiring diagram, full parts list, and step-by-step assembly instructions that walk through battery integration, power switching, fuel-gauge monitoring, and haptic driver configuration. Included firmware demonstrates battery state tracking, low-battery alerts, and LRA auto-resonance calibration to ensure consistent vibration performance from the 10 mm motor.

Wiring diagram

Gather all the parts

QtyComponent
1

Lithium Ion Polymer Battery - 3.7V 400mAh

Lithium-ion polymer (also known as 'lipo' or 'lipoly') batteries are thin, light, and powerful. The output ranges from 4.2V when completely charged to 3.7V. This battery has a capacity of 400mAh for a total of about 1.9 Wh. If you need a larger (or smaller!) battery, we have a full range of LiPoly batteries.The batteries come pre-attached with a genuine 2-pin 25mm long JST-PH connector as shown and include the necessary protection circuitry. Because they have a genuine JST connector, not a knock-off, the cable won't snag or get stuck in a matching JST jack, they click in and out smoothly.

1

TP4056 Li-Ion/LiPo charger module with protection

TP4056 single-cell Li-Ion/LiPo linear charger module, 5V USB input, 1A charge current (programmable). Common variants ship with DW01 protection. Pair with battery_lipo_storage for the cell.

1

Breadboard-friendly SPDT Slide Switch

Compact through-hole SPDT slide switch with three pins for direct breadboard insertion.

1

MAX17048 LiPo fuel-gauge breakout

A small battery monitor module that reports charge percentage and voltage over the shared data wires.

1

Haptic Driver

DRV2605L haptic motor driver breakout for ERM/LRA vibration motors. Controlled over I2C at 0x5A, uses an internal waveform library, and drives the motor through OUT+/OUT- instead of direct MCU GPIO.

1

10 mm coin LRA vibration motor

A two-wire linear resonant actuator that produces the wristband’s vibration alert.

1

4.7 kΩ I2C SDA pull-up resistor

4.7 kΩ

A resistor that holds the shared SDA data wire at 3.3 V when no device is pulling it low.

1

4.7 kΩ I2C SCL pull-up resistor

4.7 kΩ

A resistor that holds the shared SCL clock wire at 3.3 V when no device is pulling it low.

1

22 µF electrolytic capacitor

22 µF, ≥6.3 V

A polarized capacitor that smooths battery-powered voltage dips near the ESP32 power input.

1

100 nF ceramic capacitor

100 nF, ≥6.3 V

A small non-polarized capacitor that filters fast electrical noise at the ESP32 power input.

Assemble it in 7 steps

1. Prepare the charger and battery

With all power disconnected, connect the LiPo positive lead to tp4056_1 B+ and the LiPo negative lead to tp4056_1 B-. Use the TP4056 module pads labelled B+ and B−, not OUT+ and OUT−.

  • The TP4056 USB connector is only for charging; its OUT pads feed the wristband.
  • Do not reverse B+ and B− — a reversed LiPo connection can destroy the charger module or heat the battery. Set the TP4056 charge current to no more than 400 mA for this 400 mAh cell before charging.

2. Build the switched power path

Connect tp4056_1 OUT− to the ESP32-C3 GND pin. Connect tp4056_1 OUT+ to power_switch COM. Connect power_switch A to the ESP32-C3 5V/VIN pin. Leave power_switch B physically unconnected; the switch is on when COM connects to A.

  • Verify which slider position joins COM to A with a continuity meter before mounting it in the wristband.
  • A LiPo is 4.2 V when full, not regulated 5 V. Verify that your specific ESP32-C3 SuperMini’s VIN input will run correctly from the full 4.2 V to the protection cutoff before relying on this direct path.

3. Place both power capacitors

Place c_bulk_22uf and c_bypass_100nf in parallel at the ESP32-C3 power entry. Connect the 22 µF capacitor’s positive lead to 5V/VIN and its striped negative lead to GND. Connect either 100 nF ceramic lead to 5V/VIN and the other to GND.

  • Keep both capacitors physically close to the ESP32 power and GND pins.
  • The 22 µF capacitor is polarized — reversing it can make it fail or burst.

4. Wire the battery monitor

Connect max17048_1 VIN to ESP32 3V3, GND to ESP32 GND, SDA to GPIO6, SCL to GPIO7, and ALRT to GPIO3. Run max17048_1 BAT directly to the LiPo positive/B+ node, not to the switched VIN node.

  • The BAT wire remains connected to the cell so the gauge can measure the actual battery voltage.
  • Keep the MAX17048 VIN pin at 3.3 V; do not connect it to the LiPo or 5V/VIN node.

5. Wire the haptic driver and LRA

Connect drv2605l_1 VIN to ESP32 3V3, GND to ESP32 GND, SDA to the same GPIO6 wire, and SCL to the same GPIO7 wire. Connect drv2605l_1 OUT+ to lra_10mm LRA+ and OUT− to lra_10mm LRA−.

  • Keep the two LRA wires short and route them away from the I²C wires where possible.
  • Do not connect the LRA leads to ESP32 pins or to a power rail; they must connect only across the DRV2605L OUT+ and OUT− terminals.

6. Add the two visible I²C pull-ups

Connect one end of r_sda_pullup to the shared SDA/GPIO6 wire and its other end to ESP32 3V3. Connect one end of r_scl_pullup to the shared SCL/GPIO7 wire and its other end to ESP32 3V3.

  • These are ordinary non-polarized resistors, so either physical lead can be the 3.3 V end.
  • Do not connect either pull-up resistor to 5V/VIN — GPIO6 and GPIO7 are 3.3 V-only ESP32 signals.

7. Inspect before powering

With the switch off, inspect every power connection, then check that the LRA only reaches the DRV2605L outputs. Charge the battery through tp4056_1’s USB connector, turn the switch on, and expect one short vibration when the firmware starts.

  • A continuity check between 3V3 and GND before inserting the LiPo is a useful last check.
  • Stop immediately if any part becomes hot, the battery swells, or you smell overheating; disconnect the LiPo first.

Review all connections

1. Connections between "lipo_400mah" and "ESP32"

Functionlipo_400mahESP32
powerBAT+ → TP4056 Li-Ion/LiPo charger module with protection B+EXT
powerBAT+ → MAX17048 LiPo fuel-gauge breakout BATEXT
groundBAT- → TP4056 Li-Ion/LiPo charger module with protection B-EXT

2. Connections between "tp4056_1" and "ESP32"

Functiontp4056_1ESP32
powerOUT+ → Breadboard-friendly SPDT Slide Switch COMEXT
groundOUT-GND
powerIN+ → TP4056 module’s built-in 5 V USB charging connector (USB VBUS)EXT
groundIN- → TP4056 module’s built-in USB ground connectorEXT

3. Connections between "power_switch" and "ESP32"

Functionpower_switchESP32
powerAVIN

4. Connections between "c_bulk_22uf" and "ESP32"

Functionc_bulk_22ufESP32
power+VIN
ground-GND

5. Connections between "c_bypass_100nf" and "ESP32"

Functionc_bypass_100nfESP32
power1VIN
ground2GND

6. Connections between "max17048_1" and "ESP32"

Functionmax17048_1ESP32
powerVIN3V3
groundGNDGND
i2cSDAGPIO 6
i2cSCLGPIO 7
digitalALRTGPIO 3

7. Connections between "drv2605l_1" and "ESP32"

Functiondrv2605l_1ESP32
powerVIN3V3
groundGNDGND
dataOUT+ → 10 mm coin LRA vibration motor LRA+EXT
dataOUT- → 10 mm coin LRA vibration motor LRA-EXT
i2cSDA → MAX17048 LiPo fuel-gauge breakout SDAEXT
i2cSCL → MAX17048 LiPo fuel-gauge breakout SCLEXT

8. Connections between "r_sda_pullup" and "ESP32"

Functionr_sda_pullupESP32
power3V33V3
dataSDA → MAX17048 LiPo fuel-gauge breakout SDAEXT

9. Connections between "r_scl_pullup" and "ESP32"

Functionr_scl_pullupESP32
power3V33V3
dataSCL → MAX17048 LiPo fuel-gauge breakout SCLEXT

Deploy the firmware

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_DRV2605.h>
#include <Adafruit_MAX1704X.h>

constexpr int I2C_SDA_PIN = 6;
constexpr int I2C_SCL_PIN = 7;
constexpr int FUEL_ALERT_PIN = 3;
constexpr float LOW_BATTERY_PERCENT = 15.0F;
constexpr uint32_t BATTERY_POLL_MS = 10000UL;

Adafruit_DRV2605 haptic;
Adafruit_MAX17048 fuelGauge;

bool hapticReady = false;
bool fuelGaugeReady = false;
float lastPercent = -1.0F;
uint32_t lastBatteryPoll = 0;

void playEffect(uint8_t effect) {
  if (!hapticReady) {
    return;
  }

  haptic.setMode(DRV2605_MODE_INTTRIG);
  haptic.setWaveform(0, effect);
  haptic.setWaveform(1, 0);
  haptic.go();
  delay(80);
}

void configureLraAutoResonance() {
  // LRA mode enables the DRV2605L closed-loop resonance tracking path.
  haptic.useLRA();

  // Run the driver's built-in calibration routine once at power-up so it
  // measures this particular coin LRA before normal waveform playback.
  haptic.setMode(DRV2605_MODE_AUTOCAL);
  haptic.go();
  delay(1000);
  haptic.setMode(DRV2605_MODE_INTTRIG);
}

void readBatteryAndAlertIfNeeded() {
  if (!fuelGaugeReady) {
    return;
  }

  const float percent = fuelGauge.cellPercent();
  const bool alertLow = digitalRead(FUEL_ALERT_PIN) == LOW;

  if (alertLow || (percent >= 0.0F && percent <= LOW_BATTERY_PERCENT &&
                   (lastPercent < 0.0F || lastPercent > LOW_BATTERY_PERCENT))) {
    // Two short taps distinguish the low-battery condition from the single
    // acknowledgement pulse played at power-up.
    playEffect(47);
    delay(180);
    playEffect(47);
  }

  lastPercent = percent;
}

void setup() {
  pinMode(FUEL_ALERT_PIN, INPUT_PULLUP);
  Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);

  hapticReady = haptic.begin(&Wire);
  fuelGaugeReady = fuelGauge.begin(&Wire);

  if (hapticReady) {
    configureLraAutoResonance();
    playEffect(47);  // One medium click confirms the wristband is powered.
  }

  if (fuelGaugeReady) {
    readBatteryAndAlertIfNeeded();
    lastBatteryPoll = millis();
  }
}

void loop() {
  const uint32_t now = millis();
  if (fuelGaugeReady && now - lastBatteryPoll >= BATTERY_POLL_MS) {
    lastBatteryPoll = now;
    readBatteryAndAlertIfNeeded();
  }

  delay(10);
}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik