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
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()
-
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– Jot05/24/2017 07:04:34Commented 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 usedif
series (withreturn
in 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– gilhad05/24/2017 07:53:06Commented May 24, 2017 at 7:53
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
-
Accept your own answer to close this question.user31481– user3148111/21/2017 08:23:16Commented Nov 21, 2017 at 8:23
Explore related questions
See similar questions with these tags.