0

So I'm using this movement() function inside the slave Arduino Uno, which basically handles two stepper motors and one servo motor. When I call this function through the Serial.read() of the Arduino Uno, it works perfectly, both the steppers and the servo.

I have a master Arduino Mega interfaced with the Uno (mentioned above) when I send a wire message from the Mega to Arduino to run the movement() function in the Uno, the steppers work fine, but the servo does not work. Although, again I mention that when the function is called upon using the Serial.read() of the Uno, it works absolutely fine.

I'm using an Adafruit motor shield on the Arduino Uno as well.

OK so this is the Uno (slave code)

void setup() {
  servo1.attach(9);
  Serial.begin(9600);
  Wire.begin(5);
  Wire.onReceive(receiveEvent);
  Serial.begin(9600); 
}

void loop() {
  //other stuff
}

void receiveEvent(int howMany) {
  while(Wire.available()) {
    char c = Wire.read();
    int From = Wire.read();
    int To = Wire.read();
    int x = 0;
    movement(From,To);
  }
}

int movement(int from,int to) {
  int X_From = from / 10;
  int X_To = to/10;
  int Y_From = from % 10;
  int Y_To = to % 10;
  /* First we have to get the motor position and move the motor to the from position */
  /* === START === */
  int diff_X = X_From - motor_X;
  int diff_Y = Y_From - motor_Y;
  int Y_Value = abs(diff_Y * Y_Steps);
  servoDown();
  if(diff_X != 0) {
    if(diff_X > 0) {
      /* Positive value means the X_Axis motor will move in the forward direction */
      int X_Value = abs(diff_X * X_Steps);
      X_Forward(X_Value);
    } else if(diff_X < 0) {
      /* Negative value means the X_Axis motor will move in the backward direction */
      int X_Value = abs(diff_X * 400);
      X_Backwards(X_Value);
    }
  }
  if(diff_Y != 0) {
    if(diff_Y > 0) {
      Y_Forward(Y_Value);
    } else if(diff_Y < 0) {
      Y_Backwards(Y_Value);
    }
  }
  Serial.println("Motor movement done");
  /* === END === */
  motor_X = X_From;
  motor_Y = Y_From;
  int diff_X_2 = X_To - motor_X;
  int diff_Y_2 = Y_To - motor_Y;
  int Y_Value2 = abs(diff_Y_2 * Y_Steps);
  servoUp();
  if(diff_X_2 != 0) {
    if(diff_X_2 > 0) {
      /* Positive value means the X_Axis motor will move in the forward direction */
      int X_Value2 = abs(diff_X_2 * X_Steps);
      X_Forward(X_Value2);      
    } else if(diff_X_2 < 0) {
      /* Negative value means the X_Axis motor will move in the backward direction */
      int X_Value2 = abs(diff_X_2 * 400);
      X_Backwards(X_Value2);
    }
  }
  if(diff_Y_2 != 0) {
    if(diff_Y_2 > 0) {
      Y_Forward(Y_Value2);
    } else if(diff_Y_2 < 0) {
      Y_Backwards(Y_Value2);
    }
  }
  motor_X = X_To;
  motor_Y = Y_To;
  Serial.println("piece movement done");
  return 1;
}

void servoUp() {
  for (i = 100; i >= 0; i--) {
    servo1.write(i);
  }
}

void servoDown() {
  for (i = 0; i < 100; i++) {
    servo1.write(i);
  }
}

This is the master Mega Code:

void setup() {
  Wire.begin();
}

void loop() {
  //check if a condition appeared from sensor and then send the data to Uno to run the movement function through sendData()
  sendData(From,To);
}

void sendData(int From,int To) {
  Wire.beginTransmission(5);
  Wire.write('f');
  Wire.write(From);
  Wire.write(To);
  Wire.endTransmission();
}

Any suggestions what's wrong?

  • For the start - I am afraid, that you call the movement from interrupt, and so interrupts are disabled - so if movement uses interrupts, it is blocked. Next thing is, that I suppose the movement is running 'long time' as it manipulates some physical device (motor), which is slow compared to 16MHz of Uno. So you are blocking interrupt for 'long time', which is not good too. I suggest to have the From and To static volatile global (and some locking mechanizmus around to block concurent access) and use that from the loop where interrupts are enabled and not blocked. – gilhad Jan 18 '17 at 12:10
  • Don't cross post questions. – gre_gor Jan 18 '17 at 14:55
1

If you are using Servo library, which uses timer (and so interrupts) for manipulating the servos then you may have the problem, that from loop it works (as interrrupts are enabled), but from interrupt (ISR) it does not work, because while serving interrupt (from I2C) all other interrupts are (temporary) disabled.

