Before I began the final assembly, I did one more calibration test using the Flora. I had noticed that where the battery was in relation to the magnetometer had an effect on the heading reading. So I put all of the components in their place on the hat and re-calibrated it. I got different numbers from before and used them in the sketch. I was pleased with the results, it tracked smoother and truer to North. Now I was ready to commit to putting it all together.
With all the pieces ready for assembly, I sewed the crown to the brim. It is now a hat. I left an opening on the left side for all of the wires to pass through. There were a lot of wires and I was glad that I had carefully labeled them all. I went through and systematically soldered each component and put on heat shrink tubing before sewing it to the hat. I accidentally broke the data wire off of the cockade by handling it roughly. I had to hold back the organza ribbon and try to solder in a new lead. I ended up scorching the ribbon a bit, but it is the back side and not very noticeable.
Here is a step by step video of the final assembly. (no sound)
After all the components were soldered and sewn into place, I plugged in the battery and got a little surprise. Those fussy through hole NeoPixels in the fiber optic spray didn't turn on. They came on as the bootloader was running, but then shut off as the rest of the program began. I thought perhaps some faulty wiring on my part, but before opening everything up I plugged in the USB cable. Everything worked just fine.
I thought that perhaps it was a 5 volt vs. 3.7 volt thing. I wired up 3 AA batteries to a JST plug to test 4.5 volts, the fiber optics did nothing. I have run out of time now, but for the future I think I want to try one of Adafruit's 5 volt regulated power boosts to see if I can get better performance. For now, the brim and the cockade work nicely and I took pictures and video with and without the USB cable.
Special thanks to Meg Weedon for my hair, and John Baird for the photos and videos of me.
Here is my finalized code. Also available at https://codebender.cc/sketch:52729
Hats_off_gemma_compass_final
//Coachman's Navigational Top Hat for Element 14 / Adafruit Hats Off contest //http://www.element14.com/community/groups/wearable-technology?ICID=menubar_topics_wearabletechnology //sketch written by David Crittenden October 2014 //16 sew on NeoPixels around the brim //ring of 16 Neopixels for the cockade //2 through hole NeoPixels with fiber optic bundles in feather detail //Gemma and Flora Accelerometer (LSM303) //finalized calibration data derived using Flora and sketch from https://learn.adafruit.com/flora-accelerometer?view=all //some code from https://learn.adafruit.com/steam-punk-goggles?view=all //idea for pointer pixel from http://www.element14.com/community/groups/arduino/blog/2013/08/27/get-closer-challenge--navigation-glove //idea for library modification from http://www.slickstreamer.info/2014/02/led-bracelet-accelerometer-3dprinting.html #include <TinyWireM.h> #include "TinyLSM303_mag.h" #include <Adafruit_NeoPixel.h> #define pin 1 #define numPixels 34 Adafruit_NeoPixel ring = Adafruit_NeoPixel(numPixels, pin, NEO_GRB + NEO_KHZ800); TinyLSM303_mag lsm; // Pi for calculations const float Pi = 3.14159; void setup() { ring.begin(); ring.show();// Initialize all pixels to 'off' lsm.begin();// Initialize the sensors } void loop() { // Read the magnetometer and determine the compass heading: lsm.read(); int16_t x = map(lsm.magData.x, -140, 315, -1000, 1000);//use calibration data here int16_t y = map(lsm.magData.y, -450, -50, -1000, 1000);//use calibration data here // Calculate the angle of the vector y,x from magnetic North float heading = (atan2(y, x) * 180) / Pi; // Normalize to 0-360 for a compass heading if (heading < 0) { heading = 360 + heading; } uint8_t d = map(heading, 0, 360, 0, 16);//map the heading to the size of the ring around the brim //set all the brim pixels to a color for(uint8_t i = 0; i < 16; i++) { ring.setPixelColor(i, ring.Color(10, 0, 140)); } //set the closest pixel in the brim to the heading ring.setPixelColor(d, ring.Color(255, 255, 0)); //set all the cockade and feather pixels to a color for(uint8_t i = 16; i < 34; i++) { uint8_t c = d * (i-16);//color variable tied to the heading ring.setPixelColor(i, ring.Color(255 - c, c, 0)); //using variables already in play to save space } ring.show(); }
I also wrote two other sketches for my hat. The idea being once you arrived safely at your destination, just hop onto codebender and load up your party sketch.
https://codebender.cc/sketch:57029
Hats_off_gemma_larson
//Coachman's Navigational Top Hat: Color larson sketch //use a matrix of colors accesed in order //can scale to strips of variable sizes //Written by David Crittenden #include <Adafruit_NeoPixel.h> #define N_Pixels 34 //number of pixels in strand #define LED_PIN 1 //NeoPixel strand is connected to this pin on the Flora Adafruit_NeoPixel strip = Adafruit_NeoPixel(N_Pixels, LED_PIN, NEO_GRB + NEO_KHZ800); //Place the colors you want into this matrix //use braces {}, not parenthesis () uint8_t color[][3] = { {0, 0, 255}, {58, 0, 234}, {147, 0, 188}, {153, 0, 104}, {96, 0, 33}, {79, 0, 5}, {173, 0, 52}, {209, 0, 118}, {212, 0, 232}, {105, 0, 252}, {0, 0, 242}, {0, 113, 234}, {0, 183, 162}, {0, 160, 53}, {36, 130, 0}, {0, 191, 28}, {0, 224, 172}, {0, 181, 237}, {0, 104, 249}, }; #define colorlist sizeof(color) / 3//defines the number of objects in the color matrix void setup() { strip.begin(); strip.show(); } void loop() { int x; //position of main dot int tracer; //position of following dots int count; //counts down the color list with each iteration int wait = 5; //amount to pause //count down the colors in the color list showing each one in turn for (count = 0; count < colorlist; count++) { int r = color[count][0]; int g = color[count][1]; int b = color[count][2]; //Calculate and show the position of the main dot for (x = 0; x < (N_Pixels * 2) - 2; x++) { strip.setPixelColor(x, strip.Color(r, g, b)); strip.show(); delay(wait); //Calculate the position and intensity of the tracer dots //based on the size of the strip int tracer = x - 1; for (int fader = N_Pixels - 3; fader >= 0; fader--) { int redtrace = (r / N_Pixels) * fader; int greentrace = (g / N_Pixels) * fader; int bluetrace = (b / N_Pixels) * fader; strip.setPixelColor(tracer, strip.Color(redtrace, greentrace, bluetrace)); strip.show(); //advance the position of the tracer dot tracer--; } } //Choose the next color from the list //Comment out this section to keep the same color on the wipe back count = count + 1; r = color[count][0]; g = color[count][1]; b = color[count][2]; //reverse the main dot direction for (x = N_Pixels; x >= (-N_Pixels) + 1; x--) { strip.setPixelColor(x, strip.Color(r, g, b)); strip.show(); delay(wait); //Calculate the position and intensity of the tracer dots //based on the size of the strip int tracer = x + 1; for (int fader = N_Pixels - 3; fader >= 0; fader--) { int redtrace = (r / N_Pixels) * fader; int greentrace = (g / N_Pixels) * fader; int bluetrace = (b / N_Pixels) * fader; strip.setPixelColor(tracer, strip.Color(redtrace, greentrace, bluetrace)); strip.show(); //advance the position of the tracer dot tracer++; } } } }
https://codebender.cc/sketch:57240
Hats_off_gemma_wave_random
//Coachman's Navigational Top Hat: random color wave //written by David Crittenden //chooses random color //wave reverses direction and changes color each time //scalable to any size strip #include <Adafruit_NeoPixel.h> #define N_PIXELS 34 //number of pixels in strand #define LED_PIN 1 //NeoPixel strand is connected to this pin int ra = random(255); int ga = random(255); int ba = random(255); int rb= random(255); int gb = random(255); int bb = random(255); int wait = 30; int longWait = 300; Adafruit_NeoPixel strip = Adafruit_NeoPixel(N_PIXELS, LED_PIN, NEO_GRB + NEO_KHZ800); void setup() { strip.begin(); strip.show(); strip.setBrightness(255); randomSeed(analogRead(0)); } void loop() { //wave from first color to next color for (int p = 0; p < N_PIXELS + 10; p++) { strip.setPixelColor(p, strip.Color(ra/3 * 2, ga/3 * 2, ba/3 * 2));//first pixel at 2/3 strip.setPixelColor(p - 1, strip.Color(ra/3, ga/3, ba/3));//following pixel at 1/3 strip.setPixelColor(p - 2, strip.Color(0, 0, 0));//black out a pixel strip.setPixelColor(p - 3, strip.Color(0, 0, 0));//black out a pixel strip.setPixelColor(p - 4, strip.Color(rb/3, gb/3, bb/3));//following pixel at 1/3 strip.setPixelColor(p - 5, strip.Color(rb/3 * 2, gb/3 * 2, bb/3 * 2));//following pixel at 2/3 strip.setPixelColor(p - 6, strip.Color(rb, gb, bb));//final pixel full strip.setPixelColor(p - 7, strip.Color(rb/3 * 2, gb/3 * 2, bb/3 * 2));//aftershock ripple strip.setPixelColor(p - 8, strip.Color(rb/3 , gb/3 , bb/3 )); strip.setPixelColor(p - 9, strip.Color(rb/3 * 2, gb/3 * 2, bb/3 * 2)); strip.setPixelColor(p - 10, strip.Color(rb, gb, bb)); strip.show(); delay(wait); } delay(longWait); //choose new color a ra = random(255); ga = random(255); ba = random(255); for (int p = N_PIXELS - 1; p > -10; p--) { strip.setPixelColor(p, strip.Color(rb/3 * 2, gb/3 * 2, bb/3 * 2));//first pixel at 2/3 strip.setPixelColor(p + 1, strip.Color(rb/3 , gb/3 , bb/3));//following pixel at 1/3 strip.setPixelColor(p + 2, strip.Color(0, 0, 0));//black out a pixel strip.setPixelColor(p + 3, strip.Color(0, 0, 0));//black out a pixel strip.setPixelColor(p + 4, strip.Color(ra/3, ga/3, ba/3));//following pixel at 1/3 strip.setPixelColor(p + 5, strip.Color(ra/3 * 2, ga/3 * 2, ba/3 * 2));//following pixel at 2/3 fuchsia strip.setPixelColor(p + 6, strip.Color(ra, ga, ba));//final pixel full fuchsia strip.setPixelColor(p + 7, strip.Color(ra/3 * 2, ga/3 * 2, ba/3 * 2));//aftershock ripple in fuchsia strip.setPixelColor(p + 8, strip.Color(ra/3, ga/3, ba/3)); strip.setPixelColor(p + 9, strip.Color(ra/3 * 2, ga/3 * 2, ba/3 * 2)); strip.setPixelColor(p + 10, strip.Color(ra, ga, ba)); strip.show(); delay(wait); } delay(longWait); //choose new color b rb = random(255); gb = random(255); bb = random(255); }
Thank you to Adafruit and Element 14 for hosting this contest!