2

I'm trying to measure vibrations of the motor's shaft that is oscillating due to an eccentric load. For that, I am using the MPU 6050 6 axis accelerometer. The motor is a NEMA 17 stepper, connected to Arduino UNO via EasyDriver v4.4. I first uploaded my code for the stepper, where I have 2 buttons to control the rotation direction (if you press the left, it goes clockwise and if you press the right button it goes counter clockwise). This code works fine. Later I uploaded the code only for the accelerometer and it worked fine, however when I'm trying to combine both so that the vibrations could be measured while the motor is running, apparently, I'm losing steps of the motor. Here is the code:

#include<Wire.h>
const int MPU_addr = 0x68; // I2C address of the MPU-6050
int16_t AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ;

#define RPMS                300.0
#define STEP_PIN                9
#define DIRECTION_PIN           8
#define GO_PIN_L                3
#define GO_PIN_R                4

#define STEPS_PER_REV         200
#define MICROSTEPS_PER_STEP     8
#define MICROSECONDS_PER_MICROSTEP   (1000000/(STEPS_PER_REV * 
MICROSTEPS_PER_STEP)/(RPMS / 60))

uint32_t LastStepTime = 0;
uint32_t CurrentTime = 0;

int Distance = 0;

void setup() {
  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  Serial.begin(9600);

  pinMode(STEP_PIN, OUTPUT);
  pinMode(DIRECTION_PIN, OUTPUT);
  digitalWrite(STEP_PIN, LOW);
  digitalWrite(DIRECTION_PIN, LOW);
  pinMode(GO_PIN_L, INPUT);
  pinMode(GO_PIN_R, INPUT);
}

void loop() {
  Distance = Distance + 1;   // record this step
  if (digitalRead(GO_PIN_L) == LOW)
  {
    CurrentTime = micros();
    digitalWrite(8, LOW);
    if ((CurrentTime - LastStepTime) > MICROSECONDS_PER_MICROSTEP)
    {
      LastStepTime = CurrentTime;
      digitalWrite(STEP_PIN, HIGH);
      delayMicroseconds((MICROSECONDS_PER_MICROSTEP * 0.9) / 2);
      digitalWrite(STEP_PIN, LOW);
      delayMicroseconds((MICROSECONDS_PER_MICROSTEP * 0.9) / 2);
    }
  }
  if (digitalRead(GO_PIN_R) == LOW)
  {
    CurrentTime = micros();
    digitalWrite(8, HIGH);
    if ((CurrentTime - LastStepTime) > MICROSECONDS_PER_MICROSTEP)
    {
       LastStepTime = CurrentTime;
      digitalWrite(STEP_PIN, HIGH);
      delayMicroseconds((MICROSECONDS_PER_MICROSTEP * 0.9) / 2);
      digitalWrite(STEP_PIN, LOW);
      delayMicroseconds((MICROSECONDS_PER_MICROSTEP * 0.9) / 2);
    }
  }
//  Wire.beginTransmission(MPU_addr);
//  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
//  Wire.endTransmission(false);
//  Wire.requestFrom(MPU_addr, 14, true); // request a total of 14 registers
//  float AcX = Wire.read() << 8 | Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
//  float AcY = Wire.read() << 8 | Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
//  float AcZ = Wire.read() << 8 | Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
//  Tmp = Wire.read() << 8 | Wire.read(); // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
//  // GyX=Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
//  // GyY=Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
//  // GyZ=Wire.read()<<8|Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
//  Serial.print("AcX = "); Serial.println(AcX / 16384);
//  Serial.print(" AcY = "); Serial.println(AcY / 16384);
//  Serial.print(" AcZ = "); Serial.println(AcZ / 16384);
//  Serial.print(" Tmp = "); Serial.print(Tmp / 340.00 + 36.53);
//  Serial.println("");
//  // Serial.print(" | GyX = "); Serial.print(GyX);
//  // Serial.print(" | GyY = "); Serial.print(GyY);
//  // Serial.print(" | GyZ = "); Serial.println(GyZ);
//  delay(333);
}

So the commented portion of the code is used to read and print accelerations. Since this is in the same loop as the code for the motor, I'm sure that the delay(333) has a negative effect, but also when I tried step-by-step to include lines of code for accelerometer, the motor slowly started to losing speed and steps...

