In This Guide
We are bridging the gap between basic safety gear and IoT technology. Here is what we are building today:
- Understanding the MPU-6050 Accelerometer
- Bill of Materials & Wiring Schematics
- Coding the Deceleration Logic
- 3D Printing and Weatherproofing the Enclosure
Standard bike tail lights are passive; they blink regardless of what the rider is doing. In modern traffic, you need to communicate your intentions clearly. By integrating a simple gyroscope and accelerometer into your lighting rig, we can build a light that flares bright red the moment you hit the brakes.
For the hobby bicyclist who loves to tinker, this project offers the perfect balance of utility and coding challenge. We will be using an Arduino microcontroller to read G-force data and drive a Neopixel LED ring, creating a "smart" safety device for under $20.
The Hardware Stack
To keep this unit compact enough to mount on a seat post, we need a small footprint. We are skipping the Raspberry Pi for this build—it's overkill and too power-hungry. Instead, we are using the Arduino Pro Micro (or Nano) coupled with the MPU-6050.
- Arduino Pro Micro (5V)
- MPU-6050 (3-Axis Accelerometer/Gyro)
- WS2812B LED Ring (12 or 16 bits)
- LiPo Battery (3.7V, 500mAh) + TP4056 Charger
- Slide Switch & 3D Printed Case
Why the MPU-6050? This sensor communicates via I2C and is incredibly sensitive. It can detect the subtle pitch change of your bike climbing a hill versus the sharp G-force spike of squeezing the caliper brakes.
Wiring the Logic
The wiring is straightforward I2C communication. Since we are dealing with a moving vehicle, soldering connections is mandatory. Breadboards will not survive the vibrations of the road.
| MPU-6050 Pin |
Arduino Pin |
Description |
| VCC |
5V / VCC |
Power input |
| GND |
GND |
Common Ground |
| SCL |
A5 (or Pin 3 on Pro Micro) |
I2C Clock |
| SDA |
A4 (or Pin 2 on Pro Micro) |
I2C Data |
Power Management Warning
If you are using a standard 5V Arduino, ensure your LiPo battery runs through a 5V boost converter or plug it into the `RAW` pin if the voltage regulator can handle the drop. Undervoltage can cause the accelerometer to freeze up mid-ride.
The Braking Algorithm
The code logic is where the magic happens. We don't want the light to trigger just because you hit a pothole. We need to filter the data to isolate longitudinal deceleration.
Step 1: Calibration
In your `setup()` loop, take 100 readings while the bike is stationary. Average these to find your "zero" point. This accounts for the angle at which you mounted the device on your seat post.
Step 2: Smoothing Data
Raw accelerometer data is noisy. Implementation of a Running Average or a Kalman Filter is essential. For this application, a simple weighted average works well:
currentAccel = (currentAccel * 0.9) + (newReading * 0.1);
Step 3: The Trigger
When the smoothed acceleration value drops below a specific negative threshold (indicating rapid slowing), switch the LED state from "Blink/Cruising" to "Solid Red/High Brightness."
Enclosure & Mounting
Electronics and rain do not mix. For the housing, PLA is acceptable for prototyping, but PETG or ABS is recommended for outdoor durability and UV resistance.
Design your case with a tight tolerance for the lens. Use a clear acrylic circle or print a thin layer of translucent filament to diffuse the harshness of the Neopixels.
Waterproofing Tip
Don't rely solely on the 3D print for waterproofing. Once your electronics are tested and working, coat the PCB (avoiding the barometer hole on the sensor if it has one) with Conformal Coating or clear nail polish to prevent corrosion.
The Source Code
Below is the complete sketch for the brake light. Before uploading, ensure you have installed the Adafruit MPU6050 and Adafruit NeoPixel libraries via the Arduino Library Manager.
#include <Adafruit_NeoPixel.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
// --- CONFIGURATION ---
#define LED_PIN 6 // Pin connected to Neopixel Ring
#define LED_COUNT 12 // Number of LEDs on your ring
#define BRIGHT_DIM 30 // Brightness for cruising (0-255)
#define BRIGHT_MAX 255 // Brightness for braking (0-255)
// Sensitivity Threshold (Negative value for deceleration)
// Adjust this based on road tests. -2.0 is usually a good start.
#define BRAKE_THRESHOLD -2.0
// Hardware Objects
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_MPU6050 mpu;
// Global Variables for Smoothing
float calibratedZero = 0;
float currentAccel = 0;
float alpha = 0.9; // Smoothing factor (Higher = smoother but more lag)
void setup() {
Serial.begin(115200);
// 1. Initialize LEDs
strip.begin();
strip.show();
strip.setBrightness(BRIGHT_MAX);
// 2. Initialize Sensor
if (!mpu.begin()) {
Serial.println("Failed to find MPU6050 chip");
while (1) {
// Flash Red to indicate error
colorWipe(strip.Color(255, 0, 0), 50);
delay(500);
colorWipe(strip.Color(0, 0, 0), 50);
delay(500);
}
}
// 3. Calibration Sequence
// Bike must be still during this phase!
Serial.println("Calibrating... Keep bike still.");
float total = 0;
for(int i=0; i<100; i++) {
sensors_event_t a, g, temp;
mpu.getEvent(&a, &g, &temp);
// Assuming Y-axis is pointing forward/backward
total += a.acceleration.y;
delay(10);
}
calibratedZero = total / 100.0;
// Flash Green to indicate ready
colorWipe(strip.Color(0, 255, 0), 20);
delay(500);
}
void loop() {
sensors_event_t a, g, temp;
mpu.getEvent(&a, &g, &temp);
// Get raw Y-axis acceleration (longitudinal)
float rawAccel = a.acceleration.y;
// Subtract the calibration offset
float correctedAccel = rawAccel - calibratedZero;
// Apply Low Pass Filter (Smoothing)
currentAccel = (currentAccel * alpha) + (correctedAccel * (1.0 - alpha));
// --- LOGIC CHECK ---
// If deceleration is stronger than threshold
if (currentAccel < BRAKE_THRESHOLD) {
// BRAKING: All Red, Full Brightness
setAllColor(255, 0, 0);
} else {
// CRUISING: Red, Dimmed (Tail light mode)
// Optional: Add a "Knight Rider" scanner effect here for visibility
setAllColor(BRIGHT_DIM, 0, 0);
}
delay(50); // Loop rate ~20Hz
}
// --- HELPER FUNCTIONS ---
// Set all LEDs to a specific RGB color
void setAllColor(int r, int g, int b) {
for(int i=0; i
Ready to share your build?
Join the Great Meets community and join The Maker Team group for the 3D printer files for this project. Post your own variations and get feedback from other Maker cyclists!