i2c works in test but not in main program

Hi I am having trouble getting the correct values from a Nano(slave) to Uno(master). The only real difference I can tell is that I am not running te receive event in the loop but am calling it in a function. I think it is necessary because it will stall the motor operations if it is running in the main loop. I am a novice and have cobbled this together the best I can but do not know what I need to do to resolve this issue.

The working master version on UNO:

//MASTER UNO TEST ENVIRO
// 

#include <Wire.h>
#define ANSWERSIZE 6




const byte SLV_ADR = 42;
const int clrpn = 7;                     ///////////////assign pin for clear pin
volatile boolean haveData = false;
String response = "";
long correctSteps = 0;
long stepLength = 90000;///test value
const int stepsPerIn = 300;///test value
float encoder = 0.0;
void setup() 
{
  pinMode(clrpn,OUTPUT);
  digitalWrite(clrpn,LOW);
  Wire.begin();
  Serial.begin (115200);
}  // end of setup

void loop(){
  
receiveEvent();

}  // end of loop

// called by interrupt service routine when incoming data arrives


void receiveEvent(){

   Wire.beginTransmission(SLV_ADR);
   Wire.write(0);
   Wire.endTransmission();

   Wire.requestFrom(SLV_ADR,ANSWERSIZE);
   response = "";
   while(Wire.available()){
   
   char c = Wire.read();
   response += c;
   }
   
   encoder = ((response.toFloat())/100); ////encoder is encoder val in inches
  
       
   Serial.println (encoder);  
   
   //correctSteps = (stepLength - (encoder * stepsPerIn));
  // Serial.println (correctSteps);
     // end if have enough data
 }  // end of receiveEvent

The main program with master incorporated, receive function at bottom:

///////////Notes: Pausing still not working, pausing will not keep the quant values returns zero.

//////////RunFeed not working, need to hook a sensor on and check

/////////////

#include <AccelStepper.h>
#include <Wire.h>
#define ANSWERSIZE 6

// Define a stepper and the pins it will use
AccelStepper stepper(AccelStepper::DRIVER, 5,6);//5,6

AccelStepper stepper2(AccelStepper::DRIVER, 10,11);///10,11 ////////////////////////(AccelStepper::FULL4WIRE, 10, 11, 12, 13);
boolean gotserial = false;
byte f2 = 0;
const byte SLV_ADR = 42;                  /////////////////////i2c address for encoder
const int clrpn = 7;                     ///////////////assign pin for clear pin
volatile boolean haveData = false;
String response = "";         //////////////string response for encoder
float encoder = 0.0;            ///////////inch val from encoder

////const int cuton = 6;///pin for cutter power

////////////vars from datastreme
const byte numChars = 120;
char receivedChars[numChars];
char tempChars[numChars];   ///temp array for parsing

//variables to hold parsed data

int integerFromPC = 0;
int integerFromPC2 = 0;
float floatFromPC = 0.0;



//////////////////////////holds pause values

volatile long tempPosition = 0;

int cutspd = 6000;///speed for cutter stepper
long cutdist = 33759;//dist in steps for cutter to move
int mapspeed = 0;
int fmapspeed = 0;
int pmapspeed = 0;
float correctDist = 0;  //encoderval/encoderperin
long correctSteps = 0;
float tlength = 0; /////total length in inches
float distCut = 0;  ///////distance between marks in inches
long stepLength = 0;  ////////////////////////total length in
int driveAccel = 6000; /////////////Acceleration for drive function
int cutAccel = 2000; ////////////////Acceleration for cut speed

float stepsPerIn = 302.0;///////adjust steps per inch
const int sensval = 3; //////////////////photoresistor value
const int enable2 = 4;
const int cutOn = 8;//////////////cutter power pin 8
float encoderPerIn = 0.0 ; ////encoder distance inches
const int cutLimit = 12;