Does anyone of you have a clue how this could be done so that both devices (the motor with EasyDriver v 4.4 and the accelerometer) could run simultaneously since there can't be 2 void loops? I don't know how I could timewise separate their individual codes since I need to monitor the motor with the accelerometer... Kind regards,

Luka

2 Answers 2

0

This is common problem, how to do two (and more) things with waiting, without blocking delay (the simplest way is in example BlinkWithoutDelay)

  • You wait for motor microsteps (with delayMicroseconds)
  • You wait between reading MCU (with delay(333))
  • You spend time sending Transmission over Wire (that is relatively slow) and getting answer requestFrom (also slow)
  • You spend time sending prints over Serial

Each of this is blocking delay of some kind under Arduino (and writing the transmission in non-blocking way is not an easy task)

You should use some kind of state automaton (google that) for that (or more such automats - each for one task), to overcome the blocking nature of the steps.

basically you can have lot of loops running together in this manner:

long state1_micros,state2_micros,state3_micros; // last time it happened
long state1_delay,state2_delay,state3_delay; // how long wait for next step
#define LOOP1_STEP1 1
#define LOOP1_STEP2 2
#define LOOP1_STEP3 3
#define LOOP2_STEP1 1
#define LOOP2_STEP2 2
#define LOOP2_STEP3 3
#define LOOP2_STEP4 4
#define LOOP2_STEP5 5
#define LOOP2_STEP6 6
#define LOOP2_STEP7 7
void setup(){
...
 long current_micros=micros() ; or millis, if less precision needed
 state1=LOOP1_STEP1;
 state1_micros=current_micros;
 state1_delay=0;
 state2=LOOP2_STEP1;
 state2_micros=current_micros;
 state2_delay=0;
 // state3=LOOP3_STEP1;
 // state3_micros=current_micros;
 // state3_delay=0;
}

