Skip to content

Commit 4017559

Browse files
authored
Merge pull request #318 from dastels/master
C++ code for CPX compass guide
2 parents 36b294c + 05f2a73 commit 4017559

File tree

1 file changed

+179
-0
lines changed

1 file changed

+179
-0
lines changed

CPX_Compass/CPX_Compass.ino

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
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

Comments
 (0)