I2C slave reply

Hi .

I am using a master - slave device with I2C communication on arduino ATMega boards.
I have connected the slave device to a stepper motor .
The master requests the slave to inform it once the stepper motor has finished running .
I would like to know how to make the slave send this acknowledgement to the master AFTER the stepper motor stops running . Can anyone help me ?
Here is my code for master and slave :

// I2C
MASTER

// given adressess as 4 for slave 1 
//                    

#include <Wire.h>


#include <AccelStepper.h>

int incomingByte = 0;
long millisec = 0;
//int bytesSent = 0;
int a[10]= {0};
int i =0;

void setup()
{
   Wire.begin();
   //Start Serial for debuging purposes	
  Serial.begin(115200); // USB comm with PC

  delay(1000);
  
  spam();
}



void spam() {
  Serial.println("--------------------");
  
  Serial.println("1: Stepper1 FORWARD");

  Serial.println("2: Stepper1 BACKWARD");
  
}


void loop()
{
  
   if(millis() > millisec) {
      millisec = millis();
    
    if (Serial.available() > 0) {
        incomingByte = Serial.read()-48;
        Serial.print("  Selection:");Serial.println(incomingByte);
         
        switch(incomingByte) {
          case 1:  Serial.println("Stepper1: forward"); 
                   Wire.beginTransmission(4);
                   Wire.send(1);
                   Wire.endTransmission();  
                   
                   delay(1000);
                   // to find out how many steps left for the motor 
                   Wire.requestFrom(4,3);
                   while(Wire.available())
                   { 
                      char c = Wire.receive();
                      Serial.print(c);
//                      
                   }
                  
                   break;
                   
          case 2:  Serial.println("Stepper1: backward"); 
                   
                   Wire.beginTransmission(4);
                   Wire.send(2);
                   Wire.endTransmission(); 
                   
                   Wire.requestFrom(4,3);
                   while(Wire.available())
                   {
                      char c = Wire.receive(); // receive a byte as character
                      Serial.print(c);         // print the character
                   }
                  
                   break;
                   
          default: spam(); 
                   break;
                  
        } // switch case loop
    }   //serial available loop
  }    //  millis loop
}       // void loop

SLAVE :

// I2C communication  1 Master- 1 Slave 

// program for slave 4



#include <Wire.h>

#include <AccelStepper.h>

int incomingByte = 0;
int stepperPin = 0;
int stepperInterval = 2;
int stepperCounter = 0;
int stepCount = 0;

// timer variables
long millisec = 0;
long microsec = 0;



// stepper motor pins: STEP, DIRECTION, STEPPER ON/OFF
AccelStepper Stepper1(1,2,3);



void setup() {
  
  Wire.begin(4);
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);

  
    
 //Start Serial for debuging purposes	
  Serial.begin(115200); // USB comm with PC

for(int i = 0;i <54;i++) {
    pinMode(i,OUTPUT);
    digitalWrite(i,LOW);
  }

  
  
  // switch stepper chips off 
  digitalWrite(4,HIGH);

  
  
    Stepper1.setMaxSpeed(1000);

}


  
 void receiveEvent(int howMany)
{

      if (Wire.available() > 0) {
       incomingByte = Wire.receive();
     }
}  
      

void requestEvent()
{


loop();
}               // as expected by master
 


void loop()
{ 
    Stepper1.run();
  
  if(Stepper1.run() == 0)
  {
   digitalWrite(4,HIGH);
   Wire.send("yes");
  }
  
        //Serial.print("  Selection:");Serial.println(incomingByte);
        switch(incomingByte) {
          case 1: digitalWrite(4,LOW); digitalWrite(3,HIGH); digitalWrite(2,HIGH); //Serial.println("Stepper1: forward"); 
                  
                  Serial.println("forward");
                  Stepper1.setCurrentPosition(0);
                  Stepper1.setAcceleration(500.0);
                  Stepper1.moveTo(4000); 
                  
                  break;
          
        
          case 2:  digitalWrite(4,LOW); digitalWrite(3,LOW); digitalWrite(2,HIGH);   //Serial.println("Stepper1: backward"); 
                  
                  Serial.println("backward");
                  Stepper1.setCurrentPosition(0);
                  Stepper1.setAcceleration(500.0);
                  Stepper1.moveTo(-4000); 
                  break;
          
        
      }
     // switch loop
     incomingByte = 0;   
} // main loop

