1
+ /* Circuit Playground Express compass. */
2
+
3
+ /* Adafruit invests time and resources providing this open source code. */
4
+ /* Please support Adafruit and open source hardware by purchasing */
5
+ /* products from Adafruit! */
6
+
7
+ /* Written by Dave Astels for Adafruit Industries */
8
+ /* Copyright (c) 2018 Adafruit Industries */
9
+ /* Licensed under the MIT license. */
10
+
11
+ /* All text above must be included in any redistribution. */
12
+
13
+ #include < Wire.h>
14
+ #include < Adafruit_NeoPixel.h>
15
+ #include < Adafruit_Sensor.h>
16
+ #include < Adafruit_LSM303_U.h>
17
+ #include < math.h>
18
+
19
+ /* Assign a unique ID to this sensor at the same time */
20
+ Adafruit_LSM303_Mag_Unified mag = Adafruit_LSM303_Mag_Unified(12345 );
21
+
22
+ float raw_mins[2 ] = {1000.0 , 1000.0 };
23
+ float raw_maxes[2 ] = {-1000.0 , -1000.0 };
24
+
25
+ float mins[2 ];
26
+ float maxes[2 ];
27
+ float corrections[2 ] = {0.0 , 0.0 };
28
+
29
+
30
+ // Support both classic and express
31
+ #ifdef __AVR__
32
+ #define NEOPIXEL_PIN 17
33
+ #else
34
+ #define NEOPIXEL_PIN 8
35
+ #endif
36
+
37
+ // When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
38
+ // Note that for older NeoPixel strips you might need to change the third parameter--see the strandtest
39
+ // example for more information on possible values.
40
+ Adafruit_NeoPixel strip = Adafruit_NeoPixel(10 , NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
41
+
42
+ // Map direction pie slices (of 30 deg each) to a neopixel, or two for the missing ones at USB & power.
43
+ 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 }};
44
+
45
+ #define BUTTON_A 4
46
+
47
+ void fill (int red, int green, int blue) {
48
+ for (int i = 0 ; i < 10 ; i++) {
49
+ strip.setPixelColor (i, red, green, blue);
50
+ }
51
+ strip.show ();
52
+ }
53
+
54
+
55
+ // Do some initial reading to let the magnetometer settle in.
56
+ // This was found by experience to be required.
57
+ // Indicated to the user by blue LEDs.
58
+ void warm_up (void )
59
+ {
60
+ sensors_event_t event;
61
+ fill (0 , 0 , 128 );
62
+ for (int ignore = 0 ; ignore < 100 ; ignore++) {
63
+ mag.getEvent (&event);
64
+ delay (10 );
65
+ }
66
+ }
67
+
68
+
69
+ // Find the range of X and Y values.
70
+ // User needs to rotate the CPX a bunch during this.
71
+ // Can be refined by doing more of the saem by pressing the A button.
72
+ // Indicated to the user by green LEDs.
73
+ void calibrate (void )
74
+ {
75
+ sensors_event_t event;
76
+ float values[2 ];
77
+
78
+ fill (0 , 128 , 0 );
79
+
80
+ unsigned long start_time = millis ();
81
+ while (millis () - start_time < 5000 ) {
82
+
83
+ mag.getEvent (&event);
84
+ values[0 ] = event.magnetic .x ;
85
+ values[1 ] = event.magnetic .y * -1 ;
86
+ if (values[0 ] != 0.0 && values[1 ] != 0.0 ) { /* ignore the random zero readings... it's bogus */
87
+ for (int i = 0 ; i < 2 ; i++) {
88
+ raw_mins[i] = values[i] < raw_mins[i] ? values[i] : raw_mins[i];
89
+ raw_maxes[i] = values[i] > raw_maxes[i] ? values[i] : raw_maxes[i];
90
+ }
91
+ }
92
+ delay (5 );
93
+ }
94
+ for (int i = 0 ; i < 2 ; i++) {
95
+ corrections[i] = (raw_maxes[i] + raw_mins[i]) / 2 ;
96
+ mins[i] = raw_mins[i] - corrections[i];
97
+ maxes[i] = raw_maxes[i] - corrections[i];
98
+ }
99
+ fill (0 , 0 , 0 );
100
+ }
101
+
102
+
103
+ void setup (void )
104
+ {
105
+ strip.begin ();
106
+ strip.show ();
107
+
108
+ pinMode (BUTTON_A, INPUT_PULLDOWN);
109
+
110
+ /* Enable auto-gain */
111
+ mag.enableAutoRange (true );
112
+
113
+ /* Initialise the sensor */
114
+ if (!mag.begin ())
115
+ {
116
+ /* There was a problem detecting the LSM303 ... check your connections */
117
+ fill (255 , 0 , 0 );
118
+ while (1 );
119
+ }
120
+
121
+ warm_up ();
122
+ calibrate ();
123
+ }
124
+
125
+
126
+ // Map a value from the input range to the output range
127
+ // Used to map MAG values from the calibrated (min/max) range to (-100, 100)
128
+ float normalize (float value, float in_min, float in_max) {
129
+ float mapped = (value - in_min) * 200 / (in_max - in_min) + -100 ;
130
+ float max_clipped = mapped < 100 ? mapped : 100 ;
131
+ float min_clipped = max_clipped > -100 ? max_clipped : -100 ;
132
+ return min_clipped;
133
+ }
134
+
135
+
136
+ void loop (void )
137
+ {
138
+ // Pressing button A does another round of calibration.
139
+ if (digitalRead (BUTTON_A)) {
140
+ calibrate ();
141
+ }
142
+
143
+ sensors_event_t event;
144
+ mag.getEvent (&event);
145
+
146
+ float x = event.magnetic .x ;
147
+ float y = event.magnetic .y * -1 ;
148
+
149
+ if (x == 0.0 && y == 0.0 ) {
150
+ return ;
151
+ }
152
+
153
+ float normalized_x = normalize (x - corrections[0 ], mins[0 ], maxes[0 ]);
154
+ float normalized_y = normalize (y - corrections[1 ], mins[1 ], maxes[1 ]);
155
+
156
+ int compass_heading = (int )(atan2 (normalized_y, normalized_x) * 180.0 / 3.14159 );
157
+ // compass_heading is between -180 and +180 since atan2 returns -pi to +pi
158
+ // this translates it to be between 0 and 360
159
+ compass_heading += 180 ;
160
+
161
+ // We add 15 to account to the zero position being 0 +/- 15 degrees.
162
+ // mod by 360 to keep it within a circle
163
+ // divide by 30 to find which pixel corresponding pie slice it's in
164
+ int direction_index = ((compass_heading + 15 ) % 360 ) / 30 ;
165
+
166
+ // light the pixel(s) for the direction the compass is pointing
167
+ // the empty spots where the USB and power connects are use the two leds to either side.
168
+ int *leds;
169
+ leds = led_patterns[direction_index];
170
+ for (int pixel = 0 ; pixel < 10 ; pixel++) {
171
+ if (pixel == leds[0 ] || pixel == leds[1 ]) {
172
+ strip.setPixelColor (pixel, 255 , 255 , 255 );
173
+ } else {
174
+ strip.setPixelColor (pixel, 0 , 0 , 0 );
175
+ }
176
+ }
177
+ strip.show ();
178
+ delay (50 );
179
+ }
0 commit comments