void setup() {
  

  Serial.begin(115200);
  pinMode(cutOn, OUTPUT); ////////////////////////power to cutter
  digitalWrite(cutOn, LOW);
  pinMode(enable2, OUTPUT); ////////////////////////power to cutter
  digitalWrite(enable2, HIGH);
  pinMode(clrpn, OUTPUT); //////////////////clears encoder
  digitalWrite(clrpn, HIGH);
  delay(20);
  digitalWrite(clrpn, LOW);
  delay(20);
  Wire.begin();
  pinMode(sensval, INPUT);
  pinMode(cutLimit, INPUT);

while (digitalRead(cutLimit) == HIGH){
  digitalWrite(enable2, LOW);
  stepper2.setMaxSpeed(900);
  stepper2.setAcceleration(1200);
  stepper2.setSpeed(-900);
  stepper2.runSpeed();
}
 digitalWrite(enable2, HIGH);
 stepper2.setCurrentPosition(0);
}

void loop(){

  getserial();

  if (cutState > 0) {
    Cut();
  }
  if (feedfore > 0) {
    Feedfore();
  }
  if (feedback > 0) {
    Feedback();
  }
  if (runfeed > 0) {
    feedSeq();
  }


  if ((message != lastmsg) || (message == "Resum")) {   
  
      gotserial = false;

      Drive();             
  }  
}

////////////THIS PART OF CODE MISSING

/////////////////////////////////////////i2c receive from encoder///////////////

void receiveEvent() {

  Wire.beginTransmission(SLV_ADR);
  Wire.write(0);
  Wire.endTransmission();

  Wire.requestFrom(SLV_ADR,ANSWERSIZE);
  response = "";
  while (Wire.available()) {

  char c = Wire.read();
  response += c;
  }

  encoder = ((response.toFloat()) / 100); ////encoder is encoder val in inches



   Serial.println (encoder);
  // end if have enough data
}  // end of receiveEvent

The UNO checks the encoder state at a certain point in the operation and gets the value which is correct for a certain distance then returns the same value for a longer distance and when the distance is longer returns zero. I had to cut it down the full code is attached.

JTS_ENCODER_MASTER.ino (1.19 KB)

JTS_ENC_SLV.ino (3.21 KB)

JTS_ENCODER_MASTER.ino (1.19 KB)

Few remarks:
1. If possible and is possible, please replace your String Classes of both Master and Slave with cstrings. I mean: without declaring String response;, declare as char response[7];.

2. From Master, you have been always (in every cycle of the loop() function) sending the command/data byte 0x00; but, you have not collected it at the Slave side. Is there any use of it? There is no harm to include the following lines at the appropriate places of the Slave sketch:

Wire.onReceive(receiveEvent);
void receiveEvent(int howMany)
{

}

3. What value are expecting to receive from Slave in response to Wire.requestFrom() command? Can you give a numerical example?

4. What are the purposes of the interrupt lines of the Slave NANO?

Hi Golam,

Thank you for helping me out. I will try your suggestions tonight. The purpose of the interrupts is for capturing the encoder data without missing pulses. I have tried it without but get inconsistent readings. The slave gets the pulses from an encoder then converts them into inches (float). I run the machine and it will read back the values correctly until 6.1 inches. For example, when the machines stepper runs 2 inches I will get back a reading of 1.99. The difference likely being calibration/rounding errors. If I run the stepper from 6.1 inches to 9.5(approximately) inches I get a reading of 6.1. Above 9.5 inches I get a reading of zero.

If I install the original master program back on the UNO (while using the same nano code) and spin the encoder wheel I get back correct readings consistently no matter the distance.

One more thing that could be a clue or coincidence is that one full revolution of the encoder is 6.1 inches. I can't think of a reason that would matter if it works with the master sketch.

Please, give a link to your encoder device. How many encoder devices are connected with the Slave?

Also, provide the connection diagram of the encoder device with DPin-2, 3 of the Slave.

You have enabled the internal pull-up at DPin-2 (INT0).; so, the default logic is always HIGH. Therefore, the trigger mode would be either FALLING or LOW; it can not be RISING.

