BikeLight
Spring 2022
ME 3204 Mechatronics
Problem
- Biking at night can be dangerous since it is difficult for pedestrians and motorists to see when bicyclists are signaling for turns.
- As someone who lived off-campus and had to regularly bike home at night after long days on campus, this was a problem I faced regularly.
Solution
- The solution I developed was the BikeLight: an automated LED signaling system for bikers that includes a hand-mounted turn signal component and a brake light component on the rear of the bike.
- For the hand-mounted component, an accelerometer senses the position of the bicyclist’s hand and the BikeLight displays an arrow in the direction the bike is going to turn based on traditional turning hand signals.

- For the rear bike-mounted component, the rear brake light blinks when the bicycle is slowing down and emits light continuously when the bike is fully stopped.
Video
System Components, Integration, and Construction
Hand-Mounted Turn Signal
- The sensor for the hand-mounted component was an ADXL335 3-axis accelerometer mounted to the back of the hand.
- This accelerometer reads hand position based on 3 analog values sent to the microcontroller in the form of x, y, and z acceleration values.
- Based on these 3 signals, thresholds were set that correspond to various hand positions.
- As seen below in Figure 1, for each hand position, one accelerometer signal was on one side of the threshold while the other two signals were on the opposite side.
Figure 1. Serial plot of accelerometer signals with threshold values and hand signals visualized.
- Once the exact hand position was determined with the thresholds and accelerometer signals, the LED matrix and ring outputs were then animated.
- An Adafruit NeoPixel NeoMatrix and Ring were used, both of which are compatible with the Adafruit_NeoPixel.h Arduino library.
- The Arduino called a function that corresponded to the hand position case (leftArrow() or rightArrow()), and the LEDs were animated in the correct arrangement, seen below in Figure 2.
- The LED matrix provided signals for drivers and pedestrians located behind the bike, while the ring light rested on the biker’s palm and signaled the biker’s intentions to oncoming traffic.
Figure 2. LED matrix and ring in the left and right turn hand positions, visible to both rear and oncoming traffic.
- A wrist-mounted case was modeled using SOLIDWORKS and 3D printed with Velcro straps securing it to the arm.
- A battery pack that held three 1.5V AA batteries supplied 4.5V to the LED panels and Arduino Nano microcontroller.
- The proper connections were soldered together and any exposed wirings were covered with heat shrink wire casing to create the final product, seen below in Figure 3.
- The entire system can be turned on and off using the battery pack switch and does not hinder the biker’s grip on the handlebars.
Figure 3. Final hand-mounted turn signal component including 3D-printed case
Rear Brake Light
- Four neodymium magnets were taped onto the rear wheel spokes equidistant from each other.
- A Hall effect sensor was attached to the bike frame so that as the wheel spins, the sensor is able to detect the four magnets.
- Each time a magnet is detected by the Hall sensor, an ISR is triggered in digital pin 2 of the Arduino such that a counter is incremented every time a magnet is detected. Over a sampling period of 1 second, a wheel speed is calculated by dividing the number of counts by the sampling time.
- Each speed is compared to the previous wheel speed to determine if the bike is slowing down.
- If the bike is slowing down, the ring LED will trigger a built-in animation to indicate deceleration.
- Once the bike completely stops, the ring LED will display a solid red light.
- Like the hand-mounted turn signal, the rear brake light is also controlled by an Arduino Nano and powered by 3 AA batteries that supply 4.5 V to the Nano and NeoPixel ring LED.
- The ring LED is mounted to the case and the Hall effect sensor is wired from the Nano to the bike frame, seen below in Figure 4.
Figure 4. Rear brake system with four neodymium magnets taped on the rear wheel spokes and a Hall sensor wired to the Arduino Nano.
CAD
Hand-Mounted Turn Signal Case

Rear Brake Signal Case

Circuit Schematics
Hand-Mounted Turn Signal

Rear Brake Signal

Materials
Hand-Mounted Turn Signal
| Component | Quantity |
|---|---|
| ADXL335 triple-axis accelerometer | 1 |
| Adafruit NeoPixel NeoMatrix 8x8 - 64 RGB LED Pixel Matrix | 1 |
| Adafruit NeoPixel Ring - 12 x 5050 RGB LED | 1 |
| 1”x12” Velcro Reusable Cinch Straps | 2 |
| 3x 1.5V AA Battery Holder with Leads and Switch | 1 |
| Arduino Nano | 1 |
| Cycling Gloves | 1 |
| 3D printed wrist mounting case | 1 |
| Wire, heat shrink tubing, solder | - |
Rear Brake Signal
| Component | Quantity |
|---|---|
| KY003 Magnetic Hall Effect Sensor | 1 |
| Adafruit NeoPixel Ring - 12 x 5050 RGB LED | 1 |
| 3x 1.5V AA Battery Holder with Leads and Switch | 1 |
| Arduino Nano | 1 |
| 3D printed wrist mounting case | 1 |
| Wire, heat shrink tubing, solder | - |
Code
Hand-Mounted Component
#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>
// ACCELEROMETER STUFF
// Define accelerometer x,y,z pins
const int xpin = A0;
const int ypin = A1;
const int zpin = A2;
// Define Thresholds
#define THRESHOLD_UP 375 // threshold for default hand position
#define THRESHOLD_LEFT 325 // threshold for left turn hand position
#define THRESHOLD_RIGHT 325 // threshold for right turn hand position
// Define Matrix and Ring Pins
#define MATRIXPIN 6
#define RINGPIN 5
// Declare LED matrix
// coordinate system starts on top left
// row major progressive system
// GRB coloring + correct frequency for board
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(8, 8, MATRIXPIN,
NEO_MATRIX_TOP + NEO_MATRIX_LEFT +
NEO_MATRIX_ROWS + NEO_MATRIX_PROGRESSIVE,
NEO_GRB + NEO_KHZ800);
// Declare LED ring
Adafruit_NeoPixel strip = Adafruit_NeoPixel(12, RINGPIN, NEO_GRB + NEO_KHZ800);
// Initialize basic RGB color values
uint32_t red = strip.Color(255,0,0);
uint32_t green = strip.Color(0,255,0);
uint32_t blue = strip.Color(0,0,255);
/***SETUP***/
void setup() {
// Initialize serial communications
Serial.begin(9600);
// Start NeoMatrix
matrix.begin();
matrix.setBrightness(100);
matrix.show(); // Initialize all pixels to 'off'
// Start NeoPixel ring
strip.begin();
strip.setBrightness(100);
strip.show(); // Initialize all pixels to 'off'
} // end setup
/***LOOP***/
void loop() {
// Read accelerometer values
int xval = analogRead(xpin);
int yval = analogRead(ypin);
int zval = analogRead(zpin);
// Determine hand position
if (zval>THRESHOLD_UP & xval<THRESHOLD_UP & yval<THRESHOLD_UP){
Serial.println("HAND DOWN");
matrix.clear();
matrix.show();
strip.clear();
strip.show();
} // end if
else if (zval>THRESHOLD_LEFT & xval<THRESHOLD_LEFT & yval>THRESHOLD_LEFT){
Serial.println("LEFT TURN");
leftArrow();
} // end else if
else if (zval>THRESHOLD_RIGHT & xval>THRESHOLD_RIGHT & yval<THRESHOLD_RIGHT){
Serial.println("RIGHT TURN");
rightArrow();
} // end else if
else {
matrix.clear();
matrix.show();
strip.clear();
strip.show();
} // end else
/*
// Debug Accelerometer/Print the sensor values:
Serial.print(analogRead(xpin));
Serial.print("\t");
Serial.print(analogRead(ypin));
Serial.print("\t");
Serial.print(analogRead(zpin));
Serial.println();
delay(100);
*/
/*
// Debug Matrix/rightArrow and leftArrow functions:
rightArrow();
delay(2000);
matrix.clear();
matrix.show();
delay(1000);
leftArrow();
delay(2000);
matrix.clear();
matrix.show();
delay(1000);
*/
} // end loop
// Function to display left turn arrow
void leftArrow(){
// Matrix
matrix.clear();
for (int i=23; i<=47; i=i+8){
matrix.setPixelColor(i,strip.Color(0,50,255));
} // end for
matrix.show();
delay(25);
for (int i=22; i<=46; i=i+8){
matrix.setPixelColor(i,strip.Color(0,100,255));
} // end for
matrix.show();
delay(25);
for (int i=21; i<=45; i=i+8){
matrix.setPixelColor(i,strip.Color(0,150,255));
} // end for
matrix.show();
delay(25);
for (int i=20; i<=45; i=i+8){
matrix.setPixelColor(i,strip.Color(0,255,255));
} // end for
matrix.show();
delay(25);
for (int i=3; i<=59; i=i+8){
matrix.setPixelColor(i,strip.Color(0,255,150));
} // end for
matrix.show();
delay(25);
for (int i=10; i<=50; i=i+8){
matrix.setPixelColor(i,strip.Color(0,255,100));
} // end for
matrix.show();
delay(25);
for (int i=17; i<=41; i=i+8){
matrix.setPixelColor(i,strip.Color(0,255,50));
} // end for
matrix.show();
delay(25);
for (int i=24; i<=32; i=i+8){
matrix.setPixelColor(i,strip.Color(0,255,25));
} // end for
matrix.show();
delay(75);
// Ring
strip.setPixelColor(3,blue);
strip.setPixelColor(9,blue);
strip.show();
delay(35);
strip.setPixelColor(2,blue);
strip.setPixelColor(10,blue);
strip.show();
delay(35);
strip.setPixelColor(1,blue);
strip.setPixelColor(11,blue);
strip.show();
delay(35);
strip.setPixelColor(0,blue);
strip.show();
delay(125);
strip.clear();
strip.show();
} // end leftArrow
// Function to display right turn arrow
void rightArrow(){
// Matrix
matrix.clear();
matrix.fill(strip.Color(0,50,255),58,4);
matrix.show();
delay(25);
matrix.fill(strip.Color(0,100,255),50,4);
matrix.show();
delay(25);
matrix.fill(strip.Color(0,150,255),42,4);
matrix.show();
delay(25);
matrix.fill(strip.Color(0,255,255),34,4);
matrix.show();
delay(25);
matrix.fill(strip.Color(0,255,150),24,8);
matrix.show();
delay(25);
matrix.fill(strip.Color(0,255,100),17,6);
matrix.show();
delay(25);
matrix.fill(strip.Color(0,255,50),10,4);
matrix.show();
delay(25);
matrix.fill(strip.Color(0,255,25),3,2);
matrix.show();
delay(75);
// Ring
strip.setPixelColor(0,blue);
strip.setPixelColor(6,blue);
strip.show();
delay(35);
strip.setPixelColor(11,blue);
strip.setPixelColor(7,blue);
strip.show();
delay(35);
strip.setPixelColor(10,blue);
strip.setPixelColor(8,blue);
strip.show();
delay(35);
strip.setPixelColor(9,blue);
strip.show();
delay(125);
strip.clear();
strip.show();
} // end rightArrow
Brake Component
#include <Adafruit_NeoPixel.h>
// Define Ring Pin
#define RINGPIN 6
// Declare LED ring
// 12 LEDs on ring
// Input pin number
// GRB coloring + correct frequency for board
Adafruit_NeoPixel strip = Adafruit_NeoPixel(12, RINGPIN, NEO_GRB + NEO_KHZ800);
// Define 'red' color in RGB values
uint64_t red = strip.Color(255,0,0);
// Initialize speed counter variables
int rev_count = 0;
const float sample_period = 1; // sampling period in sec
long timer1 = millis();
float rps;
float oldrps;
/***SETUP***/
void setup(){
// Initialize serial communications
Serial.begin(115200);
// Start NeoPixel ring
// Initialize all pixels to 'off'
strip.begin();
strip.setBrightness(100);
strip.show();
// Magnet interrupt stuff
// Initialize the interrupt pin (Arduino digital pin 2)
attachInterrupt(0, magnet_detect, RISING);
rev_count = 0;
rps = 0;
oldrps=rps;
} // end setup
/***LOOP***/
void loop(){
// Clear ring
strip.clear();
strip.show();
// If enough time has passed (enough samples to take rev/sec)
if (millis() - timer1 > (1000 * sample_period)) {
rps=(1000*rev_count)/(millis()-timer1); // find rev/sec
rev_count = 0; // reset rev_count for next cycle
timer1 = millis(); // start time for next cycle
Serial.println(rps); // print rev/sec for debugging
// See if the wheel is slowing down
if (rps<oldrps){
//Serial.println("Slowing down");
theaterChase(red,50);
} // end slow down if
} // end sampling if
// If the bike isn't moving
if (rps==0){
strip.fill(red,0,64); // Fill with solid red
strip.show();
} // end if
// Update rpm value
oldrps=rps;
} // end loop
// Called whenever a magnet is detected
void magnet_detect(){
rev_count++;
} // end magnet_detect()
// Theatre-style crawling lights (from NeoPixel example code)
void theaterChase(uint32_t c, uint8_t wait) {
for (int j=0; j<7; j++) { //do 10 cycles of chasing
for (int q=0; q < 3; q++) {
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, c); //turn every third pixel on
} // end for
strip.show();
delay(wait);
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, 0); //turn every third pixel off
} // end for
} // end for
} // end for
} // end theaterChase