I would like to know how to make the slave send this acknowledgement to the master AFTER the stepper motor stops running .

How does the slave KNOW this? If you are using the Stepper library, the step() method blocks until the function ends. At that point, send the reply.

Hi Paul ,

I have just added my code . I have used accelstepper library . Would you be able to tell me how to implement Wire.send after going through this code please .

When the master calls Wire.requestFrom(), that triggers the OnRequest event on the slave. You have registered requestEvent() as the event handler to be called.

void requestEvent()
{
loop();
}               // as expected by master

Calling loop() once is a silly thing to do. What you need to do, in this method, is wait for the stepper to get where it is going, then send the reply.

Use the AccelStepper::currentPosition() method to find where the stepper is. Compare that to where you told it to go. When they match, the stepper has finished moving.

Hi Paul

I changed my SLAVE program a bit . It still doesnt seem to work . Would you be able to advise me on how to proceed now .

// I2C communication  1 Master- 1 Slave 

// program for slave 4



#include <Wire.h>

#include <AccelStepper.h>

int incomingByte = 0;
int stepperPin = 0;
int stepperInterval = 2;
int stepperCounter = 0;
int stepCount = 0;

// timer variables
long millisec = 0;
long microsec = 0;



// stepper motor pins: STEP, DIRECTION, STEPPER ON/OFF
AccelStepper Stepper1(1,2,3);



void setup() {
  
  Wire.begin(4);
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);

  
    
 //Start Serial for debuging purposes	
  Serial.begin(115200); // USB comm with PC

for(int i = 0;i <54;i++) {
    pinMode(i,OUTPUT);
    digitalWrite(i,LOW);
  }

  
  
  // switch stepper chips off 
  digitalWrite(4,HIGH);

  
  
    Stepper1.setMaxSpeed(1000);

}


void receiveEvent(int howMany)
{
     if (Wire.available() > 0) {
     incomingByte = Wire.receive();
     }
}  
      

void requestEvent()
{

  if(Stepper1.run() == false)
  {
   if(Stepper1.currentPosition() ==4000)
   {digitalWrite(4,HIGH);
   Wire.send("yes");
   }               
  } 
}


void loop()
{ 
    Stepper1.run();
    
   
  
  
        //Serial.print("  Selection:");Serial.println(incomingByte);
        switch(incomingByte) {
          case 1: digitalWrite(4,LOW); digitalWrite(3,HIGH); digitalWrite(2,HIGH); //Serial.println("Stepper1: forward"); 
                   
                  Serial.println("forward");
                  Stepper1.setCurrentPosition(0);
                  Stepper1.setAcceleration(500.0);
                  Stepper1.moveTo(4000); 
                  
                  break;
          
        
          case 2:  digitalWrite(4,LOW); digitalWrite(3,LOW); digitalWrite(2,HIGH);   //Serial.println("Stepper1: backward"); 
                  
                  Serial.println("backward");
                  Stepper1.setCurrentPosition(0);
                  Stepper1.setAcceleration(500.0);
                  Stepper1.moveTo(-4000); 
                  break;
          
        
      }
     // switch loop
     incomingByte = 0;   
} // main loop
  if(Stepper1.run() == false)
  {
   if(Stepper1.currentPosition() ==4000)
   {digitalWrite(4,HIGH);
   Wire.send("yes");
   }               
  }

The run() method returns true if the motor is at the desired position. If not, it returns false.

You are checking whether the motor is where it is supposed to be. If not, you check if it is at some hardcoded location. Since that location matches where you told it to go, it is unlikely to have gotten there in the interval between the two calls.

In any event, you need a while loop that keeps checking if the stepper is where it is meant to be. Checking only once is not going to work.

Hi Paul

I did make a few changes and put a while loop as suggested
Please have a look at it .
The result is it is giving me y with two dots on top of it . And this is while running … not AFTER running . Please help . .

SLAVE :

// I2C communication  1 Master- 1 Slave 