One revolution of the encoder is equivalent to 6.1 inch. During one revolution, how many pulses the encoder generates.

To me it appears that something is not correct in your ISRs of INT0 and INT1.

If possible, provide the signals diagrams of the encoder when Motor turns CW and CCW.

I made a mistake in the initial post, I attached the working version of the program twice on accident but forgot to include the main program where I am getting the errors. It is attached now. I am still working on converting the Strings to char arrays. I have am researching that to get a better understanding of the syntax required. When I changed them where declared , then where called in the function dose not work. So I will re-write that part to collect the bytes into the array then convert it into a float. As for the encoder: There is only one and it is ZSP4006-003G-600B-5-240 . I get 1200 pulses per rev. It's a 5-24v NPN encoder 600pulse and I have the wires direct into pins 2&3 on the nano as well as the 5v and ground. OH, I just realized I forgot to put pull up resistors on 2 and 3 as I had in a past project. That could make a difference. So I have a couple of things to work on here I will update you on the progress win or lose.

Thanks,

Joel

Jack_the_Splitter_1.3.ino (16 KB)

I can not proceed my study on your codes without the signal diagrams for the outputs of the encdoer when the motor turns CW and CCW.

Is this what you are looking for? I could not find one in english.

I got this working by taking your initial suggestion. I believe the string variable was the problem. I had written the code around that string variable. I created a union with the long and char and was able to send it over that way. Pretty much redid the whole thing. Its so much fun to watch the encoder correcting the stepper in action. Thanks Golam!! The reworked code below.

/* I2c Master receiving optical rotary encoder ZSP4006-003G-600B-5-24 from Nano to Uno
 
 By Joel Butler 5/3/2020
 warnng im no engineer, feel free to use how you want 

 will send over i2c pins A4 & A5 
 
 */
 
////To use type "1" and enter to get response from slave

#include <Wire.h>
 
// Define Slave I2C Address
#define SLAVE_ADDR 9
 
// Define Slave answer size
#define ANSWERSIZE 5


////union converts the byte array to long, to use: converter.buffer1 OR converter.encoder

union longToBytes{
  
  char buffer1[4];
  long encoder;
  
}converter;

/////////////////////////////////////////////////////////////////////////
char userInput=0;///serial input

////////////////////////////////////////////////Setup//////////////////////////////////
void setup() {
 
Wire.begin();
Serial.begin(9600);

}

/////////////////////////////////////////loop///////////////////////////////////////////
///////////gets data from slave when enter "1" in serial monitor///////////////////////
void loop() {
  
  while (Serial.available()>0){
    userInput = Serial.read();
    Serial.println(userInput);
  }
  if(userInput == '1'){
    getData();
 
}

}
//////////////////////////////////////receive event///////////////////////////////////
void getData(){
  
  //clears buffer
  uint8_t index = 0;
  converter.encoder=0;
  
  //delay(50);////delay required when run from loop

  // Write a character to the Slave
  Wire.beginTransmission(SLAVE_ADDR);
  Wire.write(0);
  Wire.endTransmission();
  
  // Read response from Slave
  // Read back 5 characters
  Wire.requestFrom(SLAVE_ADDR,ANSWERSIZE);
  
  //getting bytes from slave
  while (Wire.available()) {
      converter.buffer1[index] = Wire.read();
      index++;
  } 
  
  long encoderDistance = (converter.encoder);
  
  //converts to float includes two decimal places
  float correctionDistance = (encoderDistance/100.00);
  
  //output to serial
  Serial.println(converter.encoder);
  Serial.println(correctionDistance);
  
}
/*
 I2c Slave sending optical rotary encoder ZSP4006-003G-600B-5-24 from Nano to Uno
 
 By Joel Butler 5/3/2020
 warnng im no engineer, feel free to use how you want 

 will send over i2c pins A4 & A5 
 encoder pins 2 & 3
 
 */
