Arduino Forum

Using Arduino => Project Guidance => Topic started by: ggxyz on Apr 16, 2019, 11:26 pm

Title: Servo i2c using an external master
Post by: ggxyz on Apr 16, 2019, 11:26 pm
Hi, Im trying to extrenally control a servo using arduino over i2c. this i2c master will send a byte("3" in this case) to trigger the servo to go to a position and come back to start position.
The servo does not get triggered this way. I am able to make it work with 2 commands( "3" to deploy and "4" to retract) but i want to send only a single command to do the complete loop. Any help would be appreciated


I think the problem might be with the arduino getting confused about the pos value after the first for loop. Any way to control this? i tried using separate variables for deploy and retract but that did not help



//slave receiver
#include<Wire.h>
#include<Servo.h>


Servo myservo;
int pos;


void setup() {
 // put your setup code here, to run once:
 Wire.begin(8 );                 // serial comm at address 8
 Wire.onReceive(receiveEvent);  // create event for serial comm
 Serial.begin(9600);            //baud rate
  myservo.writeMicroseconds(1500);
  myservo.attach(8,900,2100);             //servo on pwm pin 9 (c8 of teensy)
;
 pinMode(LED_BUILTIN, OUTPUT);  // led indicator

}
void loop() {
 
}

void receiveEvent(int howMany) {    //activates when a byte is recieved

     while (0 < Wire.available()) { // event starts when serial comm is available and master sends a byte   
       byte c = Wire.read();         // read byte
          if(c==51){   //3=51,4=52
                    deploy();
                    delay(500);
                    retract();
            }
          }     
        }

void deploy()
{
    digitalWrite(LED_BUILTIN, HIGH);  // led on when byte is available
    for (pos=1500; pos<=2100;pos+=100) { // travel 60 degrees
            myservo.writeMicroseconds(pos);              // tell servo to go to position in variable 'pos'
            delay(50);                       // waits 20ms for the servo to reach the position
          }
}     

void retract()
{
digitalWrite(LED_BUILTIN, LOW);
  for (pos=2100; pos>=1500; pos-=100) { // travel 60 degrees
            myservo.writeMicroseconds(pos);              // tell servo to go to position in variable 'pos'
             delay(50);                       // waits 20ms for the servo to reach the position
          }     
}

Title: Re: Servo i2c using an external master
Post by: pylon on Apr 17, 2019, 08:05 pm
Always the same error:

Code: [Select]
void receiveEvent(int howMany) {    //activates when a byte is recieved

     while (0 < Wire.available()) { // event starts when serial comm is available and master sends a byte   
       byte c = Wire.read();         // read byte
          if(c==51){   //3=51,4=52
                    deploy();
                    delay(500);
                    retract();
            }
          }     
        }


receiveEvent is called in interrupt context and must not call any function that depends on interrupts. delay()  (to give you just an example) depends on interrupts and will block forever if called inside this handler.
Title: Re: Servo i2c using an external master
Post by: ggxyz on Apr 17, 2019, 09:56 pm
you are right. i realised that too after some time. is there any way to achieve what i am trying to do without delay? i can always send 2 commands but i want to do just one.
Title: Re: Servo i2c using an external master
Post by: GolamMostafa on Apr 18, 2019, 10:44 am
Code: [Select]
void receiveEvent(int howMany)
{    //activates when a byte is recieved

     c = Wire.read();         // read byte  ; declare in global area : volatile byte c; volatile flag1 = false;
     flag1 = true;              //indication that receiveEvent() handler has been visited
}

void loop
{
   if(flag1 == true)
   {
       if(c==51)
       {   //3=51,4=52
          deploy();
          delay(500);
          retract();
          flag1 = false;
        }       
    }
}
Title: Re: Servo i2c using an external master
Post by: ggxyz on Apr 22, 2019, 06:47 pm
Hey GolamMostafa, thanks for the code. I tried implementing that but i am not able to get to the void loop.

//slave reciever teensy
#include<Wire.h>
#include<Servo.h>


Servo myservo;
int pos;
volatile int flag1=false;
volatile byte c;

void setup() {
 // put your setup code here, to run once:
 Wire.begin(8);                 // serial comm at address 8
 Wire.onReceive(receiveEvent);  // create event for serial comm
 Serial.begin(9600);            //baud rate
 myservo.writeMicroseconds(1500);
 myservo.attach(8,900,2100);             //servo on pwm pin 9 (c8 of teensy)
 pinMode(LED_BUILTIN, OUTPUT);  // led indicator
}

void receiveEvent(int howMany)
{    //activates when a byte is recieved

   int  c = Wire.read();         // read byte  ; declare in global area : volatile byte c; volatile flag1 = false;
    flag1 = true;              //indication that receiveEvent() handler has been visited
    Serial.println(c);
    Serial.println(flag1);
}

void loop()
{
   if(flag1 == true)
   {
       if(c==51)
       {   //3=51,4=52
          cut();
          Serial.println("x");
          delay(500);
          retract();
          flag1 = false;
        }       
    }
}


void cut()
{
    digitalWrite(LED_BUILTIN, HIGH);  // led on when byte is available
    for (pos=1500; pos<=2100;pos+=100) { // travel 60 degrees
            myservo.writeMicroseconds(pos);              // tell servo to go to position in variable 'pos'
            delay(50);                       // waits 20ms for the servo to reach the position
          }
           
}     

void retract()
{
digitalWrite(LED_BUILTIN, LOW);
    for (pos=2100; pos>=1500; pos-=100) { // travel 60 degrees
            myservo.writeMicroseconds(pos);              // tell servo to go to position in variable 'pos'
             delay(50);                       // waits 20ms for the servo to reach the position
          }     
}


when i mean not getting in the void loop, the arduino monitor does not print "x" on the serial monitor, meaning the code does not reach the loop. Please confirm volatile int flag1 is correctly defined variable.
Title: Re: Servo i2c using an external master
Post by: cattledog on Apr 22, 2019, 11:25 pm
You have both a global and local variable c.
Code: [Select]
volatile byte c; //global
int  c = Wire.read(); //local to the ISR


Which one do you think is being compared to here?
Code: [Select]
if(c==51)


You need to brush up on the concept of "Scope"
https://www.arduino.cc/reference/en/language/variables/variable-scope--qualifiers/scope/ (https://www.arduino.cc/reference/en/language/variables/variable-scope--qualifiers/scope/)