This is a complex project with many components! Here are some recommended prerequisite guides:
This is a complex project with many components! Here are some recommended prerequisite guides:
Tools & Supplies
- two strands of 12mm flat weather-resistant pixels
- Flora main board
- Flora accelerometer
- small weather-resistant enclosure
- 3xAA battery pack (not pictured)
- Tactile on/off switch with leads
-
Momentary simple RF receiver - 315MHz
- Keyfob RF remote control - 315MHz
- 4-pin JST SM Plug + Receptacle Cable Set (not pictured)
-
JST 2-pin cable (not pictured)
- four 4.7K ohm resistors
- Sugru
- Backpack - we're using the Black Diamond Bullet
- Zipties
- Gaffers tape
- Velcro tape
You will need a good quality basic multimeter that can measure voltage and continuity.
Click here to buy a basic multimeter.
Click here to buy a top of the line multimeter.
Click here to buy a pocket multimeter.
Don't forget to learn how to use your multimeter too!
Any entry level 'all-in-one' soldering iron that you might find at your local hardware store should work. As with most things in life, you get what you pay for.
Upgrading to a higher end soldering iron setup, like the Hakko FX-888 that we stock in our store, will make soldering fun and easy.
Do not use a "ColdHeat" soldering iron! They are not suitable for delicate electronics work and can damage the Flora (see here).
Click here to buy our entry level adjustable 30W 110V soldering iron.
Click here to upgrade to a Genuine Hakko FX-888 adjustable temperature soldering iron.
Learn how to solder with tons of tutorials!
You will want rosin core, 60/40 solder. Good solder is a good thing. Bad solder leads to bridging and cold solder joints which can be tough to find.
Click here to buy a spool of leaded solder (recommended for beginners).
Click here to buy a spool of lead-free solder.
Click here to buy a helping third hand tool.
Click here to buy some.
Circuit Diagram
The Flora accelerometer connects to Flora's 3.3v, SDA, SCL, and GND pins, all in a row, and will detect when the rider is braking.
The pixel strand connects to VBATT (red wire), GND (blue wire), D9 (green) and D10 (yellow).
VBATT also connects to +5V on the RF module. GND -> GND and each signal pin is connected to a Flora digital input via a voltage divider comprised of two 4.7K ohm resistors to reduce the signal voltage to ~3V.
The remote buttons trigger the RF modules signal pins to go HIGH. Two buttons are shown for use as turn signals, but you can connect up the remaining buttons in the same way.
Control Circuit
To wire up the battery power supply for the circuit, tin the long leads of your on/off switch with solder.
You may also wish to extend the switch leads even longer with pieces of wire.
Also solder the JST cable's black wire to the battery pack's black wire.
Attach the Flora accelerometer according to the circuit diagram. Notice that all four pins necessary for this i2c sensor are in a row, making wiring very convenient!
Affix the Flora board and accelerometer to the battery pack with Velcro tape. Likewise between the battery pack and plastic enclosure.
These simple wireless receiver/remote pairs are great for adding a few wireless inputs to your project, but they operate at 5V, which doesn't match Flora's 3V i/o. So build a simple voltage divider with two 4.7K ohm resistors!
To attach to Flora, twist the resistor leads together and solder to a digital input pin. Repeat with more pairs of resistors for each button on your remote or as many as you'd like to use.
One resistor in each pair will be soldered to ground, and the other will connect to the RF module's output.
Female jumper wires can come in handy for attaching to the RF module's header pins. The pin marked +5V gets connected to VBATT on Flora, and GND goes go GND.
Plug an extender onto the input side of a strand of weather-resistant LED pixels.
Run the connector cable through a small hole bored in the plastic weather-resistant enclosure.
These tactile on/off switches are perfect for toggling power on our backpack, but the switch isn't inherently waterproof. Using a bit of plastic to prevent the button from being clogged, wrap the switch in Sugru and let it cure for 24 hours. Wear gloves to prevent fingerprint marks.
LED Pixels
The Code
Overall, though, this was a lot of fun to code, and it is a project that I will continue to tinker with until I am happy with the results. Here is the code that I came up with, and how it works.
You'll need to download and install the Adafruit_LSM303 library - we have a tutorial on installing arduino libraries here
Select Download Project Zip to get the Flora_Brakelight_Backpack.ino file.
// SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries // // SPDX-License-Identifier: MIT #include <Wire.h> #include <Adafruit_LSM303.h> #include <SPI.h> #include <Adafruit_WS2801.h> Adafruit_LSM303 lsm; #define BRAKETHRESHOLD 350 #define BRAKETIMETHRESHOLD 200 int dataPin = 2; // Yellow wire on Adafruit Pixels int clockPin = 3; // Green wire on Adafruit Pixels const int cPin = 11; const int dPin = 6; // Set the first variable to the NUMBER of pixels. 32 = 32 pixels in a row // The LED strips are 32 LEDs per meter but you can extend/cut the strip Adafruit_WS2801 strip = Adafruit_WS2801(36,dataPin,clockPin); int start = 0; int prevX = 0; int currentX = 0; int cState = 0; int dState = 0; long brakeTime = 0; void setup() { Serial.begin(9600); // Start up the LED strip strip.begin(); // Update the strip, to start they are all 'off' strip.show(); // Try to initialise and warn if we couldn't detect the chip if (!lsm.begin()) { Serial.println("Oops ... unable to initialize the LSM303. Check your wiring!"); while (1); } pinMode(cPin, INPUT); pinMode(dPin, INPUT); } void loop() { check_switches(); // when we check the switches we'll get the current state lsm.read(); currentX = abs(lsm.accelData.x); if (start == 0){ prevX = currentX; start = 1; } int i = currentX - prevX; if (abs(i) > BRAKETHRESHOLD) { brakeTime = millis(); int strikes = 0; while ((abs(i) > BRAKETHRESHOLD) && (strikes < 3)) { if (abs(i) < BRAKETHRESHOLD) { strikes = strikes + 1; } lsm.read(); currentX = abs(lsm.accelData.x); i = currentX - prevX; if ((millis() - brakeTime) > BRAKETIMETHRESHOLD) { brakeLights(Color(255,0,0),250); while (abs(i) > BRAKETHRESHOLD) { lsm.read(); currentX = abs(lsm.accelData.x); i = currentX - prevX; Serial.println(i); delay(100); } hideAll(); brakeTime = millis(); i = 0; lsm.read(); currentX = abs(lsm.accelData.x); } } } prevX = currentX; delay(200); } void check_switches() { cState = digitalRead(cPin); dState = digitalRead(dPin); if (cState == HIGH) { // left blinker Serial.println("left blink on"); hideAll(); leftTurn(Color(255,63,0),250); delay(300); Serial.println("left blink off"); hideAll(); delay(300); } if (dState == HIGH) { // right blinker Serial.println("right blink on"); hideAll(); rightTurn(Color(255,63,0),250); delay(300); Serial.println("right blink off"); hideAll(); delay(300); } } void leftTurn(uint32_t c,uint8_t wait){ innerLeftBottom(c); innerLeftTop(c); strip.show(); delay(wait); hideAll(); outerLeftTop(c); outerLeftBottom(c); strip.show(); delay(wait); hideAll(); } void rightTurn(uint32_t c,uint8_t wait){ innerRightBottom(c); innerRightTop(c); strip.show(); delay(wait); hideAll(); outerRightTop(c); outerRightBottom(c); strip.show(); delay(wait); hideAll(); } void brakeLights(uint32_t c, uint8_t wait){ innerRightBottom(c); innerRightTop(c); innerLeftBottom(c); innerLeftTop(c); strip.show(); delay(wait); hideAll(); outerLeftTop(c); outerLeftBottom(c); outerRightTop(c); outerRightBottom(c); strip.show(); delay(wait); hideAll(); } /* Helper functions */ //Input a value 0 to 384 to get a color value. //The colours are a transition r - g - b - back to r void outerRightBottom(uint32_t c){ for (int i=0; i < 5; i++) { strip.setPixelColor(i, c); } } void outerRightTop(uint32_t c){ for (int i=5; i < 10; i++) { strip.setPixelColor(i, c); } } void innerRightTop(uint32_t c){ for (int i=10; i < 14; i++) { strip.setPixelColor(i, c); } } void innerRightBottom(uint32_t c){ for (int i=14; i < 18; i++) { strip.setPixelColor(i, c); } } void innerLeftBottom(uint32_t c){ for (int i=18; i < 22; i++) { strip.setPixelColor(i, c); strip.show(); } } void innerLeftTop(uint32_t c){ for (int i=22; i < 26; i++) { strip.setPixelColor(i, c); } } void outerLeftTop(uint32_t c){ for (int i=26; i < 31; i++) { strip.setPixelColor(i, c); } } void outerLeftBottom(uint32_t c){ for (int i=31; i < 36; i++) { strip.setPixelColor(i, c); } } void hideAll(){ for(int i = 0; i > strip.numPixels();i++){ strip.setPixelColor(i,Color(0,0,0)); } strip.show(); } // Create a 24 bit color value from R,G,B uint32_t Color(byte r, byte g, byte b) { uint32_t c; c = r; c <<= 8; c |= g; c <<= 8; c |= b; return c; }
#include <Wire.h> #include <Adafruit_LSM303.h> #include <SPI.h> #include <Adafruit_WS2801.h> Adafruit_LSM303 lsm; #define BRAKETHRESHOLD 350 #define BRAKETIMETHRESHOLD 200
The most important part of the code is the LSM303 library, the WS2801 library, and the BRAKETHRESHOLD
, and BRAKETIMETHRESHOLD
values. You will need to install the LSM303 library, which can be found here, and the WS2801 library, which can be found here.
The BRAKETHRESHOLD
value will need to be tweaked to your liking. This value looks at the amount of force the accelerometer measures before it considers it worth investigating further. Similarly, the BRAKETIMETHRESHOLD
value is the amount of time that the force exists before it will trigger the brake lights. This is an important part of the code, as it allows us to ignore things like big bumps in the road.
From there, the code is quite simple. If you press and hold the C button on the remote, the left blinker will activate on the backpack. If you press and hold the D button on the remote, the right blinker will activate on the backpack. Release either button to stop blinking. You could easily modify the sketch to press the button to turn on, and press again to turn off....or simply use the toggle type RF receiver which you can buy here.
Wear it!
This guide was first published on Feb 13, 2013. It was last updated on Nov 15, 2023.