// program for slave 4



#include <Wire.h>

#include <AccelStepper.h>

int incomingByte = 0;
int stepperPin = 0;
int stepperInterval = 2;
int stepperCounter = 0;
int stepCount = 0;

// timer variables
long millisec = 0;
long microsec = 0;



// stepper motor pins: STEP, DIRECTION, STEPPER ON/OFF
AccelStepper Stepper1(1,2,3);



void setup() {
  
  Wire.begin(4);
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);

  
    
 //Start Serial for debuging purposes	
  Serial.begin(115200); // USB comm with PC

for(int i = 0;i <54;i++) {
    pinMode(i,OUTPUT);
    digitalWrite(i,LOW);
  }

  
  
  // switch stepper chips off 
  digitalWrite(4,HIGH);

  
  
    Stepper1.setMaxSpeed(1000);

}


void receiveEvent(int howMany)
{
     if (Wire.available() > 0) {
     incomingByte = Wire.receive();
     }
}  
      

void requestEvent()
{

   while(Stepper1.currentPosition() == Stepper1.targetPosition()) 
   {digitalWrite(4,HIGH);
   Wire.send("yes");
   }               
  
}


void loop()
{ 
   while( Stepper1.currentPosition() != Stepper1.targetPosition())   
   Stepper1.run();
   
  
  
        //Serial.print("  Selection:");Serial.println(incomingByte);
        switch(incomingByte) {
          case 1: digitalWrite(4,LOW); digitalWrite(3,HIGH); digitalWrite(2,HIGH); //Serial.println("Stepper1: forward"); 
                  
                  Serial.println("forward");
                  Stepper1.setCurrentPosition(0);
                  Stepper1.setAcceleration(500.0);
                  Stepper1.moveTo(4000); 
                  
                  break;
          
        
          case 2:  digitalWrite(4,LOW); digitalWrite(3,LOW); digitalWrite(2,HIGH);   //Serial.println("Stepper1: backward"); 
                  
                  Serial.println("backward");
                  Stepper1.setCurrentPosition(0);
                  Stepper1.setAcceleration(500.0);
                  Stepper1.moveTo(-4000); 
                  break;
          
        
      }
     // switch loop
     incomingByte = 0;   
} // main loop
   while(Stepper1.currentPosition() == Stepper1.targetPosition()) 
   {digitalWrite(4,HIGH);
   Wire.send("yes");
   }

So, you want the slave to send yes over and over again when the stepper is at the target position. Until then, do nothing.

