Controlling stepper motor with ITG3200 gyroscope

OK your main problem is that you simply can not control a stepping motor like that.

You carefully go and calculate the time between steps and issue a delay microseconds, and then you go and spoil it all by printing out a load of stuff and then have a delay of 10mS.
At 9600 baud you will send data to the serial port at 960 characters per second, that is one character every 1mS or so. Therefore the print stuff will take about 13mS. You have screwed your stepping motor timing.
All that is without considering the time it takes to actually read the I2C device, that further screws things up.

What you want to do is not straight forward. You need to pulse the stepping motor at a specific speed and you need that speed to change. This needs to be done as a background interrupt task, even so the I2C also runs under interrupts so the two are going to clash and produce an irregular pulse stream to the stepping motor. In fact it is so intractable I can't think of a solution off the top of my head, and that is a rare thing. I will give it some thought overnight.

As to your general understanding of code it is hard to be precise because you do not say exactly what you did when you say you tried to define something as a byte but got a compiler error. But if you define those two variables outside any function then they become global.

  int derps = 0;
  int zmotor = 0;
void setup()
{
.................

Then when you come to use them in the loop function do not define them again just assign them.

zRate = readZ();

zmotor = ((zRate / 14.375) / 6);
derps = 1000000 / ((zmotor * 400) / 60);
int dly = (derps * 2);

Grumpy_Mike:
OK your main problem is that you simply can not control a stepping motor like that.

You carefully go and calculate the time between steps and issue a delay microseconds, and then you go and spoil it all by printing out a load of stuff and then have a delay of 10mS.
At 9600 baud you will send data to the serial port at 960 characters per second, that is one character every 1mS or so. Therefore the print stuff will take about 13mS. You have screwed your stepping motor timing.
All that is without considering the time it takes to actually read the I2C device, that further screws things up.

What you want to do is not straight forward. You need to pulse the stepping motor at a specific speed and you need that speed to change. This needs to be done as a background interrupt task, even so the I2C also runs under interrupts so the two are going to clash and produce an irregular pulse stream to the stepping motor. In fact it is so intractable I can't think of a solution off the top of my head, and that is a rare thing. I will give it some thought overnight.

This has been my main question, because I've known from the start that the delay(10) would screw up the motor timing, and I've been trying this code without it and without the Serial.print stuff entirely, but I know that I can't read data from the gyro faster than once/10ms, so even without the delay and Serial.print stuff, the motor still won't run (because I've tried this). The potential solution I see is to somehow run the motor loop until I get a new reading from the gyro, but I don't know if that's possible, and that's what I'm asking.

To be fair, I've also been talking to a bunch of people in person about this, and so I've lost track of what I've said here, and outside of the forum. I just realized that it doesn't look like I've actually mentioned before now that I've been running the code without the Serial.print and 10ms delay. Sorry about that.

With that in mind, my code still won't run the motor correctly. And I know that this is because I'm trying to read gyro data before the gyro can provide the data.

So just to be super clear: is it possible to run the motor loop at a constant speed provided by the gyro UNTIL NEW data is received, and then have it run on the new data until yet newer data comes in?

blorgon:
So just to be super clear: is it possible to run the motor loop at a constant speed provided by the gyro UNTIL NEW data is received, and then have it run on the new data until yet newer data comes in?

I think we have got to the stage where you need to write down clearly in English (not code) what you want to happen - then we may all be able to sing from the same hymn-sheet. See the concept in planning and implementing a program.

It is entirely possible to do what you ask. But it is not going to happen unless you take a systematic approach to the problem.

All that is needed is that you have a variable that defines the motor speed - that can be DERPS (much as I dislike the name). The variable is updated from time to time by the Gyro.

The impression I have had (and why I focused on trying to find out what is the actual value of DERPS) is that you could not get the motor to run at the speed you want.

...R

So just to be super clear: is it possible to run the motor loop at a constant speed provided by the gyro UNTIL NEW data is received, and then have it run on the new data until yet newer data comes in?

Not without extra hardware.

It is entirely possible to do what you ask.

I don't think so, because reading the I2C takes time and uses the interrupt therefore ruling out the use of an interrupt to keep the motor going at speed. You can't use the timers because they will only give you a limited number of frequencies.

What is needed here is an external pulse generator that can be set at a speed and will keep on with that speed until a new speed is set. Then the Arduino will be free to read the sensors, do the calculation and set the speed.

With that in mind, my code still won't run the motor correctly. And I know that this is because I'm trying to read gyro data before the gyro can provide the data.

That is not the reason, if you try and read the sensor too quickly you will just get the old value, the value is only updated every 10mS. However the act of reading the I2C itself regardless of what it gives you screws up the timing.

Robin2:
I think we have got to the stage where you need to write down clearly in English (not code) what you want to happen - then we may all be able to sing from the same hymn-sheet. See the concept in planning and implementing a program.

It is entirely possible to do what you ask. But it is not going to happen unless you take a systematic approach to the problem.

All that is needed is that you have a variable that defines the motor speed - that can be DERPS (much as I dislike the name). The variable is updated from time to time by the Gyro.

The impression I have had (and why I focused on trying to find out what is the actual value of DERPS) is that you could not get the motor to run at the speed you want.

...R

Thanks for the link. I'm getting ready to go in for our next meeting, and I'll try some things out and report back tonight.

Grumpy_Mike:
However the act of reading the I2C itself regardless of what it gives you screws up the timing.

Okay, this makes sense to me. So readX(); is an interrupt?

Not quite, it is not an interrupt but it uses interrupts to read the I2C bus. That is why you can not generate stepping motor pulses using an interrupt because there will be a time when it will clash and it will delay the running of the interrupt that generates the spepping pulses and thus make it stutter.
Such a system will work better than what you have now but it will never be right,

Grumpy_Mike:
Not quite, it is not an interrupt but it uses interrupts to read the I2C bus. That is why you can not generate stepping motor pulses using an interrupt because there will be a time when it will clash and it will delay the running of the interrupt that generates the spepping pulses and thus make it stutter.
Such a system will work better than what you have now but it will never be right,

So if I send the gyro data over the TX pin to a separate Arduino, which would then print RX'd data to serial, could I run the motor via the second Arduino by reading the serial? The only thing in the second Arduino's void loop() would be a read() that would convert the Z data to a usable value for the delay, and then the motor loop.

Something like this on the RX side?

/*
Connect UNO TX to Pro Mini RX
UNO will print Gyro Z Data
as calculated motor delay in mircoseconds
to serial, Pro Mini will read that data
and use it to drive the motor until a new integer
is received in serial
*/

int dly;  // for motor

void setup()
{
  Serial.begin(9600);
}

void loop()
{

  int data = Serial.available();
  
  if (data != 0)
  {
    dly = Serial.parseInt(); // reads dly as an integer, rather than ASCII

    digitalWrite(2, HIGH);
    digitalWrite(2, LOW);
    delayMicroseconds(dly); 
  }
}

On the TX side, the relevant code would look like this:

void loop()

{

zRate = readZ();
     
zmotor = ((zRate / 14.375) / 6);
zRPM = 1000000 / ((zmotor * 400) / 60);
int dly = (zRPM * 2);

if (dly < 0);
{
(dly * (-1));
}

Serial.println(dly);
delay(10);

}

Just got back from the meeting. Our gyro seems to be malfunctioning, as we are no longer getting any sort of reading from it, even with stock code straight from Sparkfun.

So it looks like I'm going to have to wait to see if anything I've done actually made a difference.

Grumpy_Mike:
I don't think so, because reading the I2C takes time and

You may be correct, but the real problem (at the moment) is that we all seem to have different mental images of what the OP is trying to do and where the problem might be. Hence my Reply #23

I may have missed something, but I don't recall seeing any info about how often the OP needs to generate stepper pulses. That is why I have been trying to get him to tell us the value in the DERPS variable.

...R

blorgon:
So if I send the gyro data over the TX pin to a separate Arduino, which would then print RX'd data to serial, could I run the motor via the second Arduino by reading the serial? The only thing in the second Arduino's void loop() would be a read() that would convert the Z data to a usable value for the delay, and then the motor loop.

I would hesitate in going down the two Arduino route, the communications take time and use the interrupts so that will lead you to the same problems that you have at the moment.

but I don't recall seeing any info about how often the OP needs to generate stepper pulses.

As I see it this is done by this:-

  //motor pulse control loop
  digitalWrite(2,HIGH);
  delayMicroseconds(derps);
  digitalWrite(2,LOW);
  delayMicroseconds(derps);

With derps setting the delay between pulses. This neglects all the other delays in the loop function.

Grumpy_Mike:
With derps setting the delay between pulses. This neglects all the other delays in the loop function.

I understand that. But what the OP is not telling us is the actual value of DERPS (or the range of required values)

There will be a big difference in the whole approach to the problem depending on whether it is 10 or 10,000 microsecs.

...R

Robin2:
I understand that. But what the OP is not telling us is the actual value of DERPS (or the range of required values)

There will be a big difference in the whole approach to the problem depending on whether it is 10 or 10,000 microsecs.

...R

So the gyro sends raw data not yet converted to °/s. The data is also either negative or positive depending on the direction of rotation. So the math you see in the loop converts the gyro data to °/s, and then converts that into RPMs, which multiplies that by the number of steps in a full rotation on the motor, divided by 60. And then you take 1,000,000 and divide it by the number you got after all that conversion, which gives you the number of microseconds between HIGH and LOW, which directly corresponds to an RPM. Then, if the number is negative, we multiply it by -1, to get a positive number usable for the delay. For example, if there were no other delays of any sort, this...

digitalWrite(2,HIGH);
delayMicroseconds(500);
digitalWrite(2,LOW);
delayMicroseconds(500);

...would give us 300 RPM. And so would this (if I understood your own suggestion back in reply #1):

digitalWrite(2,HIGH);
digitalWrite(2,LOW);
delayMicroseconds(1000);

Here's the math:

  int zmotor = ((zRate / 14.375) / 6); 
  int dly = 1000000 / ((zmotor * 200) / 60);
  if (dly < 0);
  {
  (dly * (-1));
  }

Then, if the number is negative, we multiply it by -1, to get a positive number usable for the delay.

Use the abs function it is a lot quicker.

Grumpy_Mike:
I would hesitate in going down the two Arduino route, the communications take time and use the interrupts so that will lead you to the same problems that you have at the moment.

Wouldn't that be negated by the fact that the second Arduino would only be reading what's posted in Serial? So as long as a number is there, the loop runs until a new number is posted. The spin plate doesn't need to match the speed of the rocket instantly, it just needs to match the speed of the rocket, period. The time it takes to do the read function, and then print to serial won't have any effect on the spin plate. Are you saying that the Serial.parseInt() is an interrupt that will mess with the motor loop?

Use the abs function it is a lot quicker.

Like this?

abs(dly) = motordelay

And then use motordelay (or whatever) in the loop?

Are you saying that the Serial.parseInt() is an interrupt that will mess with the motor loop?

Gosh I am saying you will get into trouble without that massive function.
When a serial character is received an interrupt is generated and the interrupt service routine takes the character from the input UART and stores it into the input buffer and then adjusts the buffer pointer. This means that if you are using interrupts to get a smooth pulse period a small jitter will occur when ever a character is received.

Remember EVERYTHING TAKES TIME and if you are using the basic loop to do the timing then code that is used only on some passes of the loop will produce jitter.

Like this?

No like this:-
http://www.arduino.cc/en/Reference/Abs

The spin plate doesn't need to match the speed of the rocket instantly, it just needs to match the speed of the rocket, period.

Once you loose pulses they are lost forever and you platform ends up pointing in the wrong direction.

Grumpy_Mike:
Gosh I am saying you will get into trouble without that massive function.
When a serial character is received an interrupt is generated and the interrupt service routine takes the character from the input UART and stores it into the input buffer and then adjusts the buffer pointer. This means that if you are using interrupts to get a smooth pulse period a small jitter will occur when ever a character is received.

Remember EVERYTHING TAKES TIME and if you are using the basic loop to do the timing then code that is used only on some passes of the loop will produce jitter.
No like this:-
http://www.arduino.cc/en/Reference/Abs
Once you loose pulses they are lost forever and you platform ends up pointing in the wrong direction.

K. You've gone way over my head. I clearly haven't the slightest clue what I'm doing. Nor does anybody else in my group. I read the abs() reference page, and then I wrote my post. Clearly I didn't understand that either.

Does anybody have any idea how this guy did it? https://www.youtube.com/watch?v=U1sid2lG4vE

Does anybody have any idea how this guy did it?

Sure but that is not what you want to do is it? You want to anti-spin a rocket going fast.

Basically the use of a stepping motor is what is giving you the trouble. This sort of thing is normally done with a DC motor and a feedback circuit known as a Phase Locked Loop. (PLL)

I read the abs() reference page, and then I wrote my post. Clearly I didn't understand that either.

abs(dly) = motordelay
should be:-
motordelay = abs(dly)

This is so fundamental to coding that you show that most things are over your head at the moment. I would recommend that you take time out and learn some basic coding. The project is way outside your pay grade at the moment, but you can learn.

Forget your project for the moment and get some experience with the examples in the IDE. Go through them, change them and predict the results. Learn about debugging techniques like printing out variables to see what they contain. At the moment you do not know enough to understand the answers you are being given and we are all wasting our time.