├── BNO055 └── BNO055.ino ├── ISM330DHCX_1_raw_values └── ISM330DHCX_1_raw_values.ino ├── ISM330DHCX_2_IMU └── ISM330DHCX_2_IMU.ino ├── ISM330DHCX_3_pedometer └── ISM330DHCX_3_pedometer.ino ├── ISM330DHCX_4_single_tap └── ISM330DHCX_4_single_tap.ino ├── ISM330DHCX_5_double_tap └── ISM330DHCX_5_double_tap.ino ├── ISM330DHCX_6_tilt_detection └── ISM330DHCX_6_tilt_detection.ino ├── ISM330DHCX_7_free-fall └── ISM330DHCX_7_free-fall.ino ├── LSM9DS1_1_raw_values └── LSM9DS1_1_raw_values.ino ├── LSM9DS1_2_motioncal_calibration └── LSM9DS1_2_motioncal_calibration.ino ├── LSM9DS1_3_accel_gyro_calibration └── LSM9DS1_3_accel_gyro_calibration.ino ├── LSM9DS1_4_AHRS └── LSM9DS1_4_AHRS.ino ├── LSM9DS1_4_AHRS_v2 └── LSM9DS1_4_AHRS_v2.ino ├── MPU6050_1_raw_values └── MPU6050_1_raw_values.ino ├── MPU6050_2_raw_values_offsets └── MPU6050_2_raw_values_offsets.ino ├── MPU6050_3_DMP_simpilified └── MPU6050_3_DMP_simpilified.ino ├── MPU6050_4_free-falling └── MPU6050_4_free-falling.ino ├── MPU6050_5_DMP_two_sensors └── MPU6050_5_DMP_two_sensors.ino ├── MinIMU-9_1_raw_values └── MinIMU-9_1_raw_values.ino ├── MinIMU-9_2_motioncal_calibration └── MinIMU-9_2_motioncal_calibration.ino ├── MinIMU-9_3_accel_gyro_calibration └── MinIMU-9_3_accel_gyro_calibration.ino ├── MinIMU-9_4_AHRS └── MinIMU-9_4_AHRS.ino └── README.md /BNO055/BNO055.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | Adafruit_BNO055 bno = Adafruit_BNO055(55, 0x28); 5 | 6 | void setup() { 7 | Serial.begin(115200); 8 | bno.begin(); 9 | bno.setExtCrystalUse(true); 10 | Wire.setClock(400000); 11 | } 12 | 13 | void loop() { 14 | uint8_t sysCal, gyroCal, accelCal, magCal = 0; 15 | bno.getCalibration(&sysCal, &gyroCal, &accelCal, &magCal); 16 | 17 | imu::Quaternion quaternions = bno.getQuat(); 18 | imu::Vector<3> euler = quaternions.toEuler(); 19 | 20 | float roll = euler.x() * 57.2957795; 21 | float pitch = euler.y() * 57.2957795; 22 | float yaw = euler.z() * 57.2957795; 23 | 24 | Serial.print(sysCal); 25 | Serial.print(gyroCal); 26 | Serial.print(accelCal); 27 | Serial.print(magCal); 28 | Serial.print("\t"); 29 | 30 | Serial.print(roll); 31 | Serial.print("\t"); 32 | Serial.print(pitch); 33 | Serial.print("\t"); 34 | Serial.println(yaw); 35 | } 36 | -------------------------------------------------------------------------------- /ISM330DHCX_1_raw_values/ISM330DHCX_1_raw_values.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | Adafruit_ISM330DHCX ism; 4 | 5 | void setup() { 6 | Serial.begin(115200); 7 | 8 | if(!ism.begin_I2C()) { 9 | Serial.println("ISM330DHCX connection error"); 10 | } 11 | } 12 | 13 | void loop() { 14 | sensors_event_t accel; 15 | sensors_event_t gyro; 16 | sensors_event_t temp; 17 | ism.getEvent(&accel, &gyro, &temp); 18 | 19 | Serial.print("Accel XYZ: "); 20 | Serial.print("\t"); 21 | Serial.print(accel.acceleration.x); 22 | Serial.print("\t"); 23 | Serial.print(accel.acceleration.y); 24 | Serial.print("\t"); 25 | Serial.print(accel.acceleration.z); 26 | 27 | Serial.print("\tGyro XYZ: "); 28 | Serial.print("\t"); 29 | Serial.print(gyro.gyro.x); 30 | Serial.print("\t"); 31 | Serial.print(gyro.gyro.y); 32 | Serial.print("\t"); 33 | Serial.println(gyro.gyro.z); 34 | 35 | delay(20); 36 | } 37 | -------------------------------------------------------------------------------- /ISM330DHCX_2_IMU/ISM330DHCX_2_IMU.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | Adafruit_ISM330DHCX ism; 5 | Adafruit_Mahony filter; 6 | 7 | void setup() { 8 | Serial.begin(115200); 9 | 10 | if(!ism.begin_I2C(0x6A)) { 11 | Serial.println("ISM330DHCX connection error"); 12 | } 13 | 14 | filter.begin(20); 15 | } 16 | 17 | void loop() { 18 | sensors_event_t accel; 19 | sensors_event_t gyro; 20 | sensors_event_t temp; 21 | ism.getEvent(&accel, &gyro, &temp); 22 | 23 | gyro.gyro.x -= -0.0024 * 57.2957795; 24 | gyro.gyro.y -= -0.0098 * 57.2957795; 25 | gyro.gyro.z -= 0.0024 * 57.2957795; 26 | 27 | filter.updateIMU( 28 | gyro.gyro.x, 29 | gyro.gyro.y, 30 | gyro.gyro.z, 31 | accel.acceleration.x, 32 | accel.acceleration.y, 33 | accel.acceleration.z 34 | ); 35 | 36 | float roll = filter.getRoll(); 37 | float pitch = filter.getPitch(); 38 | float yaw = filter.getYaw(); 39 | 40 | Serial.print("Orientation: "); 41 | Serial.print(roll); 42 | Serial.print('\t'); 43 | Serial.print(pitch); 44 | Serial.print('\t'); 45 | Serial.print(yaw); 46 | Serial.println('\t'); 47 | } -------------------------------------------------------------------------------- /ISM330DHCX_3_pedometer/ISM330DHCX_3_pedometer.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* 5 | FUNC_CFG_ACCESS enables and disables embedded functions registers 6 | where pedometer is located. It switches the register addresses 7 | so if you want to access default registers you need to disable it 8 | again. It's tricky to do if you don't know how to do bare-metal 9 | programming. 10 | 11 | If you upload a different sketch that won't use this feature you must 12 | reset the sensor (not the Arduino itself), otherwise it won't work. 13 | */ 14 | 15 | #define ADDRESS 0x6A 16 | #define FUNC_CFG_ACCESS 0x01 17 | #define EMB_FUNC_EN_A 0x04 18 | #define MD1_CFG 0x5E 19 | #define EMB_FUNC_INT1 0x0A 20 | #define EMB_FUNC_INIT_A 0x66 21 | 22 | #define FUNC_CFG_ACCESS_EN (1 << 7) 23 | #define PEDO_EN (1 << 3) 24 | #define INT1_EMB_FUNC (1 << 1) 25 | #define INT1_STEP_DETECTOR (1 << 3) 26 | #define STEP_DET_INIT (1 << 3) 27 | 28 | #define EMB_FUNC_STEP_COUNTER_L 0x62 29 | 30 | Adafruit_ISM330DHCX ism; 31 | 32 | void setup() { 33 | Serial.begin(115200); 34 | while(!Serial) delay(10); 35 | 36 | Wire.begin(); 37 | 38 | if(!ism.begin_I2C(0x6A)) { 39 | Serial.println("ISM330DHCX connection error"); 40 | } 41 | 42 | enablePedometer(); 43 | } 44 | 45 | void loop() { 46 | uint16_t steps = readPedometer(); 47 | 48 | //the pedometer will append new steps after 10 steps in a row. 49 | Serial.print("Number of steps: "); 50 | Serial.println(steps); 51 | 52 | delay(100); 53 | } 54 | 55 | 56 | void enablePedometer(){ 57 | //interrupt routing enable 58 | //i2c_write(ADDRESS, MD1_CFG, INT1_EMB_FUNC); 59 | 60 | i2c_write(ADDRESS, FUNC_CFG_ACCESS, FUNC_CFG_ACCESS_EN); 61 | i2c_write(ADDRESS, EMB_FUNC_EN_A, PEDO_EN); 62 | 63 | //routing enable step detector 64 | //i2c_write(ADDRESS, EMB_FUNC_INT1, INT1_STEP_DETECTOR); 65 | 66 | i2c_write(ADDRESS, EMB_FUNC_INIT_A, STEP_DET_INIT); 67 | 68 | //switch back to normal registers from embedded functions 69 | //otherwise you can't read basic registers 70 | //i2c_write(ADDRESS, FUNC_CFG_ACCESS, 0); 71 | } 72 | 73 | uint16_t readPedometer(){ 74 | Wire.beginTransmission(ADDRESS); 75 | Wire.write(EMB_FUNC_STEP_COUNTER_L); 76 | Wire.endTransmission(false); 77 | Wire.requestFrom(ADDRESS, 2, true); 78 | 79 | uint16_t steps = (Wire.read() | Wire.read() << 8); 80 | 81 | return steps; 82 | } 83 | 84 | void i2c_write(uint8_t addr, uint8_t reg, uint8_t val){ 85 | Wire.beginTransmission(addr); 86 | Wire.write(reg); 87 | Wire.write(val); 88 | Wire.endTransmission(); 89 | } -------------------------------------------------------------------------------- /ISM330DHCX_4_single_tap/ISM330DHCX_4_single_tap.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define ADDRESS 0x6A 5 | 6 | #define CTRL1_XL 0x10 7 | #define CTRL3_C 0x12 8 | 9 | #define TAP_CFG0 0x56 10 | #define TAP_CFG1 0x57 11 | #define TAP_CFG2 0x58 12 | #define INT1_CTRL 0x0D 13 | #define TAP_THS_6D 0x59 14 | #define INT_DUR2 0x5A 15 | #define MD1_CFG 0x5E 16 | #define TAP_SRC 0x1C 17 | 18 | #define INT_CLR_ON_READ (1 << 7) 19 | #define TAP_X_EN (1 << 3) 20 | #define TAP_Y_EN (1 << 2) 21 | #define TAP_Z_EN (1 << 1) 22 | #define LATCHED_INT (1 << 0) 23 | 24 | #define TAP_THRESHOLD_X 9 25 | #define TAP_THRESHOLD_Y 9 26 | #define TAP_THRESHOLD_Z 9 27 | #define TAP_SHOCK 2 28 | #define TAP_QUIET (1 << 2) 29 | 30 | #define INTERRUPTS_EN (1 << 7) 31 | #define INT1_SINGLE_TAP (1 << 6) 32 | #define SINGLE_TAP (1 << 6) 33 | 34 | #define ACCEL_ODR (6 << 4) 35 | #define SW_RESET 1 36 | 37 | #define isTapX (1 << 2) 38 | #define isTapY (1 << 1) 39 | #define isTapZ (1 << 0) 40 | #define tapSign (1 << 3) 41 | 42 | Adafruit_ISM330DHCX ism; 43 | 44 | volatile int val = 0; 45 | volatile int counter = 0; 46 | volatile bool isTappedFlag = false; 47 | 48 | void setup(){ 49 | Serial.begin(115200); 50 | while(!Serial) delay(10); 51 | 52 | Wire.begin(); 53 | Wire.setClock(400000); 54 | Wire.setWireTimeout(3000, true); 55 | 56 | i2c_write(ADDRESS, CTRL3_C, SW_RESET); 57 | 58 | if(!ism.begin_I2C(ADDRESS)) { 59 | Serial.println("ISM330DHCX connection error"); 60 | } 61 | 62 | enableSingleTap(); 63 | attachInterrupt(digitalPinToInterrupt(2), readTap, RISING); 64 | } 65 | 66 | void loop(){ 67 | 68 | if(isTappedFlag){ 69 | Wire.beginTransmission(ADDRESS); 70 | Wire.write(TAP_SRC); 71 | Wire.endTransmission(false); 72 | Wire.requestFrom(ADDRESS, 1, false); 73 | 74 | volatile uint8_t tap = Wire.read(); 75 | 76 | if(tap & isTapX){ 77 | if(tap & tapSign) Serial.print("+X tap "); 78 | else Serial.print("-X tap "); 79 | } 80 | else if(tap & isTapY){ 81 | if(tap & tapSign) Serial.print("+Y tap "); 82 | else Serial.print("-Y tap "); 83 | } 84 | else if(tap & isTapZ){ 85 | if(tap & tapSign) Serial.print("+Z tap "); 86 | else Serial.print("-Z tap "); 87 | } 88 | 89 | Serial.println(counter); 90 | isTappedFlag = false; 91 | } 92 | 93 | } 94 | 95 | void enableSingleTap(){ 96 | i2c_write(ADDRESS, CTRL1_XL, ACCEL_ODR); 97 | i2c_write(ADDRESS, TAP_CFG0, INT_CLR_ON_READ | TAP_X_EN | TAP_Y_EN | TAP_Z_EN | LATCHED_INT); 98 | i2c_write(ADDRESS, TAP_CFG1, TAP_THRESHOLD_X); 99 | i2c_write(ADDRESS, TAP_CFG2, INTERRUPTS_EN | TAP_THRESHOLD_Y); 100 | i2c_write(ADDRESS, TAP_THS_6D, TAP_THRESHOLD_Z); 101 | i2c_write(ADDRESS, INT_DUR2, TAP_SHOCK | TAP_QUIET); 102 | i2c_write(ADDRESS, MD1_CFG, INT1_SINGLE_TAP); 103 | } 104 | 105 | void readTap(){ 106 | counter++; 107 | isTappedFlag = true; 108 | } 109 | 110 | void i2c_write(uint8_t addr, uint8_t reg, uint8_t val){ 111 | Wire.beginTransmission(addr); 112 | Wire.write(reg); 113 | Wire.write(val); 114 | uint8_t error = Wire.endTransmission(); 115 | 116 | Serial.print(reg, HEX); 117 | Serial.print(" , "); 118 | Serial.println(error); 119 | } -------------------------------------------------------------------------------- /ISM330DHCX_5_double_tap/ISM330DHCX_5_double_tap.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define ADDRESS 0x6A 5 | 6 | #define CTRL1_XL 0x10 7 | #define CTRL3_C 0x12 8 | 9 | #define TAP_CFG0 0x56 10 | #define TAP_CFG1 0x57 11 | #define TAP_CFG2 0x58 12 | #define INT1_CTRL 0x0D 13 | #define TAP_THS_6D 0x59 14 | #define INT_DUR2 0x5A 15 | #define WAKE_UP_THS 0x5B 16 | #define MD1_CFG 0x5E 17 | #define TAP_SRC 0x1C 18 | 19 | #define INT_CLR_ON_READ (1 << 7) 20 | #define TAP_X_EN (1 << 3) 21 | #define TAP_Y_EN (1 << 2) 22 | #define TAP_Z_EN (1 << 1) 23 | #define LATCHED_INT (1 << 0) 24 | #define DOUBLE_TAP_EN (1 << 7) 25 | 26 | #define TAP_THRESHOLD_X 0xC 27 | #define TAP_THRESHOLD_Y 0xC 28 | #define TAP_THRESHOLD_Z 0xC 29 | #define TAP_SHOCK 3 30 | #define TAP_QUIET (3 << 2) 31 | #define TAP_DURATION (7 << 4) 32 | 33 | #define INTERRUPTS_EN (1 << 7) 34 | #define INT1_SINGLE_TAP (1 << 6) 35 | #define INT1_DOUBLE_TAP (1 << 3) 36 | #define SINGLE_TAP (1 << 6) 37 | 38 | #define ACCEL_ODR (6 << 4) 39 | #define SW_RESET 1 40 | 41 | #define isTapX (1 << 2) 42 | #define isTapY (1 << 1) 43 | #define isTapZ (1 << 0) 44 | #define tapSign (1 << 3) 45 | #define isDoubleTap (1 << 4) 46 | 47 | Adafruit_ISM330DHCX ism; 48 | 49 | volatile int val = 0; 50 | volatile int counter = 0; 51 | volatile bool isTappedFlag = false; 52 | 53 | void setup(){ 54 | Serial.begin(115200); 55 | while(!Serial) delay(10); 56 | 57 | Wire.begin(); 58 | Wire.setClock(400000); 59 | Wire.setWireTimeout(3000, true); 60 | 61 | i2c_write(ADDRESS, CTRL3_C, SW_RESET); 62 | 63 | if(!ism.begin_I2C(ADDRESS)) { 64 | Serial.println("ISM330DHCX connection error"); 65 | } 66 | 67 | enableDoubleTap(); 68 | attachInterrupt(digitalPinToInterrupt(2), readTap, RISING); 69 | } 70 | 71 | void loop(){ 72 | 73 | if(isTappedFlag){ 74 | Wire.beginTransmission(ADDRESS); 75 | Wire.write(TAP_SRC); 76 | Wire.endTransmission(false); 77 | Wire.requestFrom(ADDRESS, 1, false); 78 | 79 | volatile uint8_t tap = Wire.read(); 80 | 81 | if(tap & isDoubleTap) Serial.print("Double tapped "); 82 | 83 | if(tap & isTapX){ 84 | if(tap & tapSign) Serial.print("+X tap "); 85 | else Serial.print("-X tap "); 86 | } 87 | else if(tap & isTapY){ 88 | if(tap & tapSign) Serial.print("+Y tap "); 89 | else Serial.print("-Y tap "); 90 | } 91 | else if(tap & isTapZ){ 92 | if(tap & tapSign) Serial.print("+Z tap "); 93 | else Serial.print("-Z tap "); 94 | } 95 | 96 | Serial.println(counter); 97 | isTappedFlag = false; 98 | } 99 | 100 | } 101 | 102 | void enableDoubleTap(){ 103 | i2c_write(ADDRESS, CTRL1_XL, 0x60); 104 | i2c_write(ADDRESS, TAP_CFG0, TAP_X_EN | TAP_Y_EN | TAP_Z_EN); 105 | i2c_write(ADDRESS, TAP_CFG1, TAP_THRESHOLD_X); 106 | i2c_write(ADDRESS, TAP_CFG2, INTERRUPTS_EN | TAP_THRESHOLD_Y); 107 | i2c_write(ADDRESS, TAP_THS_6D, TAP_THRESHOLD_Z); 108 | i2c_write(ADDRESS, INT_DUR2, TAP_DURATION | TAP_SHOCK | TAP_QUIET); 109 | i2c_write(ADDRESS, WAKE_UP_THS, DOUBLE_TAP_EN); 110 | i2c_write(ADDRESS, MD1_CFG, INT1_SINGLE_TAP | INT1_DOUBLE_TAP); 111 | } 112 | 113 | void readTap(){ 114 | counter++; 115 | isTappedFlag = true; 116 | } 117 | 118 | void i2c_write(uint8_t addr, uint8_t reg, uint8_t val){ 119 | Wire.beginTransmission(addr); 120 | Wire.write(reg); 121 | Wire.write(val); 122 | uint8_t error = Wire.endTransmission(); 123 | 124 | Serial.print(reg, HEX); 125 | Serial.print(" , "); 126 | Serial.println(error); 127 | } -------------------------------------------------------------------------------- /ISM330DHCX_6_tilt_detection/ISM330DHCX_6_tilt_detection.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define ADDRESS 0x6A 5 | 6 | #define CTRL1_XL 0x10 7 | #define CTRL3_C 0x12 8 | #define TAP_CFG0 0x56 9 | #define TAP_CFG2 0x58 10 | #define TAP_THS_6D 0x59 11 | #define CTRL8_XL 0x17 12 | #define MD1_CFG 0x5E 13 | #define DRD_SRC 0x1D 14 | 15 | #define ACCELEROMETER_417HZ_2G (6 << 4) 16 | #define INT_CLR_ON_READ (1 << 7) 17 | #define LATCHED_INT (1 << 0) 18 | #define INTERRUPTS_EN (1 << 7) 19 | #define TILT_THRESHOLD_60DEG (2 << 5) 20 | #define LOW_PASS_ON_6D (1 << 0) 21 | #define INT1_6D (1 << 2) 22 | 23 | #define TILT_ZH (1 << 5) 24 | #define TILT_ZL (1 << 4) 25 | #define TILT_YH (1 << 3) 26 | #define TILT_YL (1 << 2) 27 | #define TILT_XH (1 << 1) 28 | #define TILT_XL (1 << 0) 29 | 30 | #define SW_RESET 1 31 | 32 | volatile bool isTiltFlag = false; 33 | 34 | Adafruit_ISM330DHCX ism; 35 | 36 | void setup(){ 37 | Serial.begin(115200); 38 | while(!Serial) delay(10); 39 | 40 | Wire.begin(); 41 | Wire.setClock(400000); 42 | Wire.setWireTimeout(3000, true); 43 | 44 | i2c_write(ADDRESS, CTRL3_C, SW_RESET); 45 | 46 | if(!ism.begin_I2C(ADDRESS)) { 47 | Serial.println("ISM330DHCX connection error"); 48 | } 49 | 50 | enableTilt6D(); 51 | attachInterrupt(digitalPinToInterrupt(2), readTilt, RISING); 52 | } 53 | 54 | void loop(){ 55 | if(isTiltFlag){ 56 | 57 | Wire.beginTransmission(ADDRESS); 58 | Wire.write(DRD_SRC); 59 | Wire.endTransmission(false); 60 | Wire.requestFrom(ADDRESS, 1, false); 61 | 62 | volatile uint8_t tilt = Wire.read(); 63 | 64 | Serial.print("Orientation: "); 65 | 66 | if(tilt & TILT_ZH) Serial.println("ZH"); 67 | else if(tilt & TILT_ZL) Serial.println("ZL"); 68 | else if(tilt & TILT_YH) Serial.println("YH"); 69 | else if(tilt & TILT_YL) Serial.println("YL"); 70 | else if(tilt & TILT_XH) Serial.println("XH"); 71 | else if(tilt & TILT_XL) Serial.println("XL"); 72 | 73 | isTiltFlag = false; 74 | } 75 | } 76 | 77 | void enableTilt6D(){ 78 | i2c_write(ADDRESS, CTRL1_XL, ACCELEROMETER_417HZ_2G); 79 | i2c_write(ADDRESS, TAP_CFG0, INT_CLR_ON_READ | LATCHED_INT); 80 | i2c_write(ADDRESS, TAP_CFG2, INTERRUPTS_EN); 81 | i2c_write(ADDRESS, TAP_THS_6D, TILT_THRESHOLD_60DEG); 82 | i2c_write(ADDRESS, CTRL8_XL, LOW_PASS_ON_6D ); 83 | i2c_write(ADDRESS, MD1_CFG, INT1_6D); 84 | } 85 | 86 | void readTilt(){ 87 | isTiltFlag = true; 88 | } 89 | 90 | void i2c_write(uint8_t addr, uint8_t reg, uint8_t val){ 91 | Wire.beginTransmission(addr); 92 | Wire.write(reg); 93 | Wire.write(val); 94 | uint8_t error = Wire.endTransmission(); 95 | 96 | Serial.print(reg, HEX); 97 | Serial.print(" , "); 98 | Serial.println(error); 99 | } -------------------------------------------------------------------------------- /ISM330DHCX_7_free-fall/ISM330DHCX_7_free-fall.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define ADDRESS 0x6A 5 | 6 | #define CTRL1_XL 0x10 7 | #define CTRL3_C 0x12 8 | #define TAP_CFG0 0x56 9 | #define TAP_CFG2 0x58 10 | //#define WAKE_UP_DUR 0x5C 11 | #define FREE_FALL 0x5D 12 | #define MD1_CFG 0x5E 13 | 14 | #define ACCELEROMETER_417HZ_2G (6 << 4) 15 | #define INT_CLR_ON_READ (1 << 7) 16 | #define LATCHED_INT (1 << 0) 17 | #define INTERRUPTS_EN (1 << 7) 18 | #define FREE_FALL_THRESHOLD (3 << 0) 19 | #define FREE_FALL_DURATION (6 << 3) 20 | #define INT1_FF_INTERRUPT (1 << 4) 21 | 22 | #define SW_RESET 1 23 | 24 | Adafruit_ISM330DHCX ism; 25 | 26 | void setup(){ 27 | Serial.begin(115200); 28 | while(!Serial) delay(10); 29 | 30 | Wire.begin(); 31 | Wire.setClock(400000); 32 | Wire.setWireTimeout(3000, true); 33 | 34 | i2c_write(ADDRESS, CTRL3_C, SW_RESET); 35 | 36 | if(!ism.begin_I2C(ADDRESS)) { 37 | Serial.println("ISM330DHCX connection error"); 38 | } 39 | 40 | enableFreeFall(); 41 | attachInterrupt(digitalPinToInterrupt(2), freeFallInt, RISING); 42 | } 43 | 44 | void loop(){}; 45 | 46 | void enableFreeFall(){ 47 | i2c_write(ADDRESS, CTRL1_XL, ACCELEROMETER_417HZ_2G); 48 | i2c_write(ADDRESS, TAP_CFG0, INT_CLR_ON_READ | LATCHED_INT); 49 | i2c_write(ADDRESS, TAP_CFG2, INTERRUPTS_EN); 50 | i2c_write(ADDRESS, FREE_FALL, FREE_FALL_DURATION | FREE_FALL_THRESHOLD); 51 | i2c_write(ADDRESS, MD1_CFG, INT1_FF_INTERRUPT); 52 | } 53 | 54 | void freeFallInt(){ 55 | Serial.println("Free falling!"); 56 | } 57 | 58 | void i2c_write(uint8_t addr, uint8_t reg, uint8_t val){ 59 | Wire.beginTransmission(addr); 60 | Wire.write(reg); 61 | Wire.write(val); 62 | uint8_t error = Wire.endTransmission(); 63 | 64 | Serial.print(reg, HEX); 65 | Serial.print(" , "); 66 | Serial.println(error); 67 | } -------------------------------------------------------------------------------- /LSM9DS1_1_raw_values/LSM9DS1_1_raw_values.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void setup() { 4 | Serial.begin(115200); 5 | 6 | if(!IMU.begin()){ 7 | Serial.println("LSM9DS1 initialization error"); 8 | while(1); 9 | } 10 | } 11 | 12 | void loop() { 13 | float ax, ay, az, gx, gy, gz, mx, my, mz; 14 | 15 | IMU.readAcceleration(ax, ay, az); 16 | IMU.readGyroscope(gx, gy, gz); 17 | IMU.readMagneticField(mx, my, mz); 18 | 19 | Serial.print(ax); 20 | Serial.print('\t'); 21 | Serial.print(ay); 22 | Serial.print('\t'); 23 | Serial.print(az); 24 | 25 | Serial.print('\t'); 26 | Serial.print(gx); 27 | Serial.print('\t'); 28 | Serial.print(gy); 29 | Serial.print('\t'); 30 | Serial.print(gz); 31 | 32 | Serial.print('\t'); 33 | Serial.print(mx); 34 | Serial.print('\t'); 35 | Serial.print(my); 36 | Serial.print('\t'); 37 | Serial.println(mz); 38 | } 39 | -------------------------------------------------------------------------------- /LSM9DS1_2_motioncal_calibration/LSM9DS1_2_motioncal_calibration.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void setup() { 4 | Serial.begin(115200); 5 | 6 | if(!IMU.begin()){ 7 | Serial.println("LSM9DS1 initialization error"); 8 | while(1); 9 | } 10 | } 11 | 12 | void loop() { 13 | float ax, ay, az, gx, gy, gz, mx, my, mz; 14 | 15 | IMU.readAcceleration(ax, ay, az); 16 | IMU.readGyroscope(gx, gy, gz); 17 | IMU.readMagneticField(mx, my, mz); 18 | 19 | Serial.print("Raw:"); 20 | Serial.print(int(ax*8192/9.8)); Serial.print(","); 21 | Serial.print(int(ay*8192/9.8)); Serial.print(","); 22 | Serial.print(int(az*8192/9.8)); Serial.print(","); 23 | Serial.print(int(gx*57.2957795*16)); Serial.print(","); 24 | Serial.print(int(gy*57.2957795*16)); Serial.print(","); 25 | Serial.print(int(gz*57.2957795*16)); Serial.print(","); 26 | Serial.print(int(mx*10)); Serial.print(","); 27 | Serial.print(int(my*10)); Serial.print(","); 28 | Serial.print(int(mz*10)); Serial.println(""); 29 | } 30 | -------------------------------------------------------------------------------- /LSM9DS1_3_accel_gyro_calibration/LSM9DS1_3_accel_gyro_calibration.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void setup() { 4 | Serial.begin(115200); 5 | while(!Serial); 6 | 7 | if(!IMU.begin()){ 8 | Serial.println("LSM9DS1 initialization error"); 9 | while(1); 10 | } 11 | 12 | float totalAccelX = 0; 13 | float totalAccelY = 0; 14 | float totalAccelZ = 0; 15 | float totalGyroX = 0; 16 | float totalGyroY = 0; 17 | float totalGyroZ = 0; 18 | 19 | int samples = 10000; 20 | 21 | Serial.println("Starting Calibration..."); 22 | Serial.println("Do not move the device"); 23 | delay(4000); 24 | Serial.println("Calibrating..."); 25 | 26 | for(int i=0; i < samples; i++){ 27 | 28 | float gx, gy, gz; 29 | IMU.readGyroscope(gx, gy, gz); 30 | 31 | //Quick and dirty way to remove spikes from gyro measurement. 32 | if( gx > 8 || gy > 8 || gz > 8){ 33 | i--; 34 | delay(1); 35 | continue; 36 | } 37 | 38 | totalGyroX += gx; 39 | totalGyroY += gy; 40 | totalGyroZ += gz; 41 | 42 | float ax, ay, az; 43 | IMU.readAcceleration(ax, ay, az); 44 | 45 | totalAccelX += ax; 46 | totalAccelY += ay; 47 | totalAccelZ += az - 0.98; 48 | 49 | delay(1); 50 | } 51 | 52 | float accelMeanX = totalAccelX / samples; 53 | float accelMeanY = totalAccelY / samples; 54 | float accelMeanZ = totalAccelZ / samples; 55 | 56 | float gyroMeanX = totalGyroX / samples; 57 | float gyroMeanY = totalGyroY / samples; 58 | float gyroMeanZ = totalGyroZ / samples; 59 | 60 | 61 | Serial.println("Accel XYZ offset:"); 62 | Serial.print(accelMeanX); 63 | Serial.print('\t'); 64 | Serial.print(accelMeanY); 65 | Serial.print('\t'); 66 | Serial.println(accelMeanZ); 67 | 68 | Serial.println("Gyro XYZ offset:"); 69 | Serial.print(gyroMeanX); 70 | Serial.print('\t'); 71 | Serial.print(gyroMeanY); 72 | Serial.print('\t'); 73 | Serial.println(gyroMeanZ); 74 | Serial.println(' '); 75 | } 76 | 77 | void loop() { 78 | 79 | } 80 | -------------------------------------------------------------------------------- /LSM9DS1_4_AHRS/LSM9DS1_4_AHRS.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | Adafruit_Madgwick filter; 5 | 6 | int updateRate = 100; 7 | int printEveryUpdate = 5; 8 | 9 | float mag_hardIron[3] = {49.29, -34.35, -58.99}; 10 | float mag_softIron[9] = { 11 | 0.995, 0.025, 0.003, 12 | 0.025, 0.989, -0.006, 13 | 0.003, -0.006, 1.017 14 | }; 15 | float accel_calibration[3] = {-0.01, 0.06, 0.01}; 16 | float gyro_calibration[3] = {-0.55, 0.52, 1.15}; 17 | 18 | void setup() { 19 | Serial.begin(115200); 20 | 21 | if(!IMU.begin()){ 22 | Serial.println("LSM9DS1 initialization error"); 23 | while(1); 24 | } 25 | 26 | filter.begin(updateRate); 27 | 28 | Wire1.begin(); 29 | Wire1.beginTransmission(0x1e); 30 | Wire1.write(0x20); 31 | Wire1.write(0x9e); 32 | Wire1.endTransmission(); 33 | } 34 | 35 | unsigned long timestamp = 0; 36 | int counter = 0; 37 | 38 | float ax, ay, az, gx, gy, gz, mx, my, mz = 0; 39 | 40 | void loop() { 41 | 42 | if(millis() - timestamp >= 1000 / updateRate){ 43 | 44 | if(IMU.accelerationAvailable()) IMU.readAcceleration(ax, ay, az); 45 | if(IMU.gyroscopeAvailable()) IMU.readGyroscope(gx, gy, gz); 46 | if(IMU.magneticFieldAvailable()) IMU.readMagneticField(mx, my, mz); 47 | 48 | calibrate(&ax, &ay, &az, &gx, &gy, &gz, &mx, &my, &mz); 49 | 50 | //filter.update(gx, gy, gz, ax, ay, az, mx, my, mz); 51 | filter.update(-gy, -gx, gz, -ay, -ax, az, mx, my, mz); 52 | 53 | timestamp = millis(); 54 | 55 | counter++; 56 | 57 | if(counter == printEveryUpdate){ 58 | 59 | float w, x, y, z; 60 | filter.getQuaternion(&w,&x,&y,&z); 61 | 62 | Serial.print("Quaternion: "); 63 | Serial.print(w); 64 | Serial.print(','); 65 | Serial.print(x); 66 | Serial.print(','); 67 | Serial.print(y); 68 | Serial.print(','); 69 | Serial.println(z); 70 | 71 | // float roll = filter.getRoll(); 72 | // float pitch = filter.getPitch(); 73 | // float yaw = filter.getYaw(); 74 | 75 | // Serial.print("Orientation: "); 76 | // Serial.print(yaw); 77 | // Serial.print(','); 78 | // Serial.print(pitch); 79 | // Serial.print(','); 80 | // Serial.println(roll); 81 | 82 | counter = 0; 83 | } 84 | } 85 | } 86 | 87 | void calibrate(float *ax, float *ay, float *az, float *gx, float *gy, float *gz, float *mx, float *my, float *mz){ 88 | *ax -= accel_calibration[0]; 89 | *ay -= accel_calibration[1]; 90 | *az -= accel_calibration[2]; 91 | 92 | *gx -= gyro_calibration[0]; 93 | *gy -= gyro_calibration[1]; 94 | *gz -= gyro_calibration[2]; 95 | 96 | *mx -= mag_hardIron[0]; 97 | *my -= mag_hardIron[1]; 98 | *mz -= mag_hardIron[2]; 99 | 100 | *mx = *mx * mag_softIron[0] + *my * mag_softIron[1] + *mz * mag_softIron[2]; 101 | *my = *mx * mag_softIron[3] + *my * mag_softIron[4] + *mz * mag_softIron[5]; 102 | *mz = *mx * mag_softIron[6] + *my * mag_softIron[7] + *mz * mag_softIron[8]; 103 | } 104 | -------------------------------------------------------------------------------- /LSM9DS1_4_AHRS_v2/LSM9DS1_4_AHRS_v2.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | Adafruit_Madgwick filter; 5 | 6 | int updateRate = 200; 7 | int printEveryUpdate = 5; 8 | 9 | float mag_hardIron[3] = {49.29, -34.35, -58.99}; 10 | float mag_softIron[9] = { 11 | 0.995, 0.025, 0.003, 12 | 0.025, 0.989, -0.006, 13 | 0.003, -0.006, 1.017 14 | }; 15 | float accel_calibration[3] = {-0.01, 0.06, 0.01}; 16 | float gyro_calibration[3] = {-0.55, 0.52, 1.15}; 17 | 18 | void setup() { 19 | Serial.begin(115200); 20 | 21 | if(!IMU.begin()){ 22 | Serial.println("LSM9DS1 initialization error"); 23 | while(1); 24 | } 25 | 26 | filter.begin(updateRate); 27 | 28 | Wire1.begin(); 29 | Wire1.beginTransmission(0x1e); 30 | Wire1.write(0x20); 31 | Wire1.write(0x9e); 32 | Wire1.endTransmission(); 33 | } 34 | 35 | unsigned long timestamp = 0; 36 | int counter = 0; 37 | 38 | float ax, ay, az, gx, gy, gz, mx, my, mz = 0; 39 | 40 | void loop() { 41 | 42 | //if(millis() - timestamp >= 1000 / updateRate){ 43 | 44 | if(IMU.accelerationAvailable()) IMU.readAcceleration(ax, ay, az); 45 | if(IMU.gyroscopeAvailable()) IMU.readGyroscope(gx, gy, gz); 46 | if(IMU.magneticFieldAvailable()) IMU.readMagneticField(mx, my, mz); 47 | 48 | calibrate(&ax, &ay, &az, &gx, &gy, &gz, &mx, &my, &mz); 49 | 50 | //filter.update(gx, gy, gz, ax, ay, az, mx, my, mz); 51 | filter.update(-gy, -gx, gz, -ay, -ax, az, mx, my, mz); 52 | 53 | timestamp = millis(); 54 | 55 | counter++; 56 | 57 | if(counter == printEveryUpdate){ 58 | 59 | float w, x, y, z; 60 | filter.getQuaternion(&w,&x,&y,&z); 61 | 62 | Serial.print("Quaternion: "); 63 | Serial.print(w); 64 | Serial.print(','); 65 | Serial.print(x); 66 | Serial.print(','); 67 | Serial.print(y); 68 | Serial.print(','); 69 | Serial.println(z); 70 | 71 | // float roll = filter.getRoll(); 72 | // float pitch = filter.getPitch(); 73 | // float yaw = filter.getYaw(); 74 | 75 | // Serial.print("Orientation: "); 76 | // Serial.print(yaw); 77 | // Serial.print(','); 78 | // Serial.print(pitch); 79 | // Serial.print(','); 80 | // Serial.println(roll); 81 | 82 | counter = 0; 83 | } 84 | //} 85 | } 86 | 87 | void calibrate(float *ax, float *ay, float *az, float *gx, float *gy, float *gz, float *mx, float *my, float *mz){ 88 | *ax -= accel_calibration[0]; 89 | *ay -= accel_calibration[1]; 90 | *az -= accel_calibration[2]; 91 | 92 | *gx -= gyro_calibration[0]; 93 | *gy -= gyro_calibration[1]; 94 | *gz -= gyro_calibration[2]; 95 | 96 | *mx -= mag_hardIron[0]; 97 | *my -= mag_hardIron[1]; 98 | *mz -= mag_hardIron[2]; 99 | 100 | *mx = *mx * mag_softIron[0] + *my * mag_softIron[1] + *mz * mag_softIron[2]; 101 | *my = *mx * mag_softIron[3] + *my * mag_softIron[4] + *mz * mag_softIron[5]; 102 | *mz = *mx * mag_softIron[6] + *my * mag_softIron[7] + *mz * mag_softIron[8]; 103 | } 104 | -------------------------------------------------------------------------------- /MPU6050_1_raw_values/MPU6050_1_raw_values.ino: -------------------------------------------------------------------------------- 1 | #include "MPU6050.h" 2 | 3 | MPU6050 mpu6050; 4 | 5 | int ax, ay, az; 6 | int gx, gy, gz; 7 | 8 | void setup() { 9 | Serial.begin(38400); 10 | mpu6050.initialize(); 11 | } 12 | 13 | void loop() { 14 | 15 | mpu6050.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); 16 | Serial.print("Accel XYZ:"); 17 | Serial.print("\t"); 18 | Serial.print(ax); 19 | Serial.print("\t"); 20 | Serial.print(ay); 21 | Serial.print("\t"); 22 | Serial.print(az); 23 | Serial.print("\t"); 24 | 25 | Serial.print("Gyro XYZ:"); 26 | Serial.print("\t"); 27 | Serial.print(gx); 28 | Serial.print("\t"); 29 | Serial.print(gy); 30 | Serial.print("\t"); 31 | Serial.println(gz); 32 | delay(80); 33 | } 34 | -------------------------------------------------------------------------------- /MPU6050_2_raw_values_offsets/MPU6050_2_raw_values_offsets.ino: -------------------------------------------------------------------------------- 1 | #include "MPU6050.h" 2 | 3 | MPU6050 mpu6050; 4 | 5 | int ax, ay, az; 6 | int gx, gy, gz; 7 | 8 | void setup() { 9 | Serial.begin(38400); 10 | mpu6050.initialize(); 11 | 12 | // mpu6050.setXAccelOffset(-2020); 13 | // mpu6050.setYAccelOffset(-1482); 14 | // mpu6050.setZAccelOffset(608); 15 | // mpu6050.setXGyroOffset(145); 16 | // mpu6050.setYGyroOffset(-37); 17 | // mpu6050.setZGyroOffset(-31); 18 | 19 | mpu6050.CalibrateAccel(6); 20 | mpu6050.CalibrateGyro(6); 21 | } 22 | 23 | void loop() { 24 | 25 | mpu6050.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); 26 | Serial.print("Accel XYZ:"); 27 | Serial.print("\t"); 28 | Serial.print(ax); 29 | Serial.print("\t"); 30 | Serial.print(ay); 31 | Serial.print("\t"); 32 | Serial.print(az); 33 | Serial.print("\t"); 34 | 35 | Serial.print("Gyro XYZ:"); 36 | Serial.print("\t"); 37 | Serial.print(gx); 38 | Serial.print("\t"); 39 | Serial.print(gy); 40 | Serial.print("\t"); 41 | Serial.println(gz); 42 | delay(80); 43 | } 44 | -------------------------------------------------------------------------------- /MPU6050_3_DMP_simpilified/MPU6050_3_DMP_simpilified.ino: -------------------------------------------------------------------------------- 1 | /* 2 | MPU-6050 3d model preview 3 | 4 | https://adafruit.github.io/Adafruit_WebSerial_3DModelViewer/ 5 | 6 | This app uses WebSerial which allow us to communicate Arduino 7 | with an app build like a website. 8 | */ 9 | 10 | #include "I2Cdev.h" 11 | #include "MPU6050_6Axis_MotionApps612.h" 12 | #include "Wire.h" 13 | 14 | MPU6050 mpu; 15 | 16 | uint8_t fifoBuffer[64]; 17 | 18 | Quaternion quat; 19 | VectorFloat gravity; 20 | float ypr[3]; 21 | 22 | void setup() { 23 | Wire.begin(); 24 | Wire.setClock(400000); 25 | Wire.setWireTimeout(3000, true); 26 | 27 | Serial.begin(115200); 28 | while (!Serial); 29 | 30 | mpu.initialize(); 31 | mpu.dmpInitialize(); 32 | 33 | mpu.CalibrateAccel(6); 34 | mpu.CalibrateGyro(6); 35 | mpu.PrintActiveOffsets(); 36 | 37 | mpu.setDMPEnabled(true); 38 | } 39 | 40 | void loop() { 41 | if (mpu.dmpGetCurrentFIFOPacket(fifoBuffer)) { 42 | mpu.dmpGetQuaternion(&quat, fifoBuffer); 43 | mpu.dmpGetGravity(&gravity, &quat); 44 | mpu.dmpGetYawPitchRoll(ypr, &quat, &gravity); 45 | 46 | float yaw = ypr[0] * 180 / M_PI; 47 | float pitch = ypr[1] * 180 / M_PI; 48 | float roll = ypr[2] * 180 / M_PI; 49 | 50 | //Output orientation (euler) 51 | Serial.print("Orientation: "); 52 | Serial.print(yaw); 53 | Serial.print(','); 54 | Serial.print(pitch); 55 | Serial.print(','); 56 | Serial.println(roll); 57 | 58 | // Output quaternions 59 | // Serial.print("Quaternion: "); 60 | // Serial.print(quat.w); 61 | // Serial.print(','); 62 | // Serial.print(quat.x); 63 | // Serial.print(','); 64 | // Serial.print(quat.y); 65 | // Serial.print(','); 66 | // Serial.println(quat.z); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /MPU6050_4_free-falling/MPU6050_4_free-falling.ino: -------------------------------------------------------------------------------- 1 | #include "MPU6050.h" 2 | 3 | MPU6050 mpu6050; 4 | 5 | void setup() { 6 | Serial.begin(38400); 7 | mpu6050.initialize(); 8 | 9 | mpu6050.setIntFreefallEnabled(true); 10 | mpu6050.setIntZeroMotionEnabled(false); 11 | mpu6050.setIntMotionEnabled(false); 12 | 13 | mpu6050.setAccelerometerPowerOnDelay(3); 14 | mpu6050.setDHPFMode(MPU6050_IMU::MPU6050_DHPF_5); 15 | 16 | mpu6050.setFreefallDetectionThreshold(17); 17 | mpu6050.setFreefallDetectionDuration(2); 18 | 19 | attachInterrupt(digitalPinToInterrupt(2), freeFallInt, RISING); 20 | } 21 | 22 | void loop() { 23 | 24 | } 25 | 26 | void freeFallInt(){ 27 | Serial.println("Free falling!"); 28 | } -------------------------------------------------------------------------------- /MPU6050_5_DMP_two_sensors/MPU6050_5_DMP_two_sensors.ino: -------------------------------------------------------------------------------- 1 | #include "I2Cdev.h" 2 | #include "MPU6050_6Axis_MotionApps612.h" 3 | #include "Wire.h" 4 | 5 | typedef struct{ 6 | Quaternion qat; 7 | VectorFloat gravity; 8 | float yaw; 9 | float pitch; 10 | float roll; 11 | } MPU6050_values; 12 | 13 | MPU6050_values mpu_1_values; 14 | MPU6050_values mpu_2_values; 15 | 16 | //To set secondary address set pin AD0 to high, otherwise both will have address conflict. 17 | MPU6050 mpu_1(0x68); 18 | MPU6050 mpu_2(0x69); 19 | 20 | //Both devices share same buffer. If for whatever reason you've decided 21 | //to use RTOS or interrupts you should create separate buffer per device 22 | //and avoid overwriting values. Current code is safe because it parses 23 | //values from buffer immediately. 24 | uint8_t dataBuffer[64]; 25 | 26 | void setup() { 27 | Wire.begin(); 28 | Wire.setClock(400000); 29 | Wire.setWireTimeout(3000, true); 30 | 31 | Serial.begin(115200); 32 | while (!Serial); 33 | 34 | Serial.println("\nSensor 1 Init:"); 35 | mpuInit(mpu_1); 36 | 37 | Serial.println("\nSensor 2 Init:"); 38 | mpuInit(mpu_2); 39 | 40 | 41 | delay(1000); 42 | } 43 | 44 | void loop() { 45 | 46 | if(mpu_1.dmpGetCurrentFIFOPacket(dataBuffer)) { 47 | mpuGetValues(mpu_1, mpu_1_values); 48 | 49 | Serial.print("Sensor 1 ypr: \t"); 50 | Serial.print(mpu_1_values.yaw); 51 | Serial.print("\t"); 52 | Serial.print(mpu_1_values.pitch); 53 | Serial.print("\t"); 54 | Serial.print(mpu_1_values.roll); 55 | Serial.println(); 56 | } 57 | 58 | if(mpu_2.dmpGetCurrentFIFOPacket(dataBuffer)) { 59 | mpuGetValues(mpu_2, mpu_2_values); 60 | 61 | Serial.print("Sensor 2 ypr:\t"); 62 | Serial.print(mpu_2_values.yaw); 63 | Serial.print("\t"); 64 | Serial.print(mpu_2_values.pitch); 65 | Serial.print("\t"); 66 | Serial.print(mpu_2_values.roll); 67 | Serial.println(); 68 | } 69 | 70 | } 71 | 72 | void mpuInit(MPU6050& device){ 73 | device.initialize(); 74 | device.dmpInitialize(); 75 | device.CalibrateAccel(6); 76 | device.CalibrateGyro(6); 77 | 78 | //device.PrintActiveOffsets(); 79 | 80 | // Serial.print("Testing connection: "); 81 | // Serial.println(device.testConnection()); 82 | 83 | device.setDMPEnabled(true); 84 | } 85 | 86 | void mpuGetValues(MPU6050& device, MPU6050_values& values){ 87 | float ypr[3]; 88 | device.dmpGetQuaternion(&values.qat, dataBuffer); 89 | device.dmpGetGravity(&values.gravity, &values.qat); 90 | device.dmpGetYawPitchRoll(ypr, &values.qat, &values.gravity); 91 | 92 | values.yaw = ypr[0] * 180 / M_PI; 93 | values.pitch = ypr[1] * 180 / M_PI; 94 | values.roll = ypr[2] * 180 / M_PI; 95 | } -------------------------------------------------------------------------------- /MinIMU-9_1_raw_values/MinIMU-9_1_raw_values.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | Adafruit_LSM6DS33 lsm6ds33; 6 | Adafruit_LIS3MDL lis3mdl; 7 | 8 | void setup() { 9 | Serial.begin(115200); 10 | //while (!Serial) delay(10); 11 | 12 | if (!lsm6ds33.begin_I2C(0x6B)) { 13 | Serial.println("LSM6DS connection error"); 14 | while(1); 15 | } 16 | 17 | if (!lis3mdl.begin_I2C(0x1E)) { 18 | Serial.println("LIS3MDL connection error"); 19 | while(1); 20 | } 21 | 22 | lsm6ds33.setAccelRange(LSM6DS_ACCEL_RANGE_2_G); 23 | lsm6ds33.setGyroRange(LSM6DS_GYRO_RANGE_250_DPS); 24 | lsm6ds33.setAccelDataRate(LSM6DS_RATE_104_HZ); 25 | lsm6ds33.setGyroDataRate(LSM6DS_RATE_104_HZ); 26 | 27 | lis3mdl.setRange(LIS3MDL_RANGE_4_GAUSS); 28 | lis3mdl.setPerformanceMode(LIS3MDL_MEDIUMMODE); 29 | lis3mdl.setOperationMode(LIS3MDL_CONTINUOUSMODE); 30 | lis3mdl.setDataRate(LIS3MDL_DATARATE_155_HZ); 31 | } 32 | 33 | void loop() { 34 | sensors_event_t accel; 35 | sensors_event_t gyro; 36 | sensors_event_t temp; 37 | sensors_event_t mag; 38 | 39 | lsm6ds33.getEvent(&accel, &gyro, &temp); 40 | lis3mdl.getEvent(&mag); 41 | 42 | Serial.print("Accel: "); 43 | Serial.print(accel.acceleration.x); 44 | Serial.print('\t'); 45 | Serial.print(accel.acceleration.y); 46 | Serial.print('\t'); 47 | Serial.print(accel.acceleration.z); 48 | Serial.print(" m/s^2 "); 49 | 50 | Serial.print("\tGyro: "); 51 | Serial.print(gyro.gyro.x); 52 | Serial.print('\t'); 53 | Serial.print(gyro.gyro.y); 54 | Serial.print('\t'); 55 | Serial.print(gyro.gyro.z); 56 | Serial.print(" radians/s "); 57 | 58 | Serial.print("\tMag: "); 59 | Serial.print(mag.magnetic.x); 60 | Serial.print('\t'); 61 | Serial.print(mag.magnetic.y); 62 | Serial.print('\t'); 63 | Serial.print(mag.magnetic.z); 64 | Serial.println(" uTesla "); 65 | 66 | delay(50); 67 | } 68 | -------------------------------------------------------------------------------- /MinIMU-9_2_motioncal_calibration/MinIMU-9_2_motioncal_calibration.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | Adafruit_LSM6DS33 lsm6ds33; 6 | Adafruit_LIS3MDL lis3mdl; 7 | 8 | void setup() { 9 | Serial.begin(115200); 10 | //while (!Serial) delay(10); 11 | 12 | if (!lsm6ds33.begin_I2C(0x6B)) { 13 | Serial.println("LSM6DS connection error"); 14 | while(1); 15 | } 16 | 17 | if (!lis3mdl.begin_I2C(0x1E)) { 18 | Serial.println("LIS3MDL connection error"); 19 | while(1); 20 | } 21 | 22 | lsm6ds33.setAccelRange(LSM6DS_ACCEL_RANGE_2_G); 23 | lsm6ds33.setGyroRange(LSM6DS_GYRO_RANGE_250_DPS); 24 | lsm6ds33.setAccelDataRate(LSM6DS_RATE_104_HZ); 25 | lsm6ds33.setGyroDataRate(LSM6DS_RATE_104_HZ); 26 | 27 | lis3mdl.setRange(LIS3MDL_RANGE_4_GAUSS); 28 | lis3mdl.setPerformanceMode(LIS3MDL_MEDIUMMODE); 29 | lis3mdl.setOperationMode(LIS3MDL_CONTINUOUSMODE); 30 | lis3mdl.setDataRate(LIS3MDL_DATARATE_155_HZ); 31 | } 32 | 33 | void loop() { 34 | sensors_event_t accel; 35 | sensors_event_t gyro; 36 | sensors_event_t temp; 37 | sensors_event_t mag; 38 | 39 | lsm6ds33.getEvent(&accel, &gyro, &temp); 40 | lis3mdl.getEvent(&mag); 41 | 42 | Serial.print("Raw:"); 43 | Serial.print(int(accel.acceleration.x*8192/9.8)); Serial.print(","); 44 | Serial.print(int(accel.acceleration.y*8192/9.8)); Serial.print(","); 45 | Serial.print(int(accel.acceleration.z*8192/9.8)); Serial.print(","); 46 | Serial.print(int(gyro.gyro.x*57.2957795*16)); Serial.print(","); 47 | Serial.print(int(gyro.gyro.y*57.2957795*16)); Serial.print(","); 48 | Serial.print(int(gyro.gyro.z*57.2957795*16)); Serial.print(","); 49 | Serial.print(int(mag.magnetic.x*10)); Serial.print(","); 50 | Serial.print(int(mag.magnetic.y*10)); Serial.print(","); 51 | Serial.print(int(mag.magnetic.z*10)); Serial.println(""); 52 | 53 | delay(10); 54 | } 55 | -------------------------------------------------------------------------------- /MinIMU-9_3_accel_gyro_calibration/MinIMU-9_3_accel_gyro_calibration.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | Adafruit_LSM6DS33 lsm6ds33; 6 | Adafruit_LIS3MDL lis3mdl; 7 | 8 | void setup() { 9 | Serial.begin(115200); 10 | //while (!Serial) delay(10); 11 | 12 | if (!lsm6ds33.begin_I2C(0x6B)) { 13 | Serial.println("LSM6DS connection error"); 14 | while(1); 15 | } 16 | 17 | if (!lis3mdl.begin_I2C(0x1E)) { 18 | Serial.println("LIS3MDL connection error"); 19 | while(1); 20 | } 21 | 22 | lsm6ds33.setAccelRange(LSM6DS_ACCEL_RANGE_2_G); 23 | lsm6ds33.setGyroRange(LSM6DS_GYRO_RANGE_250_DPS); 24 | lsm6ds33.setAccelDataRate(LSM6DS_RATE_104_HZ); 25 | lsm6ds33.setGyroDataRate(LSM6DS_RATE_104_HZ); 26 | 27 | lis3mdl.setRange(LIS3MDL_RANGE_4_GAUSS); 28 | lis3mdl.setPerformanceMode(LIS3MDL_MEDIUMMODE); 29 | lis3mdl.setOperationMode(LIS3MDL_CONTINUOUSMODE); 30 | lis3mdl.setDataRate(LIS3MDL_DATARATE_155_HZ); 31 | 32 | float totalAccelX = 0; 33 | float totalAccelY = 0; 34 | float totalAccelZ = 0; 35 | float totalGyroX = 0; 36 | float totalGyroY = 0; 37 | float totalGyroZ = 0; 38 | 39 | int samples = 10000; 40 | 41 | Serial.println("Starting Calibration..."); 42 | Serial.println("Do not move the device"); 43 | delay(4000); 44 | Serial.println("Calibrating..."); 45 | 46 | for(int i=0; i < samples; i++){ 47 | 48 | sensors_event_t accel; 49 | sensors_event_t gyro; 50 | sensors_event_t temp; 51 | lsm6ds33.getEvent(&accel, &gyro, &temp); 52 | 53 | totalGyroX += gyro.gyro.x; 54 | totalGyroY += gyro.gyro.y; 55 | totalGyroZ += gyro.gyro.z; 56 | 57 | totalAccelX += accel.acceleration.x; 58 | totalAccelY += accel.acceleration.y; 59 | totalAccelZ += accel.acceleration.z - 9.806; 60 | 61 | delay(1); 62 | } 63 | 64 | float accelMeanX = totalAccelX / samples; 65 | float accelMeanY = totalAccelY / samples; 66 | float accelMeanZ = totalAccelZ / samples; 67 | 68 | float gyroMeanX = totalGyroX / samples; 69 | float gyroMeanY = totalGyroY / samples; 70 | float gyroMeanZ = totalGyroZ / samples; 71 | 72 | 73 | Serial.println("Accel XYZ offset:"); 74 | Serial.print(accelMeanX); 75 | Serial.print('\t'); 76 | Serial.print(accelMeanY); 77 | Serial.print('\t'); 78 | Serial.println(accelMeanZ); 79 | 80 | Serial.println("Gyro XYZ offset:"); 81 | Serial.print(gyroMeanX); 82 | Serial.print('\t'); 83 | Serial.print(gyroMeanY); 84 | Serial.print('\t'); 85 | Serial.println(gyroMeanZ); 86 | Serial.println(' '); 87 | } 88 | 89 | void loop() { 90 | 91 | } 92 | 93 | -------------------------------------------------------------------------------- /MinIMU-9_4_AHRS/MinIMU-9_4_AHRS.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | Adafruit_LSM6DS33 lsm6ds33; 7 | Adafruit_LIS3MDL lis3mdl; 8 | 9 | Adafruit_Mahony filter; 10 | 11 | int updateRate = 50; 12 | int printEveryUpdate = 3; 13 | 14 | unsigned long timestamp = 0; 15 | int counter = 0; 16 | 17 | float mag_hardIron[3] = {-3.59, -23.27, 74.52}; 18 | float mag_softIron[9] = { 19 | 0.969, 0.015, 0.038, 20 | 0.015, 1.022, -0.016, 21 | 0.038, -0.016, 1.012 22 | }; 23 | float accel_calibration[3] = {-0.33, -0.61, 0.16}; 24 | float gyro_calibration[3] = {0.05, -0.06, -0.08}; 25 | 26 | void setup() { 27 | Serial.begin(115200); 28 | //while (!Serial) delay(10); 29 | 30 | if (!lsm6ds33.begin_I2C(0x6B)) { 31 | Serial.println("LSM6DS connection error"); 32 | while(1); 33 | } 34 | 35 | if (!lis3mdl.begin_I2C(0x1E)) { 36 | Serial.println("LIS3MDL connection error"); 37 | while(1); 38 | } 39 | 40 | lsm6ds33.setAccelRange(LSM6DS_ACCEL_RANGE_2_G); 41 | lsm6ds33.setGyroRange(LSM6DS_GYRO_RANGE_250_DPS); 42 | lsm6ds33.setAccelDataRate(LSM6DS_RATE_104_HZ); 43 | lsm6ds33.setGyroDataRate(LSM6DS_RATE_104_HZ); 44 | 45 | lis3mdl.setRange(LIS3MDL_RANGE_4_GAUSS); 46 | lis3mdl.setPerformanceMode(LIS3MDL_MEDIUMMODE); 47 | lis3mdl.setOperationMode(LIS3MDL_CONTINUOUSMODE); 48 | lis3mdl.setDataRate(LIS3MDL_DATARATE_80_HZ); 49 | 50 | filter.begin(updateRate); 51 | } 52 | 53 | float ax, ay, az, gx, gy, gz, mx, my, mz = 0; 54 | sensors_event_t accel; 55 | sensors_event_t gyro; 56 | sensors_event_t temp; 57 | sensors_event_t mag; 58 | 59 | void loop() { 60 | if(millis() - timestamp >= 1000 / updateRate){ 61 | 62 | lsm6ds33.getEvent(&accel, &gyro, &temp); 63 | lis3mdl.getEvent(&mag); 64 | 65 | ax = accel.acceleration.x; 66 | ay = accel.acceleration.y; 67 | az = accel.acceleration.z; 68 | gx = gyro.gyro.x; 69 | gy = gyro.gyro.y; 70 | gz = gyro.gyro.z; 71 | mx = mag.magnetic.x; 72 | my = mag.magnetic.y; 73 | mz = mag.magnetic.z; 74 | 75 | calibrate(&ax, &ay, &az, &gx, &gy, &gz, &mx, &my, &mz); 76 | 77 | filter.update( 78 | gx * 57.2957795, 79 | gy * 57.2957795, 80 | gz * 57.2957795, 81 | ax * 0.101972, 82 | ay * 0.101972, 83 | az * 0.101972, 84 | mx, 85 | my, 86 | mz 87 | ); 88 | 89 | timestamp = millis(); 90 | 91 | counter++; 92 | 93 | if(counter == printEveryUpdate){ 94 | 95 | float w, x, y, z; 96 | filter.getQuaternion(&w,&x,&y,&z); 97 | 98 | Serial.print("Quaternion: "); 99 | Serial.print(w); 100 | Serial.print(','); 101 | Serial.print(x); 102 | Serial.print(','); 103 | Serial.print(y); 104 | Serial.print(','); 105 | Serial.println(z); 106 | 107 | // float roll = filter.getRoll(); 108 | // float pitch = filter.getPitch(); 109 | // float yaw = filter.getYaw(); 110 | 111 | // Serial.print("Orientation: "); 112 | // Serial.print(yaw); 113 | // Serial.print(','); 114 | // Serial.print(pitch); 115 | // Serial.print(','); 116 | // Serial.println(roll); 117 | 118 | counter = 0; 119 | } 120 | } 121 | } 122 | 123 | void calibrate(float *ax, float *ay, float *az, float *gx, float *gy, float *gz, float *mx, float *my, float *mz){ 124 | *ax -= accel_calibration[0]; 125 | *ay -= accel_calibration[1]; 126 | *az -= accel_calibration[2]; 127 | 128 | *gx -= gyro_calibration[0]; 129 | *gy -= gyro_calibration[1]; 130 | *gz -= gyro_calibration[2]; 131 | 132 | *mx -= mag_hardIron[0]; 133 | *my -= mag_hardIron[1]; 134 | *mz -= mag_hardIron[2]; 135 | 136 | *mx = *mx * mag_softIron[0] + *my * mag_softIron[1] + *mz * mag_softIron[2]; 137 | *my = *mx * mag_softIron[3] + *my * mag_softIron[4] + *mz * mag_softIron[5]; 138 | *mz = *mx * mag_softIron[6] + *my * mag_softIron[7] + *mz * mag_softIron[8]; 139 | } 140 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **A series of tests made together with students for the purpose of one of our semester project at Industrial Design faculty. I decided to put it online as reference for future projects that require orientation sensing.** 2 | 3 | ## Sensors tested: 4 | ### 6-dof (Gyroscope + Accelerometer): 5 | - **MPU-6050** 6 | - **ISM330DHCX** 7 | 8 | ### 9-dof (Gyroscope + Accelerometer + Magnetometer) 9 | - **Pololu MinIMU-9 v5 9DOF** (LSM6DS33 + LIS3MDL) 10 | - **LSM9DS1** in Arduino Nano 33 BLE 11 | - **BNO055** (Adafruit module) 12 | - *ICM-20948 *Not included, tested as 6-dof in 2021* 13 | 14 | ## Methods of test: 15 | - Tests were made in similar conditions. 16 | - Tests were made with sensor grabbed in hand for basic gestures and then whole device inside enclosure (indirect movement). 17 | - Slow and fast movement. 18 | - Checking edge angles to cause gimbal lock. 19 | - We used similar calibration method for magnetometer. MotionCal is available for download here: https://www.pjrc.com/store/prop_shield.html Accelerometer and Gyro is not working here. 20 | - Testing with Adafruit app. https://adafruit.github.io/Adafruit_WebSerial_3DModelViewer/. Be aware of format how data must be send `Orientation: x,y,z` or `Quaternion: w,x,y,z`. 21 | 22 | ## Notes: 23 | *I've included some further explanations - Ernest* 24 | - **6-dof IMUs are enough to measure tilt angle. For yaw/Z-axis rotation 9-dof (with magnetometer) or precise gyro calculation is necessary.** 25 | - Always include calibration code. 26 | - **Gyroscope data integration requires matching with defined frequency and updating data when new measurement is available. Period.** *It's difficult to do properly with Arduino libraries. Ideally we should avoid any delays and use interrupts. Without proper handling it just won't work. Depending on libraries it's handled differently. Our projects rarely requires advanced usage. It's an issue if your project requires all three axes or we really have to handle the z-axis.* 27 | - **Integration problems can be easily spotted if we rotate by 90 degrees but the axis moves by different angle and error accumulates over time.** (it integrates too much/few samples per second therefore error grows over time) It's issue with Z-axis which relies on gyroscope (unless we use magnetometer). Tilt measurements are dependent on accelerometer which doesn't drift but have noise in rapid movements. 28 | - **Drifting in gyroscope can be spotted if our axis moves by some small angle over time without any movement.** Technically drifting can't be avoided but we can marginalize it by proper callibration or compensation over time if device is running for hours. Usually libraries handle the compensation with accelerometer. 29 | - **Stick to quaternions in your code for calculations, eventually convert to euler angles to avoid gimbal lock.** *Quaternions are 4 dimensional (w, x, y, z) representations of 3 dimensional rotations and are less intuitive to read but are better for handling continuous movement.* 30 | - *Raw values can be useful to detect if object is shaking, is tapped, moving or tilting toward one of its side.* **Sometimes you don't have to compute the euler angles.** *Furthermore, you can use a simpler complementary filter without involving gyroscope.* 31 | - **Raw values aren't in SI units** *(conversion is necessary for AHRS libraries), through some libraries we get the SI units by default but some of them calculate angles in radians. Therfore, for example in BNO055 code we multiply by 57.2957795 to get degrees. Furthermore, accelerometer values sometimes are described in G. 1G is equal to gravity acceleration approx 9.8m/s^2. The raw value is scaled to match range of intiger depending on what G sensitivity we set. For example: 2G sensitivity will be scaled from -2G - 2G to -32768 - 32767. The 1G, which is gravity should show approx +/-16k on flat surface on one axis. Gyroscope sensitivity works in similar way.* 32 | - *For accelerometer noise can be reduced by averaging values or additional filtering.* 33 | - *We still didn't figure out how automatic magnetometer calibration routine should be done.* 34 | - **Any 9-dof AHRS algorithm is too demanding for Arduino UNO** *Unless it's the only thing you want to do and not be very precise. 6-dof IMU can work fine.* 35 | 36 | # Sensors usage: 37 | ## MPU-6050 38 | **We are using the commonly used MPU6050 library. The Adafruit equivalent is cumbersome to configure.** 39 | ### Calibration: 40 | Open the *MPU6050_2_raw_values_offsets* example. There are two ways to calibrate the sensor: 41 | - Use `CalibrateAccel` and `CalibrateGyro` to do that automatically. 42 | - Hard-coding: Place the device on a flat surface. Run the calibration code from MPU6050 library (*IMU_Zero*). Write down the averaged offsets and use these offsets in our sketch (eventually deleting the auto calibration). 43 | ### Angles: 44 | **Digital Motion Processor (DMP)** calculates the euler angles or quaternions directly in sensor without wasting resources on your Arduino. You can use the library's *MPU6050_DMP6_using_DMP_V6v12* but you need to add *`Wire.setWireTimeout(3000, true);`* after *`Wire.setClock(400000);`* to prevent sketch from randomly freezing. *We also prepared a simplified sketch without unnecessary configurations.* 45 | 46 | **Adafruit Unified sensors and AHRS library** requires its Adafruit Calibration Library. We found it more difficult to use and measurements are worse than DMP. 47 | 48 | ### Notes: 49 | - **MPU-6050 is the cheapest among all. Usually cheapest sensors have significantly lower quality but not in this case thanks to DMP. During our classes don't buy the expensive MPU-6050 variants, the cheap one is enough.** 50 | - *For prototyping, in most cases MPU-6050 is enough if you need to measure tilt angle without relative heading (it doesn't have a magnetometer to do so). The MPU-9250 and ICM-20948 have magnetometer included.* 51 | - **DMP computes Euler angles or quaternions without wasting resources of your Arduino.** 52 | - *Using Mahony filter is not even close to quality of DMP.* 53 | - ***ICM-20948** *is a successor to MPU-6050/MPU-9250. Sparkfun has a library that leverages the same DMP algorithm for 9-dof. The problem is product availability, however there are concerns that Invensense doesn't use 9-dof for its DMP. We can't investigate it until we get one.* 54 | 55 | ## ISM330DHCX 56 | ### Calibration: 57 | 1. Use **Adafruit sensorlab** *gyro_zerorate_simplecal* to calibrate gyroscope. It won't store the values on EEPROM or SD card. *It takes a long time to compile.* 58 | 2. Write the detected offsets into the *ISM330DHCX_2_IMU* code. Remember that values are in rad/s so we have to do multiplication. 59 | 60 | ### Angles: 61 | We shall use the Adafruit AHRS library. Remember that without the magnetometer only roll and pitch makes sense. 62 | 63 | ### Notes: 64 | - *ISM330DHCX is an industrial-grade version of LSM6DSOX with many enhancements.* 65 | - *ISM330DHCX has built-in tilt direction, double tap, pedometer and free falling detection.* 66 | - *ISM330DHCX doesn't contain DMP but has Finite State Machine and Machine Learning Core for gesture recognition. ST provides iNEMO engine AHRS fusion algorithm but unfortunately it seems they removed it.* **Anyway, advanced features aren't possible to use on Arduino platform so it's not worth buying in our case.** 67 | - *Data is less noisy compared to MPU-6050.* 68 | - *It's compatible with Adafruit LSM6DS library. Unfortunately things like pedometer and double tap aren't implemented. I made my own implementation but it requires basic understanding of bare-metal programming.* 69 | 70 | ## Pololu MinIMU-9 v5 9DOF 71 | **You can use the adafruit library but the i2c address must be changed to 0x6b `lsm6ds33.begin_I2C(0x6b)` and `lis3mdl.begin_I2C(0x1E)`.** 72 | 73 | The magnetometer is a separate sensor on this board. 74 | 75 | ### Calibration: 76 | 1. Magnetometer: MotionCal. Upload the calibration code and open the app. 77 | 2. Accelerometer and Gyroscope: calibrated via included code. 78 | 79 | ### Angles: 80 | There are libraries for these sensors from Pololu, Sparkfun and Adafruit.We will stick to Adafruit as we are using Adafruit AHRS library. 81 | 82 | ### Notes: 83 | - *It's the same sensor you have on Adafruit Feather BLE.* 84 | - *LIS3MDL magnetometer frequency is faster than magnetometer in LSM9DS1.* 85 | - *You can use Adafruit library to use pedometer.* 86 | - *Pololu libraries are very basic, there is an AHRS example but there are more steps to make it running https://github.com/pololu/minimu-9-ahrs-arduino* 87 | 88 | ## Arduino Nano 33 BLE (LSM9DS1) 89 | ### Calibration: 90 | **Because we are using Arduino Nano 33 BLE we had to prepare a custom code to communicate with Motioncal and to calibrate accelerometer and gyroscope (Adafruit library won't detect the sensor). If you are using Adafruit LSM9DS1 (or LSM9DS0) as a standalone module you can use Adafruit Unified Sensor Library** 91 | 1. Magnetometer: MotionCal. Upload the calibration code and open the app. 92 | 2. Accelerometer and Gyroscope: calibrated via included code. 93 | 3. Write these values into *LSM9DS1_4_AHRS*. 94 | 95 | ### Angles: 96 | We are using a sketch which sends quaternions to Adafruit web app https://adafruit.github.io/Adafruit_WebSerial_3DModelViewer/ You can easily change to euler angles if you whish. 97 | 98 | **The position becomes stable and eventually correct after a short time. Achieving zero-drift correct orientation seems impossible during frequent movements.** 99 | 100 | **The problem is the Yaw value - try rotating around Z-axis and after a few 360 degree rotations you see how delayed the movement is.** Magnetometer is set to 20hz frequency which is slow for AHRS. We've changed its Output Data Rate to 80hz and fast ODR `LSM9DS1_CTRL_REG1_M` and the performance mode. Documentation https://www.st.com/resource/en/datasheet/lsm9ds1.pdf 101 | 102 | **Different frequency/update ratio somehow fixes the issue, check the difference in v2 version.** 103 | 104 | Furthermore, LSM9DS1 magnetometer has different direction of X and Y axes. *You can test them on your own but to be honest it's impossible to see it on that app's Bunny which ones are correct.* 105 | 106 | **Things that could improve the measurements:** 107 | - Improve calibration, apply something to additionaly reduce noise? 108 | - **Measurement frequency ratio? Read notes on Gyro integration problems.** 109 | - Different AHRS algorithm? Adafruit contains Mahony, Madgwick and NXP Sensor Fusion. 110 | 111 | ### Notes: 112 | - *Great all-in-one solution if you have to use Bluetooth LE. The Adafruit Feather BLE can be an alternative but I remember that BT worked significantly slower. On the other hand, Feather BLE includes Li-Po battery circuit.* 113 | - *The Arduino LSM9DS1 library is mediocre, it lacks features except getting the data and it doesn't play well with Adafruit Libraries.* 114 | - *LSM9DS1 documentation misleads it has click/double click detection. The sensor has movement and tilt detection available.* 115 | 116 | ## BNO055 117 | ### Calibration: 118 | **Calibration is done internally on startup and adapts on runtime.** You can print calibration status, numbers between 0-3 are calibration grades. 119 | 120 | ### Angles: 121 | Reading quaternions is recommended by manufacturer. Later you can convert them to euler angles. 122 | 123 | ### Notes: 124 | - **For demanding projects, to be honest, if you can and your project can afford it, just buy BNO055 (or equivalent) sensor module for Arduino. The real-time calibration and build-in AHRS fusion algorithm saves a lot of headaches and time.** *("Personally I don't understand why students never value their time so they waste it on figuring things too complex for them or buying the cheapest sensor shipped from China wasting one or two classes to get it while price difference isn't worth it at all. It's a problem with any kind of project." - Ernest)* 125 | - Quality of measurements especially matter when you have to frequently change device orienation axis, based on my observation, other sensors mentioned just lose their orientation. However, for basic scenarios there is no reason to buy it.* 126 | - *This particular version doesn't have any double tap, gesture nor pedometer sensor.* 127 | - **Computations are made internally so it doesn't waste Arduino resources.** --------------------------------------------------------------------------------