Try again. Something like:

   while(Stepper1.currentPosition() != Stepper1.targetPosition()) 
   { // Do nothing }
   digitalWrite(4,HIGH);
   Wire.send("yes");

The code in loop to execute Stepper1.run() when the stepper is not at the desired point is not necessary. Just call Stepper1.run(). It will do something, or not, based on whether it is at the target position or not.

Hi Paul ,
Thanks
This seems to work for me .
But I had to increase my delay in the master to a value greater than 5000 . If it is lower , the stepper motor just doesnt run .
Would you be able to explain why this happens ?

And also ,
if I was to replace ur code with

while(Stepper1.currentPosition() != Stepper1.targetPosition()) 
   { //Wire.send("Action not complete");
   }
   digitalWrite(4,HIGH);
   Wire.send("Action complete");

then shouldnt it send “Action not complete” continuously ?

Anish

then shouldnt it send "Action not complete" continuously ?

Yes, and no.

No, it wouldn't send the message continuously, because the statement is commented out (and doesn't belong on the same line as the {).

If uncommented, the slave would send the message over and over, but the master is only expecting three bytes (unless you changed that). As soon as it gets three bytes, it quits listening to the slave.

But I had to increase my delay in the master to a value greater than 5000 . If it is lower , the stepper motor just doesnt run .

What delay?

What does your debug output tell you is happening? You can use Serial.print(), Serial.println(), etc. to talk to the PC from either the master or the slave, to learn what is happening.

Hi Paul I gave a delay in between endTransmission and requestFrom in the Master . If I dont give this , the motor doesnt run . The slave acknowledges the command and activates the motor , but the motor doesnt move .

Any idea ?

Any idea ?

Have you removed the while statement from loop()?

Hi Paul ,
No I did not remove the while loop.
Let me post u a simplifed version of the program … same program … just cut off some unwanted cases …
I would also like to know , while the stepper is in motion , does the slave relay that to the master ? I dont understand why I have to give such a big delay .

MASTER

// I2C 1 master  2 slaves

// given adressess as 4 for slave 1 
//                    

#include <Wire.h>


#include <AccelStepper.h>

int incomingByte = 0;
long millisec = 0;
//int bytesSent = 0;
int a[10]= {0};
int i =0;

void setup()
{
   Wire.begin();
   //Start Serial for debuging purposes	
  Serial.begin(115200); // USB comm with PC
  //Serial3.begin(115200); // master-slave comm with second Arduino
  delay(1000);
  
  spam();
}



void spam() {
  Serial.println("--------------------");
  
  Serial.println("1: Stepper1 FORWARD");

 
  
}


void loop()
{
  
   if(millis() > millisec) {
      millisec = millis();
    
    if (Serial.available() > 0) {
        incomingByte = Serial.read()-48;
        Serial.print("  Selection:");Serial.println(incomingByte);
         
        switch(incomingByte) {
          case 1:  Serial.println("Stepper1: forward"); 
                   Wire.beginTransmission(4);
                   Wire.send(1);
                   Wire.endTransmission();  
                   
                   //Serial.println("Waiting for action to complete");
                   delay(1000);
                   
                   Wire.requestFrom(4,15);
                   while(Wire.available())
                   {  
                      char c = Wire.receive();
                      Serial.print(c);
                     
                   }

                  
                   break;
                   
                   default: spam(); 
                   break;
                  
        } // switch case loop
    }   //serial available loop
  }    //  millis loop
}       // void loop

SLAVE

// I2C communication  1 Master- 1 Slave 

// program for slave 4



#include <Wire.h>

#include <AccelStepper.h>

int incomingByte = 0;
int stepperPin = 0;
int stepperInterval = 2;
int stepperCounter = 0;
int stepCount = 0;

// timer variables
long millisec = 0;
long microsec = 0;



// stepper motor pins: STEP, DIRECTION, STEPPER ON/OFF
AccelStepper Stepper1(1,2,3);



void setup() {
  
  Wire.begin(4);
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);

  
    
 //Start Serial for debuging purposes	
  Serial.begin(115200); // USB comm with PC

for(int i = 0;i <54;i++) {
    pinMode(i,OUTPUT);
    digitalWrite(i,LOW);
  }

  
  
  // switch stepper chips off 
  digitalWrite(4,HIGH);

  
Stepper1.setMaxSpeed(1000);

}


void receiveEvent(int howMany)
{
     if (Wire.available() > 0) {
     incomingByte = Wire.receive();
     }
}  
      

void requestEvent()
{

   while(Stepper1.currentPosition() != Stepper1.targetPosition()) 
   { //do nothing
   }
   digitalWrite(4,HIGH);
   Wire.send("Action complete");
                 
  
}


void loop()
{ 
    
   Stepper1.run();
   
  
  
        //Serial.print("  Selection:");Serial.println(incomingByte);
        switch(incomingByte) {
          case 1: digitalWrite(4,LOW); digitalWrite(3,HIGH); digitalWrite(2,HIGH); //Serial.println("Stepper1: forward"); 
                  
                  Serial.println("forward");
                  Stepper1.setCurrentPosition(0);
                  Stepper1.setAcceleration(500.0);
                  Stepper1.moveTo(4000);
                  break;
        }
     // switch loop
     incomingByte = 0;   
} // main loop

I would also like to know , while the stepper is in motion , does the slave relay that to the master ?

Look at what happens when the master requests data:

void requestEvent()
{
   while(Stepper1.currentPosition() != Stepper1.targetPosition()) 
   { //do nothing
   }
   digitalWrite(4,HIGH);
   Wire.send("Action complete");
}

The code blocks until the stepper gets to where it is supposed to go. Then, it sends a message. So, no, the slave does not tell the master anything while the stepper is moving.

I'll ask again. What are your debug messages telling you?

anishh2003: Hi Paul

