Servo i2c using an external master

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
}
}

Always the same error:

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.

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.

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;
        }        
    }
}

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.

You have both a global and local variable c.

volatile byte c; //global
int  c = Wire.read(); //local to the ISR

Which one do you think is being compared to here?

 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/