#include <Wire.h>
#define SLAVE_ADDR 9
 
volatile unsigned long counter = 0;
long tcount = 0; 
const int clrpn = 7;  

////union converts the long to byte array, to use: converter.buffer1 OR converter.encoder

union longToBytes{
  
  char buffer1[4];
  long encoder;
  
}converter;

///////////////////////////////////////////////////////////////////////////
 
float encoderPPR = 1200.00;///encoder pulses per rev
float encoderCirc = 6.1;////encoder calibrated at 6.1 inches per revolution

////////////////////////////////////////////SETUP////////////////////////////////
void setup() {

  pinMode(clrpn, INPUT);     
  pinMode(2, INPUT);          
  pinMode(3, INPUT);         

  digitalWrite(2, HIGH);      
  digitalWrite(3, HIGH); 
  digitalWrite(clrpn, LOW);     

  //Setting up interrupt
  //A rising pulse from encodenren activated ai0(). AttachInterrupt 0 is DigitalPin nr 2 on moust Arduino.
  attachInterrupt(0, ai0, RISING);

  //B rising pulse from encodenren activated ai1(). AttachInterrupt 1 is DigitalPin nr 3 on moust Arduino.
  attachInterrupt(1, ai1, RISING);
  
  Wire.begin(SLAVE_ADDR);
  
  // Function to run when data requested from master
  Wire.onRequest(requestEvent); 
  
  // Function to run when data received from master
  Wire.onReceive(receiveEvent);
  

  Serial.begin(9600);

  converter.encoder = 0;
}
 ///////////////////////////////////////////receiveEvent/////////////////////////////
void receiveEvent() {
 
  while (0 < Wire.available()) {
    byte x = Wire.read();
  }

}
 //////////////////////////////////////////requestEvent//////////////////////////////
void requestEvent() {

//encoder value devided by pulses per revolution
float revs = counter/encoderPPR;
//clculates distance based on wheel circumference
float dist = revs * encoderCirc;
//changes to integer from float
long sendDist = dist * 100;

//long integer distance to be sent 
converter.encoder = (sendDist);
//sends byte array from union

Wire.write(converter.buffer1,4);

}
///////////////////////////////////////////loop/////////////////////////////////////
/////////////////////loop empty except getting serial output for debugging//////////

void loop() {
  
resetCounter(); 
/*
float revs = counter/encoderPPR;
float dist = revs * encoderCirc;
int sendDist = dist * 100;
//encoderHolder = (((counter/encoderPPR)*Enc_Circ)*100);
converter.encoder = counter/10;/////devides the encoder value by 10 to fit into long int
if (tcount != counter){
//Serial.println(converter.encoder);
//Serial.println(revs,4);
//Serial.println(dist,4);
Serial.println(sendDist);
}
tcount = counter;
*/
}

//////////////////////////////////////////read encoder pin 3//////////////////////////////////
void ai0() {
  // ai0 is activated if DigitalPin nr 2 is going from LOW to HIGH
  // Check pin 3 to determine the direction
  if (digitalRead(3) == LOW) {
    counter++;
  } else {
    counter--;
  }
  
}
/////////////////////////////////////////read encoder pin 4///////////////////////////////
void ai1() {
  // ai0 is activated if DigitalPin nr 3 is going from LOW to HIGH
  // Check with pin 2 to determine the direction
  if (digitalRead(2)==LOW) {
    counter--;
  } else {
    counter++;
  }
  
}
////////////////////////////////resets counter when pin 7 high///////////////////////////
void resetCounter() { 
  if (digitalRead(clrpn) == HIGH) {
    //Serial.println(answer);
    counter = 0;
    converter.encoder=0;
  }

}

I believe you have external pull-down resistors at the DPin-2, 3 of slave. It is recommended to keep this information in the comment field like:

pinMode(2, INPUT);   //with external poll=down      
  pinMode(3, INPUT);  //with external pull-down