void loop(){
 long current_micros=micros() ; or millis, if less precision needed
 if (state1==LOOP1_STEP1 && current_micros-state1_micros>=state1_delay) { 
   //state1 paused for at least state1_delay
   if (/*some condition for leave state1 step1  like (digitalRead(GO_PIN_L) == LOW)*/) { 
     state1=LOOP1_STEP2;// next step
     state1_micros+=/*some delay like MICROSECONDS_PER_MICROSTEP */;
     /* do action for STEP1, like  digitalWrite(8, LOW); */
    }
  }
 if (state1==LOOP1_STEP2 && current_micros-state1_micros>=state1_delay) { 
   //state1 paused for at least state1_delay
   if (/*some condition for leave state1 step2  like (True)*/) { 
     state1=LOOP1_STEP3;//next step
     state1_micros =/*some delay like(MICROSECONDS_PER_MICROSTEP * 0.9) / 2) */;
     /* do action for STEP2, like  digitalWrite(STEP_PIN, HIGH); */
    }
  }
 if (state1==LOOP1_STEP3 && current_micros-state1_micros>=state1_delay) { 
   //state1 paused for at least state1_delay
   if (/*some condition for leave state1 step3  like (True)*/) { 
     state1=LOOP1_STEP1;//next step
     state1_micros =/*some delay like(MICROSECONDS_PER_MICROSTEP * 0.9) / 2) */;
     /* do action for STEP2, like  digitalWrite(STEP_PIN, LOW); */
    }
  }
////////
 if (state2==LOOP2_STEP1 && current_micros-state2_micros>=state2_delay) { 
   //state2 paused for at least state2_delay
   if (/*some condition for leave state2 step1  like (digitalRead(GO_PIN_L) == LOW)*/) { 
     state1=LOOP2_STEP2;// next step
     state1_micros+=/*some delay like 100*1000 (333/3) */;
     /* do action for STEP1, like  
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr, 14, true); // request a total of 14 registers
  float AcX = Wire.read() << 8 | Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
  float AcY = Wire.read() << 8 | Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  float AcZ = Wire.read() << 8 | Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  Tmp = Wire.read() << 8 | Wire.read(); // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
  // GyX=Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  // GyY=Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  // GyZ=Wire.read()<<8|Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
 */
    }
  }
 if (state2==LOOP2_STEP2 && current_micros-state2_micros>=state2_delay) { 
   //state2 paused for at least state2_delay
   if (/*some condition for leave state1 step2  like (True)*/) { 
     state2=LOOP2_STEP3;//next step
     state2_micros +=/*some delay like 10*1000 (10ms) */;
     /* do action for STEP2, like  Serial.print("AcX = "); Serial.println(AcX / 16384);  */
    }
  }
 if (state2==LOOP2_STEP3 && current_micros-state2_micros>=state2_delay) { 
   //state2 paused for at least state2_delay
   if (/*some condition for leave state2 step3  like (True)*/) { 
     state2=LOOP2_STEP4;//next step
     state2_micros +=/*some delay like 10*1000 (10ms) */;
     /* do action for STEP3, like Serial.print(" AcY = "); Serial.println(AcY / 16384); */
    }
  }
 if (state2==LOOP2_STEP4 && current_micros-state2_micros>=state2_delay) { 
   //state2 paused for at least state2_delay
   if (/*some condition for leave state2 step4  like (True)*/) { 
     state2=LOOP2_STEP5;//next step
     state2_micros +=/*some delay 10*1000 (10ms) */;
     /* do action for STEP4, like  Serial.print(" AcZ = "); Serial.println(AcZ / 16384); */
    }
  }
 if (state2==LOOP2_STEP5 && current_micros-state2_micros>=state2_delay) { 
   //state2 paused for at least state2_delay
   if (/*some condition for leave state2 step5  like (True)*/) { 
     state2=LOOP2_STEP6;//next step
     state2_micros +=/*some delay 10*1000 (10ms) */;
     /* do action for STEP5, like  Serial.print(" AcZ = "); Serial.println(AcZ / 16384); */
    }
  }
 if (state2==LOOP2_STEP6 && current_micros-state2_micros>=state2_delay) { 
   //state2 paused for at least state2_delay
   if (/*some condition for leave state2 step6  like (True)*/) { 
     state2=LOOP2_STEP7;//next step
     state2_micros +=/*some delay 10*1000 (10ms) */;
     /* do action for STEP6, like  Serial.println(""); */
    }
  }

 if (state2==LOOP2_STEP7 && current_micros-state2_micros>=state2_delay) { 
   //state2 paused for at least state2_delay
   if (/*some condition for leave state2 step7  like (True)*/) { 
     state2=LOOP2_STEP1;//start over
     state2_micros +=/*some delay (333-100-4*10)*1000 (rest to 333ms) */;
     /* do action for STEP7, like  nothing*/
    }
  }
/////
/* if we have more automatons, like stat3, stat4 and so on, put it her */
} // end loop()
2
  • I associate a state machine with a switch-case statement. The name 'STEP' for the state machine is confusing because a stepper motor is involved. The name 'STATE' might be better. Whatever you do, you MUST make the timing variables "unsigned long" to avoid the rollover problem. mcluka, gilhad is making two seperate state machines in the loop function. Using a state machine together with the millis function is indeed the ideal solution.
    – Jot
    Commented May 24, 2017 at 7:04
  • Yes, unsingned long of course, my bad, wrote it from head before first cofee. switch-case is also sometimes more descriptive, in my last project I needed one machine "preferred", so I used if series (with returnin each part), where the more critical parts was solved first, then the less critical and the machines was reordered and interchanged heavily. So I first mentioned the google, then ofered one exapmle approach. There is much more ways hoe to do it.
    – gilhad
    Commented May 24, 2017 at 7:53
0

Thank you gilhad and Jot for your time, I really appreciate it. However, my best friend who happens to be an electric guru as far as I am concerned, proposed an ever more elegant solution to this multi-tasking problem. He proposed to use timer settings in the void setup, like this:

#include<Wire.h>
const int MPU_addr = 0x68; // I2C address of the MPU-6050
int16_t AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ;

#define RPMS                300.0
#define STEP_PIN                9
#define DIRECTION_PIN           8
#define GO_PIN_L                3
#define GO_PIN_R                4

#define STEPS_PER_REV         200
#define MICROSTEPS_PER_STEP     8
#define MICROSECONDS_PER_MICROSTEP   (1000000/(STEPS_PER_REV * MICROSTEPS_PER_STEP)/(RPMS / 60))

uint32_t LastStepTime = 0;
uint32_t CurrentTime = 0;