I used

Serial.println(Stepper1.currentPosition());

after Stepper.run() .

I found that if I give a delay of '100' in Master , I get a currentPosition of the Motor as '7' delay of 4000 - gave currentPosition of 2547 10,000 - gave me the currentPosition of 4000 , which I want

Is there anything else you would have me check ? please let me know . Also the motor just stalls after it abruptly stops .. meaning it does not switch off .. it is just stuck.

I used

Where? I need to see where you used that.

In the loop() method on the slave, print the position of the stepper.

In the requestEvent callback, print when the callback is entered and when it is ended.

It may be that requestEvent needs to behave like an interrupt service request handler, in that it can not block. If that is the case, then I don't know what you will have to do. Perhaps you could just connect two other pins between the two Arduinos. On the "master" poll the pin of interest. On the "slave", set the pin LOW when the stepper is running. Set it HIGH when the stepper is done.

Hi Paul

Please check if this slave code is what you are looking for :
Basically ,

for delay of 100 - the current position of motor (when it stopped) is 7 and the request is called when the motor is at position 7 .
for delay of 4000 - the current position of motor is 2547 and the request is called when the motor is at position 2547 .

So is the request being called after the motor stops ? or is the delay interfering in some way ?

// I2C communication  1 Master- 1 Slave 

// program for slave 4



#include <Wire.h>

#include <AccelStepper.h>

int incomingByte = 0;


// timer variables
long millisec = 0;
long microsec = 0;



// stepper motor pins: STEP, DIRECTION, STEPPER ON/OFF
AccelStepper Stepper1(1,2,3);



void setup() {
  
  Wire.begin(4);
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);

  
    
 //Start Serial for debuging purposes	
  Serial.begin(115200); // USB comm with PC

for(int i = 0;i <54;i++) {
    pinMode(i,OUTPUT);
    digitalWrite(i,LOW);
  }

// switch stepper chips off 
  digitalWrite(4,HIGH);

Stepper1.setMaxSpeed(1000);

}


void receiveEvent(int howMany)
{
     if (Wire.available() > 0) {
     incomingByte = Wire.receive();
     }

    

}  
      

void requestEvent()
{ 
   Serial.println(" Request is called when the motor is at position");
   Serial.println(Stepper1.currentPosition());
   
   while(Stepper1.currentPosition() != Stepper1.targetPosition()) 
   { //do nothing
   }
   digitalWrite(4,HIGH);
   Wire.send("Action complete");
  
  Serial.println("Request send when motor is at position");              
  Serial.println(Stepper1.currentPosition());
}


void loop()
{ 
     
   Stepper1.run();
  
   Serial.println(Stepper1.currentPosition());
  
  
        //Serial.print("  Selection:");Serial.println(incomingByte);
        switch(incomingByte) {
          case 1: digitalWrite(4,LOW); digitalWrite(3,HIGH); digitalWrite(2,HIGH); //Serial.println("Stepper1: forward"); 
                  
                  Serial.println("forward");
                  Stepper1.setCurrentPosition(0);
                  Stepper1.setAcceleration(500.0);
                  Stepper1.moveTo(4000); 
                  
                  break;
         }
     // switch loop
     incomingByte = 0;   
} // main loop

Please SHOW me the serial output.

@anishh2003 - please post your output. Don’t just interpret it, or comment on it. Post it. For example, does the stuff below appear?

void requestEvent()
{ 
   Serial.println(" Request is called when the motor is at position");
   Serial.println(Stepper1.currentPosition());
   
   while(Stepper1.currentPosition() != Stepper1.targetPosition()) 
   { //do nothing
   }
   digitalWrite(4,HIGH);
   Wire.send("Action complete");
  
  Serial.println("Request send when motor is at position");              
  Serial.println(Stepper1.currentPosition());
}

Doing serial prints inside an ISR (which effectively is what requestEvent is) is a very bad idea. It is likely to block and hang. Does your code hang?

… it is just stuck.

Looks like it does.

Hi

Here is my output for a delay of 100

0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
forward
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
6
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7 Request is called when the motor is at position
7

@Nick : The code hangs irrespective of whether the serial.println statments are there in the requestEvent() for me .