Controlling a stepmotor with an accelerometer MPU6050

Hello everyone,
I'm doing a project where I need to drive a stepmotor (Nema 17HS8401) following the data I get from my accelerometer (MPU6050).
Let me explain my code further:

  • First I ask to my motor to do clockwise rotation until this one press a limit switch
  • As soon as my limitswitch is ON, I receive data from my accelerometer (coordinates X, Y, Z).
  • Finally and following the data I received, I ask to the motor to rotate counterclockwise (with more or less steps following the coordinates)

When I run the code (and using if...else if) the motor don't follow the instructions. However, when I write only one condition (just "if" and I put the "else if" into comments or vice versa), the motor do the correct rotation.
I will be grateful if you can help me to fix that.
Have a great day!

This is my code btw`

// I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h files
// for both classes must be in the include path of your project
#include "I2Cdev.h"
#include "MPU6050.h"

// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation
// is used in I2Cdev.h
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
    #include "Wire.h"
#endif

// class default I2C address is 0x68
// specific I2C addresses may be passed as a parameter here
// AD0 low = 0x68 (default for InvenSense evaluation board)
// AD0 high = 0x69
MPU6050 accelgyro;
//MPU6050 accelgyro(0x69); // <-- use for AD0 high

// Declare Stepper Motor used pins
const int dirPin = 3;
const int stepPin = 4;
const int enPin = 6;
const int limit1 = 5;// left limit
//const int limit2 = 12;// right limit
int16_t ax, ay, az;
int16_t gx, gy, gz;



// uncomment "OUTPUT_READABLE_ACCELGYRO" if you want to see a tab-separated
// list of the accel X/Y/Z and then gyro X/Y/Z values in decimal. Easy to read,
// not so easy to parse, and slow(er) over UART.
#define OUTPUT_READABLE_ACCELGYRO

// uncomment "OUTPUT_BINARY_ACCELGYRO" to send all 6 axes of data as 16-bit
// binary, one right after the other. This is very fast (as fast as possible
// without compression or data loss), and easy to parse, but impossible to read
// for a human.
//#define OUTPUT_BINARY_ACCELGYRO


#define LED_PIN 13
bool blinkState = false;




void setup() {
     // Set up stepper motor
  Serial.begin(1200);
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(enPin, OUTPUT);
 digitalWrite(enPin, LOW);  // active le driver

  //Set up 2 limit switch
  pinMode(limit1 , INPUT_PULLUP);
  //pinMode(limit2 , INPUT_PULLUP);
    // join I2C bus (I2Cdev library doesn't do this automatically)
    #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
        Wire.begin();
    #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
        Fastwire::setup(400, true);
    #endif

    // initialize serial communication
    // (38400 chosen because it works as well at 8MHz as it does at 16MHz, but
    // it's really up to you depending on your project)
    Serial.begin(1200);

    // initialize device
    Serial.println("Initializing I2C devices...");
    accelgyro.initialize();

    // verify connection
    Serial.println("Testing device connections...");
    Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");

    // use the code below to change accel/gyro offset values
    /*
    Serial.println("Updating internal sensor offsets...");
    // -76  -2359 1688  0 0 0
    Serial.print(accelgyro.getXAccelOffset()); Serial.print("\t"); // -76
    Serial.print(accelgyro.getYAccelOffset()); Serial.print("\t"); // -2359
    Serial.print(accelgyro.getZAccelOffset()); Serial.print("\t"); // 1688
    Serial.print(accelgyro.getXGyroOffset()); Serial.print("\t"); // 0
    Serial.print(accelgyro.getYGyroOffset()); Serial.print("\t"); // 0
    Serial.print(accelgyro.getZGyroOffset()); Serial.print("\t"); // 0
    Serial.print("\n");
    accelgyro.setXGyroOffset(220);
    accelgyro.setYGyroOffset(76);
    accelgyro.setZGyroOffset(-85);
    Serial.print(accelgyro.getXAccelOffset()); Serial.print("\t"); // -76
    Serial.print(accelgyro.getYAccelOffset()); Serial.print("\t"); // -2359
    Serial.print(accelgyro.getZAccelOffset()); Serial.print("\t"); // 1688
    Serial.print(accelgyro.getXGyroOffset()); Serial.print("\t"); // 0
    Serial.print(accelgyro.getYGyroOffset()); Serial.print("\t"); // 0
    Serial.print(accelgyro.getZGyroOffset()); Serial.print("\t"); // 0
    Serial.print("\n");
    */

    // configure Arduino LED pin for output
    pinMode(LED_PIN, OUTPUT);


      digitalWrite(dirPin, LOW); // setup motor turn left first
  do {
    digitalWrite(stepPin , HIGH);
    delayMicroseconds(10000);
    digitalWrite(stepPin , LOW);
    delayMicroseconds(10000);
  } while (digitalRead(limit1) == LOW);  // motor turn left until touch limit 1 handle 
  delay(500);
  digitalWrite(enPin, HIGH);  //desactive le driver dès que le fin de course est atteint

  
    // read raw accel/gyro measurements from device
    accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);

    // these methods (and a few others) are also available
    //accelgyro.getAcceleration(&ax, &ay, &az);
    //accelgyro.getRotation(&gx, &gy, &gz);

    #ifdef OUTPUT_READABLE_ACCELGYRO
        // display tab-separated accel/gyro x/y/z values
        Serial.print("a/g:\t");
        Serial.print(ax/16384.0); Serial.print("\t");
        Serial.print(ay/16384.0 ); Serial.print("\t");
        Serial.println(az/16384.0); Serial.print("\t");
        //Serial.print(gx); Serial.print("\t");
        //Serial.print(gy); Serial.print("\t");
        //Serial.println(gz);
    #endif

    #ifdef OUTPUT_BINARY_ACCELGYRO
        Serial.write((uint8_t)(ax >> 8)); Serial.write((uint8_t)(ax & 0xFF));
        Serial.write((uint8_t)(ay >> 8)); Serial.write((uint8_t)(ay & 0xFF));
        Serial.write((uint8_t)(az >> 8)); Serial.write((uint8_t)(az & 0xFF));
        Serial.write((uint8_t)(gx >> 8)); Serial.write((uint8_t)(gx & 0xFF));
        Serial.write((uint8_t)(gy >> 8)); Serial.write((uint8_t)(gy & 0xFF));
        Serial.write((uint8_t)(gz >> 8)); Serial.write((uint8_t)(gz & 0xFF));
    #endif
delay(2000);
    // blink LED to indicate activity
    blinkState = !blinkState;
    digitalWrite(LED_PIN, blinkState);

 if (analogRead(ax) <= 0,1 && analogRead(ay)>= -0,9 && analogRead(az)<= 0,1) {     //1  X=0  Y=-1 Z=0
        for (int i=0; i <= 100; i++){
                                                                                        digitalWrite(enPin, LOW);  // reactive le driver
                                                                                        digitalWrite(dirPin, HIGH); // setup motor turn right first
                                                                                        digitalWrite(stepPin , HIGH);
                                                                                        delayMicroseconds(10000);
                                                                                        digitalWrite(stepPin , LOW);
                                                                                        delayMicroseconds(10000);
                                                                                                                     }
                                                                                        delay(500);
                                                                                        digitalWrite(enPin, HIGH); 
    }
 
else if (analogRead(ax) >= -0,1 && analogRead(ay)>= 0,9 && analogRead(az)>= -0,1 ) {    //2   X=0  Y=1 Z=0
      for (int i=0; i <= 200; i++){
                                                                                        digitalWrite(enPin, LOW);  // reactive le driver
                                                                                        digitalWrite(dirPin, HIGH); // setup motor turn left first
                                                                                        digitalWrite(stepPin , HIGH);
                                                                                        delayMicroseconds(10000);
                                                                                        digitalWrite(stepPin , LOW);
                                                                                        delayMicroseconds(10000);
                                                                                                                     }
                                                                                        delay(500);
                                                                                        digitalWrite(enPin, HIGH); 
      }


else { digitalWrite(enPin, HIGH);  } //driver disabled, motor not running
}
 

void loop() {}

What's that?

I just wanted to mention the condition if X around 0. But I agree it's not really clear for this one: i could have said if (analogRead(ax) <=0,1

...and it would have made just as little sense.

What concept are you trying to express there?

Well, I want to be able to find the location of my device in 3D space. For example, if my device is on the floor. I should be able to see X=0, Y=0, Z=-1 (if you consider the gravity). From that, I would like to rotate my motor with different steps according the location of my device.

No, mathematically, what concept is that trying to express?
Just the one expression I quoted.

Ok let's take an example: if my sensor is on the floor you should read with accelerometer X around 0, Y around 0 and Z around -1 (if you consider the gravity
Capture forum.PNG

Now I want to order the stepmotor to do 200 steps clockwise if my device is in this position.
But for other positions in the space, I would ask maybe 100 steps clockwise (for example)

You're quite a few concepts away from worrying about motors
analogRead returns a value in the range 0 to 1023, and the difference between zero and one is literally down in the noise
You need to scale the readings first.

Then you need to stop misusing the comma operator

Sorry I'm a beginner in Arduino, what do you mean by scaling the reads?

Are you a beginner in writing code as well?
To scale a reading you multiply it by a scale factor to make it the size you want. Or you can use the map function to do it for you.

That line you wrote makes little sense in the C language.

Wait - the 6050 is an I2C device, not analogue - why are you using analogRead?

Indeed I never did computer science or coding before. That's the first time i use arduino and programming on it.
I used analogread because I connected my MPU6050 to my arduino through analog pins, then i thought i would read analogic values

For a beginner, I suggest you stay with the library examples.
Your code there is so far away from even having a hope of working, it's an exercise in frustration.

I know my code is far to be perfect but so far i can move my motor to reset position and get accelerometer values. My only problem until now is for the if...else if. How could you I do to get my Y value and use it for a specific instruction (like moving 200 steps clockwise)?
Please if you can provide me one example i would be grateful:)

That is what you might have done but you will get NO readings by connecting anything to the analogue pins. That sensor does not work like that.

Use the examples you got with the MPU6050 library. They can be got by going to the library menu, choosing Examples and then scrolling down to fine the library name go in that folder and you will find examples. Try and run a few of those and get real readings.work on your code by modifying one of those examples.

No you can't any reading you do get is just noise.

What pin on the sensor are you connecting to the analogue input pin?

This will tell you what the pin out of that sensor is.

Then there's the use of ax as a pin number.
Put that code in the bin.

OK, back at a computer now.

Some misconceptions need addressing: The MPU6050 is attached to pins labelled A4 and A5, but these are not operating as analogue inputs; they are being used as part of a digital interface called I2C (or I2C).

In the code you have, you have "#define OUTPUT_READABLE_ACCELGYRO", so you should be able to see scaled values for the acceleration values, but not gyro values.
The values you see printed are scaled (divided by) 16384, but the acceleration values themselves (ax, ay and az) are left raw.
You didn't share the prints of these values, but I'm guessing (I don't have an MPU6050 datasheet to hand) that these are scaled as fractions of a g, so with the device horizontal, x and y should be close to zero, and z should be close to 1 (or -1).
It may be simpler for you for now to create three float variables, and assign the scaled values to them:

float fx = ax / 16384.0,
         fy = ay / 16384.0,
         fz = az / 16384.0;

Now, to see if a value lies in an interval (between, say -1 and 0), use

if (fx >= -1.0 && fx <=0.0)

Thank you man!!
You made me realised ax, ay and az were raw values and couldn't be read in my if---if else condition! Sounds like it's working now.
For the pin adressing, it seems that i didn't need to mention which pins had to be linked to my MPU6050. I saw that I just had to wire SCL to A5 and SDA to A4, that's all.
I know my code is far to be perfect but at least it's working now^^. Thank you very much for you help again!
Have a good day!:slight_smile: