Skip to content

Commit 1d08680

Browse files
authored
Merge pull request #22 from mkrawcz1/master
Added Wake On Motion threshold setting function
2 parents 6f07109 + fe5c711 commit 1d08680

File tree

5 files changed

+433
-0
lines changed

5 files changed

+433
-0
lines changed
Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
1+
/****************************************************************
2+
* Example4_WakeOnMotion.ino
3+
* ICM 20948 Arduino Library Demo
4+
* Based on Example3_Interrupts.ino by Owen Lyke @ SparkFun Electronics
5+
* Original Creation Date: Dec 5 2020
6+
* Created by mkrawcz1 (***** ***)
7+
*
8+
* For this example you must connect the interrupt pin "INT" on the breakout
9+
* board to the pin specified by "INT_PIN" on your microcontroller.
10+
*
11+
* This code is freeware;
12+
*
13+
* Distributed as-is; no warranty is given.
14+
***************************************************************/
15+
#include "ICM_20948.h" // Click here to get the library: http://librarymanager/All#SparkFun_ICM_20948_IMU
16+
17+
//#define USE_SPI // Uncomment this to use SPI
18+
19+
#define SERIAL_PORT Serial
20+
21+
#define INT_PIN 2 // Make sure to connect this pin on your uC to the "INT" pin on the ICM-20948 breakout
22+
//#define LED_PIN 13
23+
#define LED_PIN LED_BUILTIN
24+
#define BUFFER_SAMPLE_NUM 32
25+
26+
#define SPI_PORT SPI // Your desired SPI port. Used only when "USE_SPI" is defined
27+
#define SPI_FREQ 5000000// You can override the default SPI frequency
28+
#define CS_PIN 2 // Which pin you connect CS to. Used only when "USE_SPI" is defined
29+
30+
#define WIRE_PORT Wire // Your desired Wire port. Used when "USE_SPI" is not defined
31+
#define AD0_VAL 1 // The value of the last bit of the I2C address.
32+
// On the SparkFun 9DoF IMU breakout the default is 1, and when
33+
// the ADR jumper is closed the value becomes 0
34+
35+
#ifdef USE_SPI
36+
ICM_20948_SPI myICM; // If using SPI create an ICM_20948_SPI object
37+
#else
38+
ICM_20948_I2C myICM; // Otherwise create an ICM_20948_I2C object
39+
#endif
40+
41+
// Some vars to control or respond to interrupts
42+
volatile bool isrFired = false;
43+
volatile bool sensorSleep = false;
44+
volatile bool canToggle = false;
45+
unsigned int WOM_threshold=255;
46+
double lastTrigerred;
47+
48+
void setup() {
49+
50+
pinMode(INT_PIN, INPUT_PULLUP); // Using a pullup b/c ICM-20948 Breakout board has an onboard pullup as well and we don't want them to compete
51+
attachInterrupt(digitalPinToInterrupt(INT_PIN), icmISR, FALLING); // Set up a falling interrupt
52+
53+
pinMode(LED_PIN, OUTPUT);
54+
digitalWrite(LED_PIN, LOW);
55+
56+
SERIAL_PORT.begin(115200);
57+
while(!SERIAL_PORT){};
58+
59+
#ifdef USE_SPI
60+
SPI_PORT.begin();
61+
#else
62+
WIRE_PORT.begin();
63+
WIRE_PORT.setClock(400000);
64+
#endif
65+
66+
bool initialized = false;
67+
while( !initialized ){
68+
69+
#ifdef USE_SPI
70+
myICM.begin( CS_PIN, SPI_PORT, SPI_FREQ ); // Here we are using the user-defined SPI_FREQ as the clock speed of the SPI bus
71+
#else
72+
myICM.begin( WIRE_PORT, AD0_VAL );
73+
#endif
74+
75+
SERIAL_PORT.print( F("Initialization of the sensor returned: ") );
76+
SERIAL_PORT.println( myICM.statusString() );
77+
if( myICM.status != ICM_20948_Stat_Ok ){
78+
SERIAL_PORT.println( "Trying again..." );
79+
delay(500);
80+
}else{
81+
initialized = true;
82+
}
83+
}
84+
85+
// In this advanced example we'll cover how to do WakeOn Motion action using interrupts
86+
SERIAL_PORT.println("Device connected!");
87+
88+
// Here we are doing a SW reset to make sure the device starts in a known state
89+
myICM.swReset( );
90+
if( myICM.status != ICM_20948_Stat_Ok){
91+
SERIAL_PORT.print(F("Software Reset returned: "));
92+
SERIAL_PORT.println(myICM.statusString());
93+
}
94+
delay(250);
95+
96+
// Now wake the sensor up
97+
myICM.sleep( sensorSleep );
98+
myICM.lowPower( false );
99+
100+
// The next few configuration functions accept a bit-mask of sensors for which the settings should be applied.
101+
102+
// Set Gyro and Accelerometer to a particular sample mode
103+
// options: ICM_20948_Sample_Mode_Continuous
104+
// ICM_20948_Sample_Mode_Cycled
105+
myICM.setSampleMode( (ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), ICM_20948_Sample_Mode_Cycled );
106+
SERIAL_PORT.print(F("setSampleMode returned: "));
107+
SERIAL_PORT.println(myICM.statusString());
108+
109+
110+
ICM_20948_smplrt_t mySmplrt;
111+
mySmplrt.g = 54;
112+
myICM.setSampleRate( ICM_20948_Internal_Gyr, mySmplrt );
113+
SERIAL_PORT.print(F("setSampleRate returned: "));
114+
SERIAL_PORT.println(myICM.statusString());
115+
116+
// Set full scale ranges for both acc and gyr
117+
ICM_20948_fss_t myFSS; // This uses a "Full Scale Settings" structure that can contain values for all configurable sensors
118+
119+
myFSS.a = gpm2; // (ICM_20948_ACCEL_CONFIG_FS_SEL_e)
120+
// gpm2
121+
// gpm4
122+
// gpm8
123+
// gpm16
124+
125+
myFSS.g = dps250; // (ICM_20948_GYRO_CONFIG_1_FS_SEL_e)
126+
// dps250
127+
// dps500
128+
// dps1000
129+
// dps2000
130+
131+
myICM.setFullScale( (ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), myFSS );
132+
if( myICM.status != ICM_20948_Stat_Ok){
133+
SERIAL_PORT.print(F("setFullScale returned: "));
134+
SERIAL_PORT.println(myICM.statusString());
135+
}
136+
137+
138+
// Set up Digital Low-Pass Filter configuration
139+
ICM_20948_dlpcfg_t myDLPcfg; // Similar to FSS, this uses a configuration structure for the desired sensors
140+
myDLPcfg.a = acc_d473bw_n499bw; // (ICM_20948_ACCEL_CONFIG_DLPCFG_e)
141+
// acc_d246bw_n265bw - means 3db bandwidth is 246 hz and nyquist bandwidth is 265 hz
142+
// acc_d111bw4_n136bw
143+
// acc_d50bw4_n68bw8
144+
// acc_d23bw9_n34bw4
145+
// acc_d11bw5_n17bw
146+
// acc_d5bw7_n8bw3 - means 3 db bandwidth is 5.7 hz and nyquist bandwidth is 8.3 hz
147+
// acc_d473bw_n499bw
148+
149+
myDLPcfg.g = gyr_d361bw4_n376bw5; // (ICM_20948_GYRO_CONFIG_1_DLPCFG_e)
150+
// gyr_d196bw6_n229bw8
151+
// gyr_d151bw8_n187bw6
152+
// gyr_d119bw5_n154bw3
153+
// gyr_d51bw2_n73bw3
154+
// gyr_d23bw9_n35bw9
155+
// gyr_d11bw6_n17bw8
156+
// gyr_d5bw7_n8bw9
157+
// gyr_d361bw4_n376bw5
158+
159+
myICM.setDLPFcfg( (ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), myDLPcfg );
160+
if( myICM.status != ICM_20948_Stat_Ok){
161+
SERIAL_PORT.print(F("setDLPcfg returned: "));
162+
SERIAL_PORT.println(myICM.statusString());
163+
}
164+
165+
// Choose whether or not to use DLPF
166+
// Here we're also showing another way to access the status values, and that it is OK to supply individual sensor masks to these functions
167+
ICM_20948_Status_e accDLPEnableStat = myICM.enableDLPF( ICM_20948_Internal_Acc, true );
168+
ICM_20948_Status_e gyrDLPEnableStat = myICM.enableDLPF( ICM_20948_Internal_Gyr, true );
169+
SERIAL_PORT.print(F("Enable DLPF for Accelerometer returned: ")); SERIAL_PORT.println(myICM.statusString(accDLPEnableStat));
170+
SERIAL_PORT.print(F("Enable DLPF for Gyroscope returned: ")); SERIAL_PORT.println(myICM.statusString(gyrDLPEnableStat));
171+
172+
// Now we're going to set up interrupts. There are a lot of options, but for this test we're just configuring the interrupt pin and enabling interrupts to tell us when new data is ready
173+
/*
174+
ICM_20948_Status_e cfgIntActiveLow ( bool active_low );
175+
ICM_20948_Status_e cfgIntOpenDrain ( bool open_drain );
176+
ICM_20948_Status_e cfgIntLatch ( bool latching ); // If not latching then the interrupt is a 50 us pulse
177+
178+
ICM_20948_Status_e cfgIntAnyReadToClear ( bool enabled ); // If enabled, *ANY* read will clear the INT_STATUS register. So if you have multiple interrupt sources enabled be sure to read INT_STATUS first
179+
180+
ICM_20948_Status_e cfgFsyncActiveLow ( bool active_low );
181+
ICM_20948_Status_e cfgFsyncIntMode ( bool interrupt_mode ); // Can ue FSYNC as an interrupt input that sets the I2C Master Status register's PASS_THROUGH bit
182+
183+
ICM_20948_Status_e intEnableI2C ( bool enable );
184+
ICM_20948_Status_e intEnableDMP ( bool enable );
185+
ICM_20948_Status_e intEnablePLL ( bool enable );
186+
ICM_20948_Status_e intEnableWOM ( bool enable );
187+
ICM_20948_Status_e intEnableWOF ( bool enable );
188+
ICM_20948_Status_e intEnableRawDataReady ( bool enable );
189+
ICM_20948_Status_e intEnableOverflowFIFO ( uint8_t bm_enable );
190+
ICM_20948_Status_e intEnableWatermarkFIFO ( uint8_t bm_enable );
191+
*/
192+
myICM.cfgIntActiveLow(true); // Active low to be compatible with the breakout board's pullup resistor
193+
myICM.cfgIntOpenDrain(false); // Push-pull, though open-drain would also work thanks to the pull-up resistors on the breakout
194+
myICM.cfgIntLatch(true); // Latch the interrupt until cleared
195+
SERIAL_PORT.print(F("cfgIntLatch returned: "));
196+
SERIAL_PORT.println(myICM.statusString());
197+
198+
myICM.WOMThreshold(WOM_threshold); // set WoM threshold
199+
SERIAL_PORT.print(F("Set threshold returned: "));
200+
SERIAL_PORT.println(myICM.statusString());
201+
202+
myICM.intEnableWOM(true); // enable interrupts on WakeOnMotion
203+
SERIAL_PORT.print(F("intEnableWOM returned: "));
204+
SERIAL_PORT.println(myICM.statusString());
205+
206+
myICM.WOMThreshold(WOM_threshold); // set WoM threshold - just in case...
207+
SERIAL_PORT.print(F("Set threshold returned: "));
208+
SERIAL_PORT.println(myICM.statusString());
209+
210+
SERIAL_PORT.println();
211+
SERIAL_PORT.println(F("Configuration complete!"));
212+
}
213+
214+
void loop() {
215+
if( isrFired ){ // If our isr flag is set then clear the interrupts on the ICM
216+
isrFired = false;
217+
myICM.getAGMT(); // get the A, G, M, and T readings
218+
// printScaledAGMT( myICM.agmt); // This function takes into account the sclae settings from when the measurement was made to calculate the values with units
219+
SERIAL_PORT.println(F("Shock detected"));
220+
digitalWrite(LED_PIN, HIGH);
221+
lastTrigerred=millis();
222+
delay(30);
223+
myICM.clearInterrupts(); // This would be efficient... but not compatible with Uno
224+
}
225+
226+
myICM.clearInterrupts(); // clear interrupts for next time -
227+
// usually you'd do this only if an interrupt has occurred, however
228+
// on the 328p I2C usage can block interrupts. This means that sometimes
229+
// an interrupt is missed. When missed, if using an edge-based interrupt
230+
// and only clearing interrupts when one was detected there will be no more
231+
// edges to respond to, so no more interrupts will be detected. Here are
232+
// some possible solutions:
233+
// 1. use a level based interrupt
234+
// 2. use the pulse-based interrupt in ICM settings (set cfgIntLatch to false)
235+
// 3. use a microcontroller with nestable interrupts
236+
// 4. clear the interrupts often
237+
if(millis()-lastTrigerred>1000)
238+
digitalWrite(LED_PIN, LOW);;
239+
}
240+
241+
void icmISR( void ){
242+
isrFired = true; // Can't use I2C within ISR on 328p, so just set a flag to know that data is available
243+
}
244+
245+
246+
// Below here are some helper functions to print the data nicely!
247+
void printPaddedInt16b( int16_t val ){
248+
if(val > 0){
249+
SERIAL_PORT.print(" ");
250+
if(val < 10000){ SERIAL_PORT.print("0"); }
251+
if(val < 1000 ){ SERIAL_PORT.print("0"); }
252+
if(val < 100 ){ SERIAL_PORT.print("0"); }
253+
if(val < 10 ){ SERIAL_PORT.print("0"); }
254+
}else{
255+
SERIAL_PORT.print("-");
256+
if(abs(val) < 10000){ SERIAL_PORT.print("0"); }
257+
if(abs(val) < 1000 ){ SERIAL_PORT.print("0"); }
258+
if(abs(val) < 100 ){ SERIAL_PORT.print("0"); }
259+
if(abs(val) < 10 ){ SERIAL_PORT.print("0"); }
260+
}
261+
SERIAL_PORT.print(abs(val));
262+
}
263+
264+
void printRawAGMT( ICM_20948_AGMT_t agmt){
265+
SERIAL_PORT.print("RAW. Acc [ ");
266+
printPaddedInt16b( agmt.acc.axes.x );
267+
SERIAL_PORT.print(", ");
268+
printPaddedInt16b( agmt.acc.axes.y );
269+
SERIAL_PORT.print(", ");
270+
printPaddedInt16b( agmt.acc.axes.z );
271+
SERIAL_PORT.print(" ], Gyr [ ");
272+
printPaddedInt16b( agmt.gyr.axes.x );
273+
SERIAL_PORT.print(", ");
274+
printPaddedInt16b( agmt.gyr.axes.y );
275+
SERIAL_PORT.print(", ");
276+
printPaddedInt16b( agmt.gyr.axes.z );
277+
SERIAL_PORT.print(" ], Mag [ ");
278+
printPaddedInt16b( agmt.mag.axes.x );
279+
SERIAL_PORT.print(", ");
280+
printPaddedInt16b( agmt.mag.axes.y );
281+
SERIAL_PORT.print(", ");
282+
printPaddedInt16b( agmt.mag.axes.z );
283+
SERIAL_PORT.print(" ], Tmp [ ");
284+
printPaddedInt16b( agmt.tmp.val );
285+
SERIAL_PORT.print(" ]");
286+
SERIAL_PORT.println();
287+
}
288+
289+
290+
void printFormattedFloat(float val, uint8_t leading, uint8_t decimals){
291+
float aval = abs(val);
292+
if(val < 0){
293+
SERIAL_PORT.print("-");
294+
}else{
295+
SERIAL_PORT.print(" ");
296+
}
297+
for( uint8_t indi = 0; indi < leading; indi++ ){
298+
uint32_t tenpow = 0;
299+
if( indi < (leading-1) ){
300+
tenpow = 1;
301+
}
302+
for(uint8_t c = 0; c < (leading-1-indi); c++){
303+
tenpow *= 10;
304+
}
305+
if( aval < tenpow){
306+
SERIAL_PORT.print("0");
307+
}else{
308+
break;
309+
}
310+
}
311+
if(val < 0){
312+
SERIAL_PORT.print(-val, decimals);
313+
}else{
314+
SERIAL_PORT.print(val, decimals);
315+
}
316+
}
317+
318+
void printScaledAGMT( ICM_20948_AGMT_t agmt){
319+
SERIAL_PORT.print("Scaled. Acc (mg) [ ");
320+
printFormattedFloat( myICM.accX(), 5, 2 );
321+
SERIAL_PORT.print(", ");
322+
printFormattedFloat( myICM.accY(), 5, 2 );
323+
SERIAL_PORT.print(", ");
324+
printFormattedFloat( myICM.accZ(), 5, 2 );
325+
SERIAL_PORT.print(" ], Gyr (DPS) [ ");
326+
printFormattedFloat( myICM.gyrX(), 5, 2 );
327+
SERIAL_PORT.print(", ");
328+
printFormattedFloat( myICM.gyrY(), 5, 2 );
329+
SERIAL_PORT.print(", ");
330+
printFormattedFloat( myICM.gyrZ(), 5, 2 );
331+
SERIAL_PORT.print(" ], Mag (uT) [ ");
332+
printFormattedFloat( myICM.magX(), 5, 2 );
333+
SERIAL_PORT.print(", ");
334+
printFormattedFloat( myICM.magY(), 5, 2 );
335+
SERIAL_PORT.print(", ");
336+
printFormattedFloat( myICM.magZ(), 5, 2 );
337+
SERIAL_PORT.print(" ], Tmp (C) [ ");
338+
printFormattedFloat( myICM.temp(), 5, 2 );
339+
SERIAL_PORT.print(" ]");
340+
SERIAL_PORT.println();
341+
}

0 commit comments

Comments
 (0)