I'm sorry that it's been a long time since my last post - sorry about that. Let's get to good stuff!
After testing the parts and proving that they work, I felt compelled to see how hard it would be to make a compass out of them. Testing a little further, I find that the FLORA Accelerometer/Compass to be a little mixed, performance-wise. At low velocity (so that the only acceleration is due to gravity) the magnitude of the acceleration vector is right around 1000. I had initially assumed that the compass would be similarly well-calibrated but I find that the range of values would be far less regular (I think that my x-axis reports values that are always negative). Below is a bit of code I used to figure out the range of values for my magnetometer.
#include <Wire.h>
#include <Adafruit_LSM303.h>
Adafruit_LSM303 lsm;
long x;
long y;
long z;
long minX = 1000;
long maxX = 0;
long minY = 1000;
long maxY = 0;
long minZ = 1000;
long maxZ = 0;
void setup(){
Serial.begin(9600);
if (!lsm.begin()){
Serial.println("Oops ... unable to initialize the LSM303. Check your wiring!");
while (1);
}
}//end of setup
void loop(){
lsm.read();
/*X*/
x = lsm.magData.x;
Serial.print(x);
Serial.print("\t");
if(x>maxX) maxX = x;
if(x<minX) minX = x;
Serial.print(minX);
Serial.print("\t");
Serial.print(maxX);
Serial.print("\t");
/*Y*/
y = lsm.magData.y;
Serial.print(y);
Serial.print("\t");
if(y>maxY) maxY = y;
if(y<minY) minY = y;
Serial.print(minY);
Serial.print("\t");
Serial.print(maxY);
Serial.print("\t");
/*Z*/
z = lsm.magData.z;
Serial.print(z);
Serial.print("\t");
if(z>maxZ) maxZ = z;
if(z<minZ) minZ = z;
Serial.print(minZ);
Serial.print("\t");
Serial.print(maxZ);
Serial.print("\t");
Serial.println();
}
I would load that code and then orient the sensor in all different directions and then observe my minimum and maximum values in each axis. I am not sure why the compass would be so much less well-calibrated from the manufacturer than the accelerometer - could they have been damaged by the magnet shipped in the package? On a somewhat embarrassing note, my initial attempts at running the calibration code during my lunch break were thwarted by my metal desk which has a number of (small) magnets stuck to the side. These calibration values would then be used to make a much more functional compass. If you are trying this at home, please make sure to generate your own calibration data - if you try to use mine let's just say that "your mileage may vary".
#include <Wire.h>
#include <Adafruit_LSM303.h>
#include <Adafruit_NeoPixel.h>
const int leds = 16;//number of NeoPixels
const int onTIme = 8000;//time to keep the LEDs on
float bearing = 0;//for now we will point north
unsigned long upTime=0;
//Adafruit objects
Adafruit_LSM303 lsm;
Adafruit_NeoPixel ring = Adafruit_NeoPixel(leds, 6, NEO_GRB + NEO_KHZ800);
//a little vector type
typedef struct vect_s{
float x;
float y;
float z;
} vect;
void setup(){
Serial.begin(9600);
// 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);
}//end of if
ring.begin();
ring.show(); //initialize the pixels to off
}//end of setup
void loop(){
lsm.read();
if(isAwake()){
upTime = millis()+onTime;
}//end of isAwake
if(millis()<upTime){
//check level and buffer the led display
if(isLevel()){
//calculate our bearing
float o = calc();
int d = map(o-bearing,0,360,0,16);
Serial.println(d);
black();
ring.setPixelColor(d,ring.Color(0,100,0));//set the LED closest to the bearing on
}//end of if
else{
red();//all the LEDs turn red
}//end of else
}//end of if
else{
black();
}//end of else
//refresh the LED ring
ring.show();
}//end of loop
//function to wake up
boolean isAwake(){
float acc = lsm.accelData.x*lsm.accelData.x + lsm.accelData.y*lsm.accelData.y + lsm.accelData.z*lsm.accelData.z;
acc = sqrt(acc);
Serial.println(acc);
return(acc>2000);//2000 is an arbitrary, experimentally-determined number
}//end of isAwake
//function to calculate orientation
float calc(){
float orient;
vect east;//a vector that will point east
//need to calibrate the magnetometer!
//-1289 -60
//-1576 -40
//-800 160
float magX = map(lsm.magData.x,-1289,-60,-1000,1000);
float magY = map(lsm.magData.y,-1576,-40,-1000,1000);
float magZ = map(lsm.magData.z,-800,160,-1000,1000);
//cross multiplication: (gravity) x (magnetic north) = (east)
east.x = lsm.accelData.y*magZ-lsm.accelData.z*magY;
east.y = lsm.accelData.z*magX-lsm.accelData.x*magZ;
east.z = lsm.accelData.x*magY-lsm.accelData.y*magX;
//determine our orientation relative to magnetic morth, in degrees (clockwise)
orient = atan2(east.x,-east.y);
orient = orient*180/PI+180;
//print the results
Serial.print("X: "); Serial.print(east.x); Serial.print(" ");
Serial.print("Y: "); Serial.print(east.y); Serial.print(" ");
Serial.print("Z: "); Serial.print(east.z); Serial.print(" ");
Serial.print("O: "); Serial.println(orient); Serial.print(" ");
return orient;
}//end of bear
//function to determine how level the accelerometer (and my palm) is
boolean isLevel(){
float xy = sqrt((lsm.accelData.x*lsm.accelData.x)+(lsm.accelData.y*lsm.accelData.y));
float tilt = atan(xy/lsm.accelData.z);
tilt = tilt*180/PI;
boolean isLevel = (tilt<30);//check to see if the tilt is less than 30 degrees
return isLevel;
}//end of level
//function to turn all LEDs off
void black(){
for(int i=0;i<leds;i++){
ring.setPixelColor(i,ring.Color(0,0,0));
}//end of for loop
}//end of black
//function to turn all LEDs red
void red(){
for(int i=0;i<leds;i++){
ring.setPixelColor(i,ring.Color(50,0,0));
}//end of for
}//end of red
And I went ahead and added a wake-on-shake function. I am trying to code in a modular fashion so that I can recycle large amounts of it later. Oh, I picked up one of Adafruit's new NeoPixel Rings to make a nice, clean prototype display (or so I could procrastinate the sewing a little longer). At the moment, the code does not support the palm-down orientation.
[Edit: Forgot to include the video]
You can see here the compass display (green), tilt indication (red), and of course the wake-on-shake function (and going back to sleep).
The NeoPixel Ring has 16 LEDs and I think that my glove will only be able to fit about 8 sewable pixels. I think that I will have to upgrade the lighting effects substantially so that the display is smoother and so it can imply an angle between two pixels (by adjusting their intensity).
So, where are we at?
- Review and test parts - I'll verify that the electronics work and I will run the code from the Adafruit tutorials.
- LED Array - I'll take my first shot at sewing with stainless steel and build a circular, 8-NeoPixel array sized to fit in the palm of my hand
- Accelerometer - The accelerometer will be used to determine if I am holding my palm up or down AND to signal my LED array to turn on if I shake it hard enough.
- Compass - The magnetometer will be used while the LEDs are active to indicate my bearing, compensating for hand orientation
- GPS - The GPS unit will be used to help calculate initial bearings and possibly distance as well. I'll have to see how the device reacts to different orientations and fabrics.
- Physical Layout - I will have to figure out how to fit all of those goodies on the front and back of a glove (while not sacrificing wearability)
- User Experience - This is probably a good time to make the lighting pattern more interesting or informative, iron out bugs, test accuracy and a few failure modes.
- Sewing - This will be an interesting experience, I hope that local fabric stores will allow me to test the light diffusion/transmission of different fabrics I will encase the finalized electronics in. I also have never sewn gloves before but look forward to practicing. A lot. I really want to make this look like a real/well-finished product.
- Light Balancing? - The Lux sensor might help me adjust the brightness level for appropriate day/nighttime use.
- Low Power? - If I have time, I will try to see if there are ways to reduce power consumption.
- Capacitive touch buttons? - If I have time, it would be nice to be able to change destinations.
I have seen a few examples of bearing calculation and it looks like some of my fellow road testers have started dabbling with it as well. That should come pretty quickly!
Sorry for the multiple walls of code text. It may happen again.
And yes... ...you will get to see my sewing soon.