In receiveEvent() you are inside interrupt serving, so other interrupts are disabled and so anything, what depends on interrupts does not work, until you leave the function.

So it is the more probable cause, why your servo does not move, while called from receiveEvent(), while it moves, if called from loop()

And you wait inside the interrupt routine, until all Wire input is read fully and then while the (slow, as it probabely involves mechanical components like motors or servos) movement(from,to) ends (if it can even end with interrupts disabled).

The solution would be to do all movement(..) in the loop and int the receiveEvent() just store the already aviable values and put them into some static volatile variables, when transfer ends together with mark, that some change was done.

static volatile int from;
static volatile int to;
static volatile int changed;

void loop() {
int my_from,my_to;
  if (changed) {
     noInterrupts();  // critical, time-sensitive code here
       my_from=from;
       my_to=to;
       changed=0;
     interrupts(); // enable interrupts, so I2C, servo and such can work again
     movement(my_from,my_to);
  }
}
void receiveEvent(int howMany)
{
  if(Wire.available())
  {    
    char c = Wire.read();
    from = readInteger();
    to = readInteger();
    changed = 1;
  }
}

Not totally perfect (mainly reading the Wire, which I would just tak as much as is ready to some buffer and decode it only when buffer is sufficiently full and syntactically correct, but if would make the idea here more complicated), but should work much better, than the current version.

Better version of `receiveEvent can look like this:

char buf[20];
int buf_pos;
void receiveEvent(int howMany)
{
  while (Wire.available()) {
    buf[buf_pos] = Wire.read();
    buf_pos++;
    if (buf[0] == 'f') { // starts with known command
      if (buf_pos>= sizeof(char)+2*sizeof(int)) { // have all data already collected 
        from=0;
        for (i=0;i<sizeof(int);i++) from=from<<8+buf[sizeof(char)+i];
        to=0;
        for (i=0;i<sizeof(int);i++) to=to<<8+buf[sizeof(char)+sizeof(int)+i];
        changed = 1;
        buf_pos=0;
        } // end of buf_pos>= ...
     } else { // not started with 'f' (or anything it should know) - throw all transfer away as bad
        while (Wire.available()) Wire.read();
        buf_pos = 0;
     }
}
  • This is exactly what I did to resolve the problem earlier. Great!! – Aimal Azmi Jan 18 '17 at 22:30
1

I don't know if this is the whole problem, but if you call sendData (0, 270) you are only going to send 3 bytes to the Uno 0x66, 0x00, 0x01. You should be sending 5, 0x66, 0x00, 0x00, 0x01, 0x0E

The Uno only reads three bytes and receives: c = 0x66; // 'f' From = 0x00; // 0 To = 0x01; // 1

This is because Wire.write() and Wire.read() send/read a single byte (when you call them like this). What you need is:

void sendData(int from, int to)
{
    Wire.beginTransmission(5);
    Wire.write('f');
    Wire.write(&from, sizeof(int));  // Write an array of 'size of int' bytes
    Wire.write(&to, sizeof(int));
    Wire.endTransmission();
}


int readInteger (void)
{
    int result= 0;
    for (int byte = 0; byte < sizeof(int); byte++)
    {
       result = result << 8;  // Left shift the current data (multiply by 2^8)
       result += Wire.read(); // Add the next byte as the LSB
    }
    return result;
}
void receiveEvent(int howMany)
{
  while(Wire.available())
  {    
    char c = Wire.read();
    int from = readInteger();
    int to = readInteger();
    int To = Wire.read();
    movement (from, to);
  }
}

This code sends and receives the complete integers, so it should work.

Sorry I can't compile it, but I think its right.

  • Oh and like I said this probably isn't the only problem, I don't think you should read all this data from the I2C bus with a blocking loop in you interrupt handler. And The interrupt should tell the running code (non-ISR) to do the movement by setting a static flag, rather than doing it itself. – Code Gorilla Jan 18 '17 at 13:22
  • Reading the data isn't the problem really, I'm receiving the data accurately. But the thing that I cant figure out is, during the movement function the steppers work fine, but the servo isnot working. That is the main problem. I've Serial.print out the data received and it looks accurate – Aimal Azmi Jan 18 '17 at 14:22
  • It you can read the data you must be using angles less than 256. – Code Gorilla Jan 18 '17 at 14:46
  • No worries I've resolved the problem now – Aimal Azmi Jan 18 '17 at 22:28
  • 1
    good to hear, please share your solution as example for others solving similar problem, who find this question in future :) – gilhad Jan 18 '17 at 22:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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