int Distance = 0;

void setup() {
  bitClear(SREG, 7);
  pinMode(10, OUTPUT);

  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;
  TCCR1A = _BV(COM1B1) | _BV(WGM10) | _BV(WGM11);
  TCCR1B = _BV(WGM12) | _BV(WGM13) | _BV(CS10); //prescaler 1, fast PWM, OCR1A as TOP
  OCR1A = 2423; //timer šteje do 444, tako dobim PWM frekvenco 36,363kHz
  OCR1B = int(OCR1A / 2); //prilagajam duty cyle, naj bo kar 50%

  bitSet(SREG, 7); //global interrupt enable

  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  Serial.begin(9600);

  pinMode(DIRECTION_PIN, OUTPUT);
  digitalWrite(DIRECTION_PIN, LOW);
  pinMode(GO_PIN_L, INPUT);
  pinMode(GO_PIN_R, INPUT);
}

void loop() {
  Distance = Distance + 1;   // record this step
  if (digitalRead(GO_PIN_L) == LOW)
  { TCCR1A = 0;
    TCCR1B = 0;
    TCNT1 = 0;
    delay(100);
    TCCR1A = _BV(COM1B1) | _BV(WGM10) | _BV(WGM11);
    TCCR1B = _BV(WGM12) | _BV(WGM13) | _BV(CS10); //prescaler 1, fast PWM, OCR1A as TOP
    OCR1A = 2423; //timer šteje do 444, tako dobim PWM frekvenco 36,363kHz
    OCR1B = int(OCR1A / 2); //prilagajam duty cyle, naj bo kar 50%

    digitalWrite(8, LOW);
  }
  if (digitalRead(GO_PIN_R) == LOW)
  {
    TCCR1A = 0;
    TCCR1B = 0;
    TCNT1 = 0;
    delay(100);
    TCCR1A = _BV(COM1B1) | _BV(WGM10) | _BV(WGM11);
    TCCR1B = _BV(WGM12) | _BV(WGM13) | _BV(CS10); //prescaler 1, fast PWM, OCR1A as TOP
    OCR1A = 2423; //timer šteje do 444, tako dobim PWM frekvenco 36,363kHz
    OCR1B = int(OCR1A / 2); //prilagajam duty cyle, naj bo kar 50%

    digitalWrite(8, HIGH);
  }
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr, 14, true); // request a total of 14 registers
  float AcX = Wire.read() << 8 | Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
  float AcY = Wire.read() << 8 | Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  float AcZ = Wire.read() << 8 | Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  Tmp = Wire.read() << 8 | Wire.read(); // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
  // GyX=Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  // GyY=Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  // GyZ=Wire.read()<<8|Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
  Serial.print("AcX = "); Serial.println(AcX / 16384);
  Serial.print(" AcY = "); Serial.println(AcY / 16384);
  Serial.print(" AcZ = "); Serial.println(AcZ / 16384);
  Serial.print(" Tmp = "); Serial.print(Tmp / 340.00 + 36.53);
  Serial.println("");
  // Serial.print(" | GyX = "); Serial.print(GyX);
  // Serial.print(" | GyY = "); Serial.print(GyY);
  // Serial.print(" | GyZ = "); Serial.println(GyZ);
  delay(333);
}

So the portion in void setup:

  bitClear(SREG, 7);
  pinMode(10, OUTPUT);

  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;
  TCCR1A = _BV(COM1B1) | _BV(WGM10) | _BV(WGM11);
  TCCR1B = _BV(WGM12) | _BV(WGM13) | _BV(CS10); //prescaler 1, fast PWM, OCR1A as TOP
  OCR1A = 2423; //timer counts to 444, PWM frequency is thereby 36,363kHz
  OCR1B = int(OCR1A / 2); //duty cyle is 50%

  bitSet(SREG, 7); //global interrupt enable

rotates the step motor WITHOUT any code in the void loop, void loop is only used to change the rotation direction of the motor by using the buttons that I incorporated (GO_PIN_L and GO_PIN_R) and to measure the acceleration with the accelerometer.

I hope this helps to anyone that might have similar problems. Kind regards,

Luka

1
  • Accept your own answer to close this question.
    – user31481
    Commented Nov 21, 2017 at 8:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.