Skip to content

Instantly share code, notes, and snippets.

@vongomben
Created November 5, 2023 09:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vongomben/1bb6bd4b5ae6227a78d9dff49fa2f1c2 to your computer and use it in GitHub Desktop.
Save vongomben/1bb6bd4b5ae6227a78d9dff49fa2f1c2 to your computer and use it in GitHub Desktop.
/*
ArduinoBLE IMU Angles example
This example creates a BLE peripheral with service that contains a
characteristic to represents the reading of the potentiometer.
Arduino Nano 33 Iot
This example code is in the public domain.
works with this with yaw pitch and roll
https://editor.p5js.org/vongomben/sketches/e-N5ybvyh
*/
#include <ArduinoBLE.h>
#include <Arduino_LSM6DS3.h> // change to Arduino_LSM6DS3.h for Nano 33 IoT or Uno WiFi Rev 2
#include <Wire.h>
float accelX, accelY, accelZ, // units m/s/s i.e. accelZ if often 9.8 (gravity)
gyroX, gyroY, gyroZ, // units dps (degrees per second)
gyroDriftX, gyroDriftY, gyroDriftZ, // units dps
gyroRoll, gyroPitch, gyroYaw, // units degrees (expect major drift)
gyroCorrectedRoll, gyroCorrectedPitch, gyroCorrectedYaw, // units degrees (expect minor drift)
accRoll, accPitch, accYaw, // units degrees (roll and pitch noisy, yaw not possible)
complementaryRoll, complementaryPitch, complementaryYaw; // units degrees (excellent roll, pitch, yaw minor drift)
long lastTime;
long lastInterval;
float angles[3];
BLEService sensorService("19B10010-E8F2-537E-4F6C-D104768A1214"); // create service
BLECharacteristic AngleCharacteristic ("19B10013-E8F2-537E-4F6C-D104768A1214", BLERead | BLENotify, sizeof(float) * 3);
void setup() {
Serial.begin(9600);
//while (!Serial);
if (!IMU.begin()) {
Serial.println("Failed to initialize IMU!");
while (1);
}
calibrateIMU(250, 250);
lastTime = micros();
if (!BLE.begin()) {
Serial.println("Failed to initialize IMU!");
while (1);
}
// begin initialization
if (!BLE.begin()) {
Serial.println("starting BLE failed!");
while (1);
}
// set the local name peripheral advertises
BLE.setLocalName("IMU");
// set the UUID for the service this peripheral advertises:
BLE.setAdvertisedService(sensorService);
// add the characteristics to the service
sensorService.addCharacteristic(AngleCharacteristic);
// add the service
BLE.addService(sensorService);
// start advertising
BLE.advertise();
Serial.println("Bluetooth device active, waiting for connections...");
}
/*
the gyro's x,y,z values drift by a steady amount. if we measure this when arduino is still
we can correct the drift when doing real measurements later
*/
void calibrateIMU(int delayMillis, int calibrationMillis) {
int calibrationCount = 0;
delay(delayMillis); // to avoid shakes after pressing reset button
float sumX, sumY, sumZ;
int startTime = millis();
while (millis() < startTime + calibrationMillis) {
if (readIMU()) {
// in an ideal world gyroX/Y/Z == 0, anything higher or lower represents drift
sumX += gyroX;
sumY += gyroY;
sumZ += gyroZ;
calibrationCount++;
}
}
if (calibrationCount == 0) {
Serial.println("Failed to calibrate");
}
gyroDriftX = sumX / calibrationCount;
gyroDriftY = sumY / calibrationCount;
gyroDriftZ = sumZ / calibrationCount;
}
/**
Read accel and gyro data.
returns true if value is 'new' and false if IMU is returning old cached data
*/
bool readIMU() {
if (IMU.accelerationAvailable() && IMU.gyroscopeAvailable() ) {
IMU.readAcceleration(accelX, accelY, accelZ);
IMU.readGyroscope(gyroX, gyroY, gyroZ);
return true;
}
return false;
}
void loop() {
// poll for BLE events
BLE.poll();
if (readIMU()) {
long currentTime = micros();
lastInterval = currentTime - lastTime; // expecting this to be ~104Hz +- 4%
lastTime = currentTime;
doCalculations();
//printCalculations();
}
angles[0] = complementaryRoll;
angles[1] = complementaryPitch;
angles[2] = complementaryYaw;
AngleCharacteristic.writeValue(angles, sizeof(angles));
for (int i = 0; i < sizeof(angles) / sizeof(float); i++) {
Serial.print(angles[i]);
}
Serial.println();
}
/**
I'm expecting, over time, the Arduino_LSM6DS3.h will add functions to do most of this,
but as of 1.0.0 this was missing.
*/
void doCalculations() {
accRoll = atan2(accelY, accelZ) * 180 / M_PI;
accPitch = atan2(-accelX, sqrt(accelY * accelY + accelZ * accelZ)) * 180 / M_PI;
float lastFrequency = (float) 1000000.0 / lastInterval;
gyroRoll = gyroRoll + (gyroX / lastFrequency);
gyroPitch = gyroPitch + (gyroY / lastFrequency);
gyroYaw = gyroYaw + (gyroZ / lastFrequency);
gyroCorrectedRoll = gyroCorrectedRoll + ((gyroX - gyroDriftX) / lastFrequency);
gyroCorrectedPitch = gyroCorrectedPitch + ((gyroY - gyroDriftY) / lastFrequency);
gyroCorrectedYaw = gyroCorrectedYaw + ((gyroZ - gyroDriftZ) / lastFrequency);
complementaryRoll = complementaryRoll + ((gyroX - gyroDriftX) / lastFrequency);
complementaryPitch = complementaryPitch + ((gyroY - gyroDriftY) / lastFrequency);
complementaryYaw = complementaryYaw + ((gyroZ - gyroDriftZ) / lastFrequency);
complementaryRoll = 0.98 * complementaryRoll + 0.02 * accRoll;
complementaryPitch = 0.98 * complementaryPitch + 0.02 * accPitch;
}
/**
This comma separated format is best 'viewed' using 'serial plotter' or processing.org client (see ./processing/RollPitchYaw3d.pde example)
*/
void printCalculations() {
/* Serial.print(gyroRoll);
Serial.print(',');
Serial.print(gyroPitch);
Serial.print(',');
Serial.print(gyroYaw);
Serial.print(',');
Serial.print(gyroCorrectedRoll);
Serial.print(',');
Serial.print(gyroCorrectedPitch);
Serial.print(',');
Serial.print(gyroCorrectedYaw);
Serial.print(',');
Serial.print(accRoll);
Serial.print(',');
Serial.print(accPitch);
Serial.print(',');
Serial.print(accYaw);
Serial.print(',');
*/
Serial.print(complementaryRoll);
Serial.print(',');
Serial.print(complementaryPitch);
Serial.print(',');
Serial.print((int)complementaryYaw);
Serial.println("");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment