1+ #include < Wire.h>
2+ #include < Adafruit_NeoPixel.h>
3+ #include < Adafruit_Sensor.h>
4+ #include < Adafruit_LSM303_U.h>
5+ #include < math.h>
6+
7+ /* Assign a unique ID to this sensor at the same time */
8+ Adafruit_LSM303_Mag_Unified mag = Adafruit_LSM303_Mag_Unified(12345 );
9+
10+ float raw_mins[3 ] = {1000.0 , 1000.0 , 1000.0 };
11+ float raw_maxes[3 ] = {-1000.0 , -1000.0 , -1000.0 };
12+
13+ float mins[3 ];
14+ float maxes[3 ];
15+ float corrections[3 ] = {0.0 , 0.0 , 0.0 };
16+
17+
18+ // Support both classic and express
19+ #ifdef __AVR__
20+ #define NEOPIXEL_PIN 17
21+ #else
22+ #define NEOPIXEL_PIN 8
23+ #endif
24+
25+ // When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
26+ // Note that for older NeoPixel strips you might need to change the third parameter--see the strandtest
27+ // example for more information on possible values.
28+ Adafruit_NeoPixel strip = Adafruit_NeoPixel(10 , NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
29+
30+ int led_patterns[12 ][2 ] = {{4 , 5 }, {5 , -1 }, {6 , -1 }, {7 , -1 }, {8 , -1 }, {9 , -1 }, {9 , 0 }, {0 -1 }, {1 , -1 }, {2 , -1 }, {3 , -1 }, {4 , -1 }};
31+
32+ #define BUTTON_A 4
33+
34+ void fill (int red, int green, int blue) {
35+ for (int i = 0 ; i < 10 ; i++) {
36+ strip.setPixelColor (i, red, green, blue);
37+ }
38+ strip.show ();
39+ }
40+
41+
42+ void warm_up (void )
43+ {
44+ sensors_event_t event;
45+ fill (0 , 0 , 128 );
46+ for (int ignore = 0 ; ignore < 100 ; ignore++) {
47+ mag.getEvent (&event);
48+ delay (10 );
49+ }
50+ }
51+
52+
53+ void calibrate (void )
54+ {
55+ sensors_event_t event;
56+ float values[3 ];
57+
58+ fill (0 , 128 , 0 );
59+
60+ unsigned long start_time = millis ();
61+ while (millis () - start_time < 5000 ) {
62+
63+ mag.getEvent (&event);
64+ values[0 ] = event.magnetic .x ;
65+ values[1 ] = event.magnetic .y * -1 ;
66+ if (values[0 ] != 0.0 && values[1 ] != 0.0 ) { /* ignore the random zero readings... it's bogus */
67+ for (int i = 0 ; i < 2 ; i++) {
68+ raw_mins[i] = values[i] < raw_mins[i] ? values[i] : raw_mins[i];
69+ raw_maxes[i] = values[i] > raw_maxes[i] ? values[i] : raw_maxes[i];
70+ }
71+ }
72+ delay (5 );
73+ }
74+ for (int i = 0 ; i < 2 ; i++) {
75+ corrections[i] = (raw_maxes[i] + raw_mins[i]) / 2 ;
76+ mins[i] = raw_mins[i] - corrections[i];
77+ maxes[i] = raw_maxes[i] - corrections[i];
78+ }
79+ fill (0 , 0 , 0 );
80+ }
81+
82+
83+ void setup (void )
84+ {
85+ strip.begin ();
86+ strip.show ();
87+
88+ pinMode (BUTTON_A, INPUT_PULLDOWN);
89+
90+ /* Enable auto-gain */
91+ mag.enableAutoRange (true );
92+
93+ /* Initialise the sensor */
94+ if (!mag.begin ())
95+ {
96+ /* There was a problem detecting the LSM303 ... check your connections */
97+ fill (255 , 0 , 0 );
98+ while (1 );
99+ }
100+
101+ warm_up ();
102+ calibrate ();
103+ }
104+
105+
106+ float normalize (float value, float in_min, float in_max, float out_min, float out_max) {
107+ float mapped = (value - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
108+ float max_clipped = mapped < out_max ? mapped : out_max;
109+ float min_clipped = max_clipped > out_min ? max_clipped : out_min;
110+ return min_clipped;
111+ }
112+
113+
114+ void loop (void )
115+ {
116+ // Pressing button A does another round of calibration.
117+ if (digitalRead (BUTTON_A)) {
118+ calibrate ();
119+ }
120+
121+ sensors_event_t event;
122+ mag.getEvent (&event);
123+
124+ float x = event.magnetic .x ;
125+ float y = event.magnetic .y * -1 ;
126+ float z = event.magnetic .z * -1 ;
127+
128+ if (x == 0.0 && y == 0.0 ) {
129+ return ;
130+ }
131+
132+ float normalized_x = normalize (x - corrections[0 ], mins[0 ], maxes[0 ], -100.0 , 100.0 );
133+ float normalized_y = normalize (y - corrections[1 ], mins[1 ], maxes[1 ], -100.0 , 100.0 );
134+
135+ int compass_heading = (int )(atan2 (normalized_y, normalized_x) * 180.0 / 3.14159 );
136+ // compass_heading is between -180 and +180 since atan2 returns -pi to +pi
137+ // this translates it to be between 0 and 360
138+ compass_heading += 180 ;
139+
140+ int direction_index = ((compass_heading + 15 ) % 360 ) / 30 ;
141+
142+ // light the pixel(s) for the direction the compass is pointing
143+ // the empty spots where the USB and power connects are use the two leds to either side.
144+ int *leds;
145+ leds = led_patterns[direction_index];
146+ for (int pixel = 0 ; pixel < 10 ; pixel++) {
147+ if (pixel == leds[0 ] || pixel == leds[1 ]) {
148+ strip.setPixelColor (pixel, 255 , 255 , 255 );
149+ } else {
150+ strip.setPixelColor (pixel, 0 , 0 , 0 );
151+ }
152+ }
153+ strip.show ();
154+ delay (50 );
155+ }
0 commit comments