Introduction: Light Cycle: Programmable RGB LED Strips on Bike Frame

I have already been working on similar projects, this one really is the next step of an earlier project. See:
https://www.instructables.com/id/Inbedded-Bike-Rim-Lights/

And before we get started, we should have a demonstration of the finished effect (the video is when the bike wasn't yet covered).


I have seen many different attempts to “pimp the ride” by decorating a bicycle with LEDs. Some are better than others. I hope mine falls in on the right side of the scale.

Important considerations:
-Programmable: this is based on an Arduino and the WS2118 Chipset. There are an endless number of possibilities that can be implemented.
-Water RESISTANT: The LEDs are covered providing a certain level of weather protection.
-Discrete: The same covering hides a lot of what is going on and from a distance, the bike looks to be unmodified.
-Expandable: Adding sensors (eg: accelerometers, Bluetooth, hall effect sensor) allow to expand the capabilities.

Step 1: Costs

Before we begin I would like to break down some of the principal costs.

The donor bike was a $30 road bike I bought for the wheelset for my girlfriends bike.

The LED strip came from Alibaba which offered a 4m strip for around $40

The Arduino clone was $11 from China on Ebay.

Covering material $17 from eBay

The saddle bag also came from eBay for $2.50

Double sided tape was around $5

All up around $120 bucks or so.

Step 2: Planning the Strip Layout

I didn't want to lay the strip out in one continuous run because most LED strips recommend having the power connected at 1 meter intervals. I suppose that is suggested to try to limit the amount of current running through the strip at any one point. It would also be impossible to run the lines in one continuous strip due to the form of a bicycle.

Step 3: Installation

The LED strip was attached to a unfurled roll of double sided tape. This was then applied to the frame taking care to align the strip in the centre of each of the tubes. for the forks, the strip was cut several times to allow the strip to conform to the bend of the forks. The cuts in the joins were then repaired by soldering short wires between the terminals of the cut sections.

Thin enamel coated wire was used to continue the run from the left fork to the right. This should be acceptable as current should be diminished at the end of the run. The enamel wire is certainly sufficient to carry the signal line and was used to connect L1 to L2 and L2 to L3.

Step 4: Electronics

For the arduino I removed the pin headers as I will never be using it in a breadboard. I then soldered on a 4 x AAA battery pack and a line from pin 2 out to the data in on the LED strip. I then covered the whole thing in heat-shrink to protect it from shorting.

Step 5: Covering

To hid the LED strips I wanted to wrap the whole frame in plastic and use a heat gun to shrink it down. I did some samples with different sheet plastics but couldn't find a suitable material. After some thought I decided to try a thin heat shrink film that is used to cover model airplanes. The material comes in many colours. It is applied with a warm iron to heat an adhesive and tack down the edge of the material. After, it is heated with a heat gun to shrink the film tight. A lot of care must be taken here because the melting point is very close to the shrinking temperature.

Several times the material overheated and tore a hole, particularly on the sharp squared edges of the LEDs.

If you cover in this manner you should take care to make sure the edge is depressed before heating with the heat gun. The material shrinks back on itself if it is not carefully tacked down. This creates an unattractive edge.

Step 6: Programming

This project uses the fast spi library available at: http://code.google.com/p/fastspi/

A video demonstrating the full range of capabilities is shown here:

I have done nothing special with the programming. My code includes a bunch of variables which define the ends of the strips to correct for them being chopped up and out of order. This is the code I used for the demo on the introduction page. There is plenty of room for improvement.

#include <FastSPI_LED.h>

#define NUM_LEDS 178
struct CRGB {
  unsigned char b;
  unsigned char r;
  unsigned char g;
}; // Sometimes chipsets wire in a backwards sort of way
struct CRGB *leds; // struct CRGB { unsigned char r; unsigned char g; unsigned char b; };

#define PIN 3

#define SSS 1
#define SSE 25

#define CSS 26
#define CSE 46

#define STS 47
#define STE 73

#define BTS 74
#define BTE 107

#define TTS 108
#define TTE 137

#define LFS 138
#define LFE 157

#define RFS 158
#define RFE 177

#define ON 500
#define OFF 100

void lightBar(int s, int e, int del, int off,  int r, int g, int b);

void bottomTube();
void topTube();
void seatTube();
void chainStay();
void seatStay();
void leftFork();
void rightFork();
void clearDisplay();

int currentBar=0;
int lastBar=0;
int nextBar=0;
int choice =0;
int clock=0; //anticlockwise

void setup()
{
  FastSPI_LED.setLeds(NUM_LEDS);
  FastSPI_LED.setChipset(CFastSPI_LED::SPI_TM1809);
  FastSPI_LED.setPin(PIN);
  FastSPI_LED.init();
  FastSPI_LED.start();
  leds = (struct CRGB*)FastSPI_LED.getRGBData();
}

void loop() {

  if(currentBar ==0 && lastBar ==0)
  {
    lastBar=4;
    currentBar=5;
    clock=0; //anticlockwise
  }

  switch(currentBar){
  case 1: //main case start
    if(clock==0){
      nextBar=2;
    }
    break;

  case 2: //main case start
    if(clock==0){
      choice = random(2);
      if(choice==0)
        nextBar=3;
      else
        nextBar=4;
    }
    else{
        nextBar=1; 
    }
    break;

  case 3: //main case start
    if(lastBar==2||lastBar==4){
      choice = random(2);
      if(choice==0)
        nextBar=1;
      else
        nextBar=5;
    }
    else{
      choice = random(2);
      if(choice==0)
        nextBar=2;
      else
        nextBar=4; 
    }
    break;

  case 4: //main case start
    if(clock==0){
      choice = random(3);
      if(choice==0)
        nextBar=5;
      else if(choice==2)
        nextBar=6;
      else
        nextBar=7;
    }
    else{
      choice = random(2);
      if(choice==0)
        nextBar=3;
      else
        nextBar=2; 
    }
    break;

  case 5: //main case start
    if(clock==0){
      choice = random(2);
      if(choice==0)
        nextBar=1;
      else
        nextBar=3;
    }
    else{
      choice = random(3);
      if(choice==0)
        nextBar=4;
      else if(choice==2)
        nextBar=6;    
      else
        nextBar=7; 
    }
    break;

  case 6: //main case start
    if(clock==0){
      nextBar=7;
    }
    else{
      choice = random(3);
      if(choice==0)
        nextBar=4;
      else if(choice==2)
        nextBar=5;    
      else
        nextBar=7; 
    }
    break; 

  case 7: //main case start
    if(clock==0){
      choice = random(3);
      if(choice==0)
        nextBar=4;
      else if(choice==2)
        nextBar=5;    
      else
        nextBar=6; 
    }
    else{
      nextBar=6;
    }
    break;
  }

  switch(currentBar){ //light currentBar
  case 1: lightBar(SSS,SSE,ON,OFF,50,50,50); break;
  case 2: lightBar(CSS,CSE,ON,OFF,50,50,50); break;
  case 3: lightBar(STS,STE,ON,OFF,50,50,50); break;
  case 4: lightBar(BTS,BTE,ON,OFF,50,50,50); break;
  case 5: lightBar(TTS,TTE,ON,OFF,50,50,50); break;  
  case 6: lightBar(RFS,RFE,ON,OFF,50,50,50); break;
  case 7: lightBar(LFS,LFE,ON,OFF,50,50,50); break;
  }
  lastBar=currentBar;
  currentBar=nextBar;

  /*
    switch(lastBar){

   case 2: //Subcase start
   choice = random(2);
   if(choice==0){
   nextBar=3;
   }
   else
   nextBar=5;
   break; //Subcase end

   case 3: //Subcase start
   nextBar=2;
   break; //Subcase end 

   case 5: //Subcase start
   nextBar=2;
   break; //Subcase end 
   }
   break; //main case end

   case 5: //main case start
   switch(lastBar){

   case 1: //Subcase start
   choice = random(3);
   if(choice==0){
   nextBar=4;
   }
   else if(choice==1){
   nextBar=6;
   }
   else
   nextBar=7;
   break; //Subcase end


   case 3: //Subcase start
   choice = random(3);
   if(choice==0){
   nextBar=4;
   }
   else if(choice==1){
   nextBar=6;
   }
   else
   nextBar=7;
   break; //Subcase end

   case 4: //Subcase start
   choice = random(2);
   if(choice==0){
   nextBar=1;
   }
   else
   nextBar=3;
   break; //Subcase end

   case 6: //Subcase start
   choice = random(2);
   if(choice==0){
   nextBar=1;
   }
   else
   nextBar=3;
   break; //Subcase end

   case 7: //Subcase start
   choice = random(2);
   if(choice==0){
   nextBar=1;
   }
   else
   nextBar=3;
   break; //Subcase end

void lightBar(int s, int e, int del, int off, int r, int g, int b){
  for(int i = s ; i <= e; i++ ) {
    leds[i].r = r;
    leds[i].g =g;
    leds[i].b = b;
  }
  FastSPI_LED.show();

  delay(del);
  clearDisplay();
  delay(off);
}

void bottomTube(){
  {
    for(int i = 74 ; i <= 108; i++ ) {
      leds[i].r = 50;
      leds[i].g =50;
      leds[i].b = 50;
    }
    FastSPI_LED.show();
  }
}
void topTube(){
  {
    for(int i = 108 ; i <= 140; i++ ) {
      leds[i].r = 50;
      leds[i].g =50;
      leds[i].b = 50;
    }
    FastSPI_LED.show();
  }
}

void seatTube(){
  for(int i = 48 ; i <= 74; i++ )
  {
    leds[i].r = 50;
    leds[i].g =50;
    leds[i].b = 50;
  }
  FastSPI_LED.show();
}


void chainStay(){
  {
    for(int i = 22; i <= 47; i++ ) {
      leds[i].r = 50;
      leds[i].g =50;
      leds[i].b = 50;
    }
    FastSPI_LED.show();
  }
}

void seatStay(){
  {
    for(int i = 1 ; i <= 22; i++ ) {
      leds[i].r = 50;
      leds[i].g =50;
      leds[i].b = 50;
    }
    FastSPI_LED.show();
  }
}

void leftFork(){
  {
    for(int i = 159 ; i <= 178; i++ ) {
      leds[i].r = 50;
      leds[i].g =50;
      leds[i].b = 50;
    }
    FastSPI_LED.show();
  }
}

void rightFork(){
  {
    for(int i = 140 ; i <= 159; i++ ) {
      leds[i].r = 50;
      leds[i].g =50;
      leds[i].b = 50;
    }
    FastSPI_LED.show();
  }
}

void clearDisplay(){
  {
    for(int i = 0 ; i <= 177; i++ ) {
      leds[i].r = 0;
      leds[i].g =0;
      leds[i].b = 0;
    }
    FastSPI_LED.show();
  }
}

Non-essential information about the code
The program I have here is just running a full bar at one time and changing to an adjacent bar in the direction of travel. For example the first bar could be the seat stay, the second may then be either the top tube, seat tube or chain stay as a direction has not yet been established.Say the top tube was the second bar. A direction of travel has been established and the third bar may be either the bottom tube or one of the forks. The illuminated bar then continues forward never doubling on itself.

Step 7: Conclusions & Ideas for Further Expansion

The final product could be improved by placing some kind of strip over the LEDs that diffuses the light and creates a more continuous as opposed to discrete effect.

I really should have taken more care with the application of the covering material It looks good from a distance but up close there are plenty of imperfections.

To expand the project further I would like to add an accelerometer that automatically adjust the scheme based on accelerations. Imagine the seat stay turning red while braking briskly.

Another video in the dark. Stay tuned for more programmes and a video in public.

http://hackingismakingisengineering.blogspot.com.au/
http://www.youtube.com/user/catmatix





Bike Contest

Participated in the
Bike Contest