New to Arduino: Adafruit Feather M0 + Neopixel + MPU6050

Hello,

I need some help explaining/guiding how to send and receive MPU6050 accelerometer data with Adafruit Feather M0 to work with a NeoPixel FeatherWing.

Trying to run the code and calibration script calculating an angle based on the two axis changes that can be detected by the led. However, I am trying to understand if I am over complicating my script, or I missed a step when calibrating the clock and data translation between the i2c and MO.

an issue I had at first was a: TWBR error ???
I read from the Adafruit page something about adapting the script to include: TinyWireM.begin(); on line 143

After I done this, I started getting this compiling error on line 30:
30 | #include <util/delay.h>
| ^~~~~~~~~~~~~~

Again i'm trying to learn, any help or feedback would be greatly appreciated.

Thank you!

#include <TinyWireM.h>
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
 #include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif



#define NEOPIXED_CONTROL_PIN 6
#define NUM_LEDS 32

#if defined(ARDUINO_SAMD_ZERO) && defined(SERIAL_PORT_USBVIRTUAL)
  // Required for Serial on Zero based boards
  #define Serial SERIAL_PORT_USBVIRTUAL
#endif

const int MAX_ANGLE = 45;
const int LED_OFFSET = 12;

MPU6050 mpu;
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, NEOPIXED_CONTROL_PIN, NEO_RBG + NEO_KHZ800);
unsigned long lastPrintTime = 0;


bool initialization = false;  // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer
Quaternion q;           // [w, x, y, z]         quaternion container
VectorFloat gravity;    // [x, y, z]            gravity vector
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector
volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high

void setup() 
{

  #if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
  clock_prescale_set(clock_div_1);
#endif
  // END of Trinket-specific code.

  pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)

    Serial.begin(9600);
    Serial.println("Program started");
    initialization = initializeGyroscope();  
    strip.begin(); 
}

void loop() 
{
    if (!initialization) {
        return;
    }
    mpuInterrupt = false;
    mpuIntStatus = mpu.getIntStatus();
    fifoCount = mpu.getFIFOCount();
    if (hasFifoOverflown(mpuIntStatus, fifoCount)) {
        mpu.resetFIFO();
        return;
    }
    if (mpuIntStatus & 0x02) {
        while (fifoCount < packetSize) {
            fifoCount = mpu.getFIFOCount();
        }
        mpu.getFIFOBytes(fifoBuffer, packetSize);        
        fifoCount -= packetSize;
        mpu.dmpGetQuaternion(&q, fifoBuffer);
        mpu.dmpGetGravity(&gravity, &q);
        mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
        redrawLeds(ypr[0] * 180/M_PI, ypr[1] * 180/M_PI, ypr[2] * 180/M_PI);
    }
}

boolean hasFifoOverflown(int mpuIntStatus, int fifoCount) 
{
    return mpuIntStatus & 0x10 || fifoCount == 1024;
}

void redrawLeds(int x, int y, int z)
{  
    x = constrain(x, -1 * MAX_ANGLE, MAX_ANGLE);
    y = constrain(y, -1 * MAX_ANGLE, MAX_ANGLE);
    if (y < 0 and z > 0) {
        lightLeds(y, z, 0, 5, 0, 89);      
    } else if (y < 0 and z < 0) {
        lightLeds(y, z, 6, 12, 89, 0);  
    } else if (y > 0 and z < 0) {
        lightLeds(y, z, 13, 19, 0, 89);     
    } else if (y > 0 and z > 0) {
        lightLeds(y, z, 20, 24, 89, 0);     
    }
}


void lightLeds(int x, int y, int fromLedPosition, int toLedPosition, int fromAngle, int toAngle) 
{
    double angle = (atan((double) abs(x) / (double) abs (y)) * 4068) / 71;
    int ledNr = map(angle, fromAngle, toAngle, fromLedPosition, toLedPosition);
    printDebug(x, y, ledNr, angle);  
    uint32_t color;

    for (int i=0; i < NUM_LEDS; i++) {
        color = strip.Color(0, 0, 0);
        if (i == ledNr) {
           color = strip.Color(0, 180, 0);
        } else if (i == ledNr - 1) {
           color = strip.Color(0, 5, 0);
        }
        strip.setPixelColor(normalizeLedPosition(i), color); 
        strip.show();
    }  
}

int normalizeLedPosition(int position)
{
    if (NUM_LEDS > position + LED_OFFSET) {
        return position + LED_OFFSET;
    }

    return position + LED_OFFSET - NUM_LEDS;
}

void printDebug(int y, int z, int lightLed, int angle)
{
    if (millis() - lastPrintTime < 500) {
        return;
    }
    Serial.print("a=");Serial.print(angle);Serial.print("; ");
    Serial.print("ll=");Serial.print(lightLed);Serial.print("; ");
    Serial.print("y=");Serial.print(y);Serial.print("; ");
    Serial.print("z=");Serial.print(z);Serial.println("; ");
    lastPrintTime = millis();
}


bool initializeGyroscope() {
    TinyWireM.begin();
    TWBR = 24;  
    mpu.initialize();
    Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
    Serial.println(F("Initializing DMP..."));
    devStatus = mpu.dmpInitialize();
    mpu.setXGyroOffset(220);
    mpu.setYGyroOffset(76);
    mpu.setZGyroOffset(-85);
    mpu.setZAccelOffset(1788);
    if (devStatus != 0) {
        Serial.print(F("DMP Initialization failed (code "));Serial.println(devStatus);
        return false;
    }
    mpu.setDMPEnabled(true);
    Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
    attachInterrupt(0, dmpDataReady, RISING);
    mpuIntStatus = mpu.getIntStatus();
    Serial.println(F("DMP ready! Waiting for first interrupt..."));
    packetSize = mpu.dmpGetFIFOPacketSize();
    
    return true;
}

void dmpDataReady() 
{
    mpuInterrupt = true;
}

These calls are specific to using an ATtiny microprocessor and have no place your scheme.

This simple example code is all that is required to get tilt angles from the MPU-6050, using the Arduino standard Wire (I2C) library.

// minimal MPU-6050 tilt and roll (sjr). Works with MPU-9250 too.
// works perfectly with GY-521, pitch and roll signs agree with arrows on sensor module 7/2019
//
// Tested with 3.3V eBay Pro Mini with no external pullups on I2C bus (worked with internal pullups)
// Add 4.7K pullup resistors to 3.3V if required. A4 = SDA, A5 = SCL

#include<Wire.h>
const int MPU_addr1 = 0x68;
float xa, ya, za, roll, pitch;

void setup() {

  Wire.begin();                                      //begin the wire communication
  Wire.beginTransmission(MPU_addr1);                 //begin, send the slave adress (in this case 68)
  Wire.write(0x6B);                                  //make the reset (place a 0 into the 6B register)
  Wire.write(0);
  Wire.endTransmission(true);                        //end the transmission
  Serial.begin(9600);
}

void loop() {

  Wire.beginTransmission(MPU_addr1);
  Wire.write(0x3B);  //send starting register address, accelerometer high byte
  Wire.endTransmission(false); //restart for read
  Wire.requestFrom(MPU_addr1, 6); //get six bytes accelerometer data
  int t = Wire.read();
  xa = (t << 8) | Wire.read();
  t = Wire.read();
  ya = (t << 8) | Wire.read();
  t = Wire.read();
  za = (t << 8) | Wire.read();
// formula from https://wiki.dfrobot.com/How_to_Use_a_Three-Axis_Accelerometer_for_Tilt_Sensing
  roll = atan2(ya , za) * 180.0 / PI;
  pitch = atan2(-xa , sqrt(ya * ya + za * za)) * 180.0 / PI; //account for roll already applied

  Serial.print("roll = ");
  Serial.print(roll,1);
  Serial.print(", pitch = ");
  Serial.println(pitch,1);
  delay(400);
}

This is specific to Arduino AVR processors, and again, has no place in your scheme:
30 | #include <util/delay.h>

Thank you for your quick response to my topic.
The example code provided for the MPU6050 worked perfectly with connecting gyroscope sensor and Wire I2C library.
I registered readings on my serial monitor via roll and pitch, which I probably can play around further to get more precise readings.

Maybe this is a dumb question, so I apologize, but I'm curious how these readings might be used to initialize the Neopixel.

That's why I thought I should specifically support the Neopixel clock with this specific code:

#if defined(AVR_ATtiny85) && (F_CPU == 16000000)
clock_prescale_set(clock_div_1);
#endif

It is possible, however, that I am totally wrong. So, it was helpful to point out that correction. Thanks!

That would depend on what you want to the Neopixel display to do.

The lines of code you posted above are specific for an ATtiny85 microprocessor running at a certain clock speed. You have the SAM M0, which is a far more advanced microprocessor, and will not recognize functions or instructions for other processors. Stick with the functions and libraries available for the M0 processor.

The Adafruit forum would be a better place to ask questions about Adafruit boards and Arduino libraries.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.