Ultimate Night Bike
I am so grateful to live in the Bay Area where there are so many cool civic events focused around bicycles. Critical Mass, East Bay Bike Party, and I'm sure dozens of others I don't know about yet.
Every time I had participated in one of these events, I was in total awe of some of the great night bikes people had built. Overtime, that awe turned into inspiration! And alas, thid Ultimate Night Bike was born!
Materials and Supplies
Materials
(1x) Bike (I used a track bike)
(10x) TriColor LED Strip Radioshack 276-339
(1x) 3/16" battery terminal connectors Radioshack 64-3132
(1x) 12V/4Ah Sealed Lead Acid Battery Radioshack 55034004
(1x) 22 Gauge stranded wire, multiple colors Radioshack 278-1224
(1x) Arduino Mega Radioshack 276-0127
(1x) M type plug Radioshack 273-344
(1x) Size M Panel-mount Coaxial DC Power Jack Radioshack 274-1563
(1x) Green LED with Holder Radioshack 276-069
(3x) 5K Ohm Potentiometers Radioshack 271-1720
(1x) 50-Ft. 16-Gauge Clear 2-Conductor Speaker Wire Radioshack 278-1267
(1x) XLR 3-Pin Jack Radioshack 2740013
(1x) XLR 3-Pin Plug Radishack 2740010
Additional Materials/Supplies
Wire Cutters/Strippers Radioshack 64-224
Rosin Core Solder Radioshack 64-009
Soldering Iron Radioshack 55027897
Razor Blade Cutting Tool (for cutting LED Strip)
Shoe Goo, Clear (Great for quickly waterproofing electronics)
(1x) Bike Basket (It was surprisingly hard to find one that fit my cruiser handlebars!)
Getting to Know My Bike.
I mounted the bike on a stand, and took some measurements. I wanted to make the lighting system for this bike, modular, and removable. The best way I could think to do this with the LED strips was by making flexible-removable housing for them to live in - that could snap in to a control system.
I examined the mounting holes and features of my bicycle, before I began adding modifications. Because I have an internally geared rear hub, I can't have a rear-wheel mounted rack. My solution to house power for this project would have to be on the inside of the electronics, or in a front mounted basket. **spoiler alert: i went with a bike basket, and it's awesome!**
I concluded that I will have to make my project out of fabric, and templated my project as follows.
But First, Give Your Bike a Kickstand!
It's a lot easier to work on a bike when it doesn't move around! I learned quickly that if I was going to keep measuring my bike and trying to attach things to it, I was going to need a way to keep it stable.
Getting Started With the FastLED Library and Uploading the Arduino Sketch
I primarily work with the FastLED.io Library for LED programming, it's pretty effortless when it comes to RGB programming, and they have great documentation for their code.
Here is an instructable detailing the installation of the FastLED Library.
This is the Code that will need to upload on to the Arduino:
// // // // // // // ultimate night bike by AUDREY LOVE // a project for Arduino, Radioshack LED Tricolor Light Strip, and a bicycle. // // // // // with great support from the FastLED.io Library // download it here: https://github.com/FastLED // // // #include "FastLED.h" #define NUM_LEDS 10 #define DATA_PIN 6 CRGB leds[NUM_LEDS]; //__________pin designations int programIndicator = 13; //Pin Number for the LED program indicator int buttonPin = 2; // the number of the pushbutton pin // Variables will change: int ledState = LOW; // ledState used to set the LED long previousMillis = 0; // will store last time LED was updated long intervalOne = 1500; // interval at which to blink (milliseconds) long intervalTwo = 500; // interval at which to blink (milliseconds) long intervalThree = 300; // interval at which to blink (milliseconds) long intervalFour = 200; // interval at which to blink (milliseconds) long intervalFive = 100; // interval at which to blink (milliseconds) long intervalSix = 50; // interval at which to blink (milliseconds) int brightValue = 0; int satValue = 0; int hueValue = 0; //button boolean buttonValue = true; // variable for reading the button status boolean buttonState = true; // variable to hold the button state //pattern int patternProgram = 0; // which button program is in use void setup() { // declare signal pins pinMode(programIndicator, OUTPUT); pinMode(buttonPin,INPUT_PULLUP); // sanity check delay - allows reprogramming if accidently blowing power w/leds delay(2000); FastLED.addLeds<TM1803, DATA_PIN, GBR>(leds, NUM_LEDS); Serial.begin(9600); // initialize serial communication at 9600 bits per second: } void loop() { // Serial.println(buttonValue); Serial.println(patternProgram); // button presses cycle through modes buttonValue = !digitalRead(buttonPin); // read input value and store it in val if (buttonValue != buttonState) { // the button state has changed! if (buttonValue == 0) { // check if the button is pressed if (patternProgram == 0) { // if set to smooth logarithmic mapping patternProgram = 1; // switch to stepped chromatic mapping } else { if (patternProgram == 1) { // patternProgram = 2; // switch to next mode } else { if (patternProgram == 2) { // patternProgram = 3; // switch to next mode } else { if (patternProgram == 3) { // patternProgram = 4; //switch to next mode } else { if (patternProgram == 4) { // patternProgram = 5; //switch to next mode } else { if (patternProgram == 5) { // patternProgram = 0; // switch to next mode } } } } } } } buttonState = buttonValue; // save the new state in our variable } brightValue = analogRead(A2); satValue = analogRead(A3); hueValue = analogRead(A4); Serial.println(hueValue); switch(patternProgram){ case 0: leds[0] = CHSV((hueValue/4)+1, (satValue/4)+1, (brightValue/4)+1); leds[1] = CHSV((hueValue/4)+1, (satValue/4)+1, (brightValue/4)+1); leds[2] = CHSV((hueValue/4)+1, (satValue/4)+1, (brightValue/4)+1); leds[3] = CHSV((hueValue/4)+1, (satValue/4)+1, (brightValue/4)+1); leds[4] = CHSV((hueValue/4)+1, (satValue/4)+1, (brightValue/4)+1); leds[5] = CHSV((hueValue/4)+1, (satValue/4)+1, (brightValue/4)+1); leds[6] = CHSV((hueValue/4)+1, (satValue/4)+1, (brightValue/4)+1); leds[7] = CHSV((hueValue/4)+1, (satValue/4)+1, (brightValue/4)+1); leds[8] = CHSV((hueValue/4)+1, (satValue/4)+1, (brightValue/4)+1); leds[9] = CHSV((hueValue/4)+1, (satValue/4)+1, (brightValue/4)+1); FastLED.show(); // delay(40); break; case 1: { unsigned long currentMillis = millis(); if(currentMillis - previousMillis > intervalTwo) { // save the last time you blinked the LED previousMillis = currentMillis; // if the LED is off turn it on and vice-versa: if (ledState == LOW) ledState = HIGH; else ledState = LOW; // set the LED with the ledState of the variable: digitalWrite(programIndicator, ledState); } } leds[0] = CRGB::Red; leds[1] = CRGB::Orange; leds[2] = CRGB::Yellow; leds[3] = CRGB::Green; leds[4] = CRGB::Aqua; leds[5] = CRGB::Blue; leds[6] = CRGB::Purple; leds[7] = CRGB::Magenta; leds[8] = CRGB::Red; leds[9] = CRGB::Orange; FastLED.show(); delay(60); leds[0] = CRGB::Magenta; leds[1] = CRGB::Red; leds[2] = CRGB::Orange; leds[3] = CRGB::Yellow; leds[4] = CRGB::Green; leds[5] = CRGB::Aqua; leds[6] = CRGB::Blue; leds[7] = CRGB::Purple; leds[8] = CRGB::Magenta; leds[9] = CRGB::Red; FastLED.show(); delay(60); leds[0] = CRGB::Purple; leds[1] = CRGB::Magenta; leds[2] = CRGB::Red; leds[3] = CRGB::Orange; leds[4] = CRGB::Yellow; leds[5] = CRGB::Green; leds[6] = CRGB::Aqua; leds[7] = CRGB::Blue; leds[8] = CRGB::Purple; leds[9] = CRGB::Magenta; FastLED.show(); delay(60); leds[0] = CRGB::Blue; leds[1] = CRGB::Purple; leds[2] = CRGB::Magenta; leds[3] = CRGB::Red; leds[4] = CRGB::Orange; leds[5] = CRGB::Yellow; leds[6] = CRGB::Green; leds[7] = CRGB::Aqua; leds[8] = CRGB::Blue; leds[9] = CRGB::Purple; FastLED.show(); delay(60); leds[0] = CRGB::Aqua; leds[1] = CRGB::Blue; leds[2] = CRGB::Purple; leds[3] = CRGB::Magenta; leds[4] = CRGB::Red; leds[5] = CRGB::Orange; leds[6] = CRGB::Yellow; leds[7] = CRGB::Green; leds[8] = CRGB::Aqua; leds[9] = CRGB::Blue; FastLED.show(); delay(60); leds[0] = CRGB::Green; leds[1] = CRGB::Aqua; leds[2] = CRGB::Blue; leds[3] = CRGB::Purple; leds[4] = CRGB::Magenta; leds[5] = CRGB::Red; leds[6] = CRGB::Orange; leds[7] = CRGB::Yellow; leds[8] = CRGB::Green; leds[9] = CRGB::Aqua; FastLED.show(); delay(60); leds[0] = CRGB::Yellow; leds[1] = CRGB::Green; leds[2] = CRGB::Aqua; leds[3] = CRGB::Blue; leds[4] = CRGB::Purple; leds[5] = CRGB::Magenta; leds[6] = CRGB::Red; leds[7] = CRGB::Orange; leds[8] = CRGB::Yellow; leds[9] = CRGB::Green; FastLED.show(); delay(60); leds[0] = CRGB::Orange; leds[1] = CRGB::Yellow; leds[2] = CRGB::Green; leds[3] = CRGB::Aqua; leds[4] = CRGB::Blue; leds[5] = CRGB::Purple; leds[6] = CRGB::Magenta; leds[7] = CRGB::Red; leds[8] = CRGB::Orange; leds[9 ] = CRGB::Yellow; FastLED.show(); delay(60); // delay(100); // wait for a second break; case 2: { unsigned long currentMillis = millis(); if(currentMillis - previousMillis > intervalThree) { // save the last time you blinked the LED previousMillis = currentMillis; // if the LED is off turn it on and vice-versa: if (ledState == LOW) ledState = HIGH; else ledState = LOW; // set the LED with the ledState of the variable: digitalWrite(programIndicator, ledState); } } Serial.println(hueValue); Serial.println(satValue); leds[0] = CHSV( ((hueValue +8)/4), ((satValue+4)/4), 200); leds[1] = CHSV( ((hueValue +4)/4), ((satValue+8)/4), 175); leds[2] = CHSV( ((hueValue +8)/4), ((satValue+4)/4), 200); leds[3] = CHSV( ((hueValue +4)/4), ((satValue+8)/4), 175); leds[4] = CHSV( ((hueValue +8)/4), ((satValue+4)/4), 200); leds[5] = CHSV( ((hueValue +4)/4), ((satValue+8)/4), 175); leds[6] = CHSV( ((hueValue +8)/4), ((satValue+4)/4), 200); leds[7] = CHSV( ((hueValue +4)/4), ((satValue+8)/4), 175); leds[8] = CHSV( ((hueValue +8)/4), ((satValue+4)/4), 200); leds[9] = CHSV( ((hueValue +4)/4), ((satValue+8)/4), 175); FastLED.show(); delay(100); leds[0] = CHSV( (hueValue +4)/4, (satValue+8)/4, 175); leds[1] = CHSV( (hueValue +8)/4, (satValue+4)/4, 200); leds[2] = CHSV( (hueValue +4)/4, (satValue+8)/4, 175); leds[3] = CHSV( (hueValue +8)/4, (satValue+4)/4, 200); leds[4] = CHSV( (hueValue +4)/4, (satValue+8)/4, 175); leds[5] = CHSV( (hueValue +8)/4, (satValue+4)/4, 200); leds[6] = CHSV( (hueValue +4)/4, (satValue+8)/4, 175); leds[7] = CHSV( (hueValue +8)/4, (satValue+4)/4, 200); leds[8] = CHSV( (hueValue +4)/4, (satValue+8)/4, 175); leds[9] = CHSV( (hueValue +8)/4, (satValue+4)/4, 200); FastLED.show(); delay(100); break; case 3: { unsigned long currentMillis = millis(); if(currentMillis - previousMillis > intervalFour) { // save the last time you blinked the LED previousMillis = currentMillis; // if the LED is off turn it on and vice-versa: if (ledState == LOW) ledState = HIGH; else ledState = LOW; // set the LED with the ledState of the variable: digitalWrite(programIndicator, ledState); } } leds[0] = CRGB::Black; leds[1] = CRGB::Black; leds[2] = CRGB::Black; leds[3] = CRGB::Black; leds[4] = CRGB::Black; leds[5] = CRGB::Black; leds[6] = CRGB::Black; leds[7] = CRGB::Black; leds[8] = CRGB::Black; leds[9] = CRGB::Black; FastLED.show(); delay(60); leds[0] = CHSV( ((hueValue +8)/4), ((satValue+4)/4), 200); leds[1] = CHSV( ((hueValue +4)/4), ((satValue+8)/4), 175); leds[2] = CHSV( ((hueValue +8)/4), ((satValue+4)/4), 200); leds[3] = CHSV( ((hueValue +4)/4), ((satValue+8)/4), 175); leds[4] = CHSV( ((hueValue +8)/4), ((satValue+4)/4), 200); leds[5] = CHSV( ((hueValue +4)/4), ((satValue+8)/4), 175); leds[6] = CHSV( ((hueValue +8)/4), ((satValue+4)/4), 200); leds[7] = CHSV( ((hueValue +4)/4), ((satValue+8)/4), 175); leds[8] = CHSV( ((hueValue +8)/4), ((satValue+4)/4), 200); leds[9] = CHSV( ((hueValue +4)/4), ((satValue+8)/4), 175); delay(100); // wait for a second break; case 4: { unsigned long currentMillis = millis(); if(currentMillis - previousMillis > intervalFive) { // save the last time you blinked the LED previousMillis = currentMillis; // if the LED is off turn it on and vice-versa: if (ledState == LOW) ledState = HIGH; else ledState = LOW; // set the LED with the ledState of the variable: digitalWrite(programIndicator, ledState); } } leds[0] = CRGB::Black; leds[1] = CRGB::Black; leds[2] = CRGB::Black; leds[3] = CRGB::Black; leds[4] = CRGB::Black; leds[5] = CRGB::Black; leds[6] = CRGB::Black; leds[7] = CRGB::Black; leds[8] = CRGB::Black; leds[9] = CRGB::Black; FastLED.show(); delay(30); leds[0] = CRGB::Gold; leds[1] = CRGB::Gold; leds[2] = CRGB::Gold; leds[3] = CRGB::Gold; leds[4] = CRGB::Gold; leds[5] = CRGB::Gold; leds[6] = CRGB::Gold; leds[7] = CRGB::Gold; leds[8] = CRGB::Gold; leds[9] = CRGB::Gold; delay(30); // wait for a second // delay(100); break; case 5: { unsigned long currentMillis = millis(); if(currentMillis - previousMillis > intervalSix) { // save the last time you blinked the LED previousMillis = currentMillis; // if the LED is off turn it on and vice-versa: if (ledState == LOW) ledState = HIGH; else ledState = LOW; // set the LED with the ledState of the variable: digitalWrite(programIndicator, ledState); } } leds[0] = CRGB::Black; leds[1] = CRGB::Black; leds[2] = CRGB::Black; leds[3] = CRGB::Black; leds[4] = CRGB::Black; leds[5] = CRGB::Black; leds[6] = CRGB::Black; leds[7] = CRGB::Black; leds[8] = CRGB::Black; leds[9] = CRGB::Black; FastLED.show(); delay(30); leds[0] = CRGB::Red; leds[1] = CRGB::Orange; leds[2] = CRGB::Yellow; leds[3] = CRGB::Green; leds[4] = CRGB::Cyan; leds[5] = CRGB::Blue; leds[6] = CRGB::Purple; leds[7] = CRGB::Violet; leds[8] = CRGB::Magenta; leds[9] = CRGB::Gold; delay(30); // delay(100); break; } }
Making the Arduino Shield: Header Pins
I like using the PCB that is larger than the Arduino. This way you have lots of room to build off more than one circuit on one board!
I put the header pins in the Arduino, and rest it upside down on the PCB to solder it into place.
Making the Arduino Shield: Solder Pots and Switch
For certain cases in the Arduino code, I wanted to control the LED light strips with 3 potentiometers - one for hue, one for saturation, and one for vibrance. These were wired to Analog Pins 2-4.
For mode selection, i.e. the case/break statements in the Code, I wired in a switch to Digital Pin 2. It is a good idea to put a low-resistance resistor on the switch coming from the digital pin.
Making the Arduino Shield: Fitting
Test fit the Shield to make sure it can come out easily, but be secure once it is inserted.
Making the Arduino Shield: 12V Power Island
Did I mention in an earlier step that big shields/PCB are awesome for constructing multiple circuits on one board? They are!
I needed to make a tiny 12V island for all of the TriColor LED Strips. 12V Island would actually power the Arduino too.
From the 12V Lead Battery, run a ground and a hot wire to the PCB - from here on, referred to as the Island. Wire the following from the Island:
- one DC Barrel Plug (Size M) that will power the Arduino.
- two XLR Panel Mount Jacks (Pin 1 = Ground, Pin 2 = Signal{Digital Pin 6}, Pin 3 = Ground)
- one DC Barrel Jack (Size M) that would receive power from the battery
Build a Control Box: Put the Pots in the Enclosure
I used a medium sized project-enclosure that I found at Radioshack, and could of gone a little bit bigger. I like to work with a slightly less tight parts tolerance, but I am still very pleased with how the whole enclosure came out.
I covered the whole thing with blue masking tape, and began marking and drilling holes for the components attached to the Arduino shield.
Build a Control Box: Switches and LED
After driling out holes, I inserted the N.O. momentary switch into the enclosure, this is the mode selector that goes to Digital Pin 2 of the Arduino, as well as an LED that would connect to digital Pin 13 on the Arduino - the mode indicator.
Build a Control Box: Power
I daisy Chained one Barrel Jack to a Barrel plug, this is to provide power the arduino, and split the connection to 12V Island.
Build a Control Box: Reset Button and Complete Schematic
Wire a small momentary N.O. switch to the Reset pin of the Arduino, and then thread it through the enclosure.
The complete circuit diagram for this project is also in this step.
Making a Sewing Pattern: Start With Paper
Patternmaking for anything can be difficult. It doesn't matter if it's for bodies or bicycles, there are some components and supplies that made this part of the build a lot easier:
- Tailors Chalk
- Pattern Paper
- Pattern Muslin
- Painters Tape
I suspended a large piece of pattern paper from my bike, and cut away the bits that didn't fit the frame I was going for.
I transfered my paper template to a piece of patternmaking muslin.
Making a Sewing Pattern: Test Fit With Fabric
After cutting out my muslin shape, I draped it over my bike frame, and did some more precise fitting. I wanted to leave space for the kickstand, and the bike chain. I was able to tape down parts of the fabric to the frame to simulate what my final fit would be.
Making a Sewing Pattern: Transfer Your Shape Back to Paper!
Oh how futile! After having a great fabric template, why would you transfer it back to paper?! Well, I needed to reproduce this shape a few more times, on felt and on DISCO fabric. It is a lot easier to pin down rigid paper to uncut fabric, than it is to try and mount flexible fabric to another piece of flexible fabric.
Sewing in Electronics: Transfer Pattern to DISCO Fabric
Scoochmaroo dropped by oodles of DISCO fabric from BetaBrand for us to play with. I sewed a bunch of scraps together to make one large sheet of DISCO material that would later become the outer shell for the LED light strip enclosure.
Sewing in Electronics: Cut the Pattern
When cutting the DISCO fabric out, I left a 1/2 inch seam allowance that would later tuck under when the whole piece is sewed up.
Sewing in Electronics: Cut Felt From Paper Pattern
I repeated the same process with the thick felt as I had with the DISCO fabric, except this time, I didn't leave any seam allowance - you want this to be as close to the paper pattern as possible.
This part is done in panels. When done marking and cutting, I had two pieces that would later be hinged over my bike.
Sewing in Electronics: Sew Felt Sheets Together
In the scrap bin of fabric, I found a nice rigid black denim. I surged it down to a rectangular shape that would become the hinge for the two felt panels. Using the heavy duty machine again, I bound the denim to the felt.
Sewing in Electronics: Size Your Light Strips
I layed out the LED Strips in a Pattern I liked, and began to trim the excess off.
Sewing in Electronics: Binding Ends of LED Strips
To join the TriColor LED strips, just stack them on top of one another and solder. The Radioshack Strips are awesome because there are tiny hole in the contact points that allow solder to flow through the layers of copper.
Seal with electrical tape or Shoe Goo.
Sewing in Electronics: Soldering to a Common Junction
This part is kind of cumbersome. I paired down all the wires from the LED strip to one common junction point. It is important shield your solder joints with heat shrink, or tape, as you go. It would be really unfortunate to expend a lot of effort on making something awesome, only to have it short out :(
Connect the three wire points to the XLR Cable Plug.
Sewing in Electronics: Stick It Down
Working with parts in silicone housing, like the TriColor LED strip, can be tricky. I used extra sticky hook-sided Velcro to bond the LED strip to the wooly felt. It worked great!
Sewing in Electronics: Testing
I cannot stress the importance of working iteratively when it comes to electronics. Test as you build! It is such a pain to re-do work, or figure out where you went wrong, if you don't test as you go.
I needed to make sure these lights definitely worked before I permanently sealed them up in a fabric sandwich with the sewing machine.
Sewing in Electronics: Seal It Up
Using a heavy duty sewing machine, I pinned down the seam allowance of the DISCO fabric to the felt, and machined the edge.
The material is flexible, stretchy, sheer, and most importantly, SHINY! The LEDs will look great shining through.
Power: Crimp Battery Terminal Connectors
Using the speaker wire, I crimped down terminal connectors to connect to a terminal block - which would connect the battery to the Arduino and lights.
Bike Basket Mod Pt. 1
The Basket I got was awesome, but not solid state, I needed to correct it's mesh-iness by giving it a base plate. I traced the outline of it's bottom and cut out a 1/2" piece of plywood that would nest at the bottom of the basket.
Bike Basket Mod Pt. 2
But how to keep it down? Fender washers! I was using short stubby screws, because the thickness of the plywood wasn't very thick at all :) I taped my drill bit so that I would only be drilling out very shallow holes, and then screwed the wood into place at the bottom of the basket.
Mounting the Components
I used these great little L Brackets to hold all my electronics in place in the basket. Nothing slips or slides around when I am in motion.
I fit everything, screwed it down, and removed the masking tape from the enclosure. Time to test!
Go Out and Be Flashy!
Ah! It works! We turned a lot of heads, and got a lot of questions about it while we were riding around the Embarcadero of San Francisco.
I'm sure I'll stand out in a crowd with this number on my bike. If you see me, and trust me, you'll be able to, be sure to say hi!