i2C Slave stuck in wire.read

Folks,

Have a situation here....have an i2C set up where the master is a Mega and the slave is a micro.
I successfully sent a message to the master, the message consist of few buttons statuses. Then trying to sent a variable to from the master to the slave. Everything appears to work fine (communications wise) but the buttons status doesn't change at all if the corresponding button is pressed, it looks like the slave code is stuck on the wire.read. If I remove the the part of the code that do the wire.read I can touch the buttons and the corresponding LED's will lit, I can also read the data sent from the slave to the master on the master serial monitor, but the minute I add the wire.read, the buttons are not read and the LED's dont get energize. The issue is on the subroutine i2cMessage_From_Master()

Slave Code:

// Libraries
#include <Button.h>   // Library for buttons
#include <SPI.h>
#include <Wire.h>   // Library for i2C communications




//Configure DI to pin #
#define StartPin 1          //Connect a tactile button switch (or something similar)
#define ParkPin 4           //Connect a tactile button switch (or something similar)
#define ReversePin 5        //Connect a tactile button switch (or something similar)
#define NeutralPin 6        //Connect a tactile button switch (or something similar)
#define DrivePin 7          //Connect a tactile button switch (or something similar)
#define ebrakePin 11        //Connect a tactile button switch (or something similar)
#define AWDPin 12          //Connect a tactile button switch (or something similar)





#define PULLUP true        //To keep things simple, we use the Arduino's internal pullup resistor.
#define INVERT true        //Since the pullup resistor will keep the pin high unless the
                           //switch is closed, this is negative logic, i.e. a high state
                           //means the button is NOT pressed. (Assuming a normally open switch.)
#define DEBOUNCE_MS 100    //A debounce time of 100 milliseconds usually works well for tactile button switches.


Button StartSignal (StartPin, PULLUP, INVERT, DEBOUNCE_MS);       
Button DriveButton (DrivePin, PULLUP, INVERT, DEBOUNCE_MS);       
Button ReverseButton (ReversePin, PULLUP, INVERT, DEBOUNCE_MS);     
Button ParkButton (ParkPin, PULLUP, INVERT, DEBOUNCE_MS);           
Button NeutralButton(NeutralPin, PULLUP, INVERT, DEBOUNCE_MS);   
Button eBrakeButton(ebrakePin, PULLUP, INVERT, DEBOUNCE_MS); 
Button AWDButton(AWDPin, PULLUP, INVERT, DEBOUNCE_MS); 
 


// Declare Global Variables 
  bool eBrakeEngage;
  bool ParkMode;
  bool AWDMode;
  bool NeutralMotion;
  bool IgnitionStatus;
  bool SystemReady;
  bool ForwardMotion;
  bool ReverseMotion;

  int Speed = 0;              //Hard coded for now, eventually this will be a value coming to this Arduino via CAN Msg. Have to code the "read CAN message sub routine 1st
  int modbus = 0;       // hange this to the desired Communication method. 0 = i2C Communications   1 = Modbus (RS485) Communications
  int ButtonStatus [6];  // Store all Button Status to be sent on the i2C bus.
 
  
  const int PrechargeDelay = 2000;  //Configure this value to make sure is long enough the system reached precharge state 
  const int Slave_Address = 10;      // This Controller Address on the RS485 Network
  const int Tx_Rx = 12;               //HIGH = Transmit Data (Tx); LOW = Receive Data (Rx)

//Declare I/O pins 
 //Digital Outputs
   int Start_OutPin = 8;           //Physical Pin #13, , Digital pin 8, also A8  
   int FWD_S_OutPin = 9;           //Physical Pin #13, Digital pin 9, also A9   
   int RWD_S_OutPin = 10;           //Physical Pin #14, Digital pin 10, also A10      
   int PRK_LED1OutPin = 18;       //Physical Pin #21, Digital pin 18, Analog A0 
   int RWD_LED1OutPin = 19;       //Physical Pin #22, Digital pin 19, Analog A1 
   int NTRL_LED1OutPin = 20;      //Physical Pin #23, Digital pin 20, Analog A2
   int FWD_LED1OutPin = 21;       //Physical Pin #24, Digital pin 21, Analog A3

void setup() 
{
  // Configure each Pin as Input or Output
  
    // Inputs. All inputs are configured as INPUT_PULLUP, this make inputs to be active low. This way will prevent any noise triggering the input in the case of active high. 
      pinMode(ReversePin, INPUT_PULLUP);   // declare ReversePin as INPUT_PULLUP, the pin will be active with a logic "0"
      pinMode(DrivePin, INPUT_PULLUP);     // declare DrivePin as INPUT_PULLUP, the pin will be active with a logic "0"
      pinMode(ParkPin, INPUT_PULLUP);      // declare ParkPin as INPUT_PULLUP, the pin will be active with a logic "0"
      pinMode(NeutralPin, INPUT_PULLUP);   // declare NeutralPin as INPUT_PULLUP, the pin will be active with a logic "0"
      pinMode(ebrakePin, INPUT_PULLUP);     // declare StartPin as INPUT_PULLUP, the pin will be active with a logic "0"
      pinMode(AWDPin, INPUT_PULLUP);     // declare StartPin as INPUT_PULLUP, the pin will be active with a logic "0"
      
    //Outputs 
 
      pinMode(RWD_S_OutPin, OUTPUT);   
      pinMode(FWD_S_OutPin, OUTPUT);     
      pinMode(Start_OutPin, OUTPUT);         
      pinMode(PRK_LED1OutPin, OUTPUT);     
      pinMode(RWD_LED1OutPin, OUTPUT);         
      pinMode(NTRL_LED1OutPin, OUTPUT);     
      pinMode(FWD_LED1OutPin, OUTPUT); 
      pinMode (Tx_Rx,OUTPUT);         


   // Turn OFF all outputs.

      digitalWrite(RWD_S_OutPin,LOW);
      digitalWrite(FWD_S_OutPin,LOW);
      digitalWrite(Start_OutPin,LOW);
      digitalWrite(PRK_LED1OutPin,LOW);
      digitalWrite(RWD_LED1OutPin,LOW);
      digitalWrite(NTRL_LED1OutPin,LOW);            
      digitalWrite(FWD_LED1OutPin,LOW);
      digitalWrite(Tx_Rx,LOW);
      
   // Start applicable Communications
    
      Serial.begin (9600);   // Local Comms with Arduino IDE Serial Monitor, This could be commented out once full functionality has been proven 
      
      //Uncomment if Modbus comms is desired 
      //Serial1.begin(9600);  // Start RS485 Comms 
       
      // Comment out the next 3 instructions  if Modbus communications is desired
      Wire.begin(Slave_Address);         // Start i2C communications with respective address. Blank () argument is in Master Mode, (#) is in slave Mode
      Wire.onReceive(i2cMessage_From_Master);      // Register an Event handler for "incoming" Data from Master. Receive Data from Master 
      Wire.onRequest (i2cMessage_To_Master);     // Register an Event handler for  Data "request" from Master. Send Data to Master
    
 // Initialize all variables 
 IgnitionStatus = 1;                // Since this arduino will be powered by Ignition, set Ignition Status to "1" (ON. No need to waste an input on this.
 ParkMode = 1;                      // Set the system to be in park mode 
 delay(PrechargeDelay);              //delay to make sure the system is precharged.
 SystemReady = 1;  
 delay (250);
 digitalWrite(Start_OutPin,HIGH);      // System should be Pre-charge. Send Ready/Start Signal (pulse) to both F-SDU & R-SDU
 digitalWrite(PRK_LED1OutPin,HIGH);
 delay (2000); 
 digitalWrite(Start_OutPin,LOW);     // System should be Pre-charge. Turn OFF Ready/Start
}

void loop() 
{
  
 
  Read_Buttons();
  Output_Update();
 
    
    
    // Uncomment the next two instructions if Modbus coomunications are desired 
    //ModbusMessage_To_Master();    
    //ModbusMessage_From_Master();

    
     
}



void Read_Buttons()
{
  // Avoid inadvertnly turning a button OFF by pressing the button twice. The mode or direction can be only be override by another button. 
  //Example1: if doing forward motion and the forward motion button is pressed by mistake, nothing will happend, if the 1st "if" condition is removed, the system will go out of "forward motion" because that will toggle 
  // the variable value.
  //Example2: If the syetm is in Froward motion and speed = 0 then reverse button can be pressed, then  forward motion = 0 and Reverse motion = 1
   
   if (ForwardMotion == 0 & Speed == 0 )  //Do not check for the button if the system is already in the mode or motion governed by the button                  
    {
      DriveButton.read();
      if (DriveButton.wasReleased())         //When the Signal goes OFF
          {  
            NeutralMotion = 0;
            ParkMode = 0;  
            ReverseMotion = 0;   
            ForwardMotion = !ForwardMotion;
           //Serial.println ("Forward Motion Selected ");
          }
    }

    if (ReverseMotion == 0 & Speed == 0)  //Do not check for the button if the system is already in the mode or motion governed by the button                  
      {
        ReverseButton.read();
        if (ReverseButton.wasReleased())         //When the Signal goes OFF
          { 
            NeutralMotion = 0;
            ParkMode = 0;
            ForwardMotion = 0;      
            ReverseMotion = !ReverseMotion;
           // Serial.println ("Reverse Motion Selected ");
          }
      }

    if (NeutralMotion == 0 & Speed == 0)  //Do not check for the button if the system is already in the mode or motion governed by the button                  
      {
        NeutralButton.read();
        if (NeutralButton.wasReleased())         //When the Signal goes OFF
          { 
            ParkMode = 0;
            ReverseMotion = 0;
            ForwardMotion = 0;      
            NeutralMotion = !NeutralMotion;
            //Serial.println ("Neutral Motion Selected ");
          }
      }

    if (ParkMode == 0 & Speed == 0)  //Do not check for the button if the system is already in the mode or motion governed by the button                  
      {
 
        ParkButton.read();
        if (ParkButton.wasReleased())         //When the Signal goes OFF
          { 
            ReverseMotion = 0;
            ForwardMotion = 0;
            NeutralMotion = 0;      
            ParkMode = !ParkMode;
            //Serial.println ("Park Mode Selected ");
      
          }
      }

        if ( Speed == 0)  //Do not check for the button if the system is already in the mode or motion governed by the button                  
         {
 
        AWDButton.read();
        if (AWDButton.wasReleased())         //When the Signal goes OFF
          { 
            //ReverseMotion = 0;
            //ForwardMotion = 0;
            //NeutralMotion = 0;      
             AWDMode = !AWDMode;
            //Serial.println ("AWD Mode Selected1 ");
      
          }
      }
         if ( Speed == 0)  //Do not check for the button if the system is already in the mode or motion governed by the button                  
         {
 
        eBrakeButton.read();
        if (eBrakeButton.wasReleased())         //When the Signal goes OFF
          { 
            //ReverseMotion = 0;
            //ForwardMotion = 0;
            //NeutralMotion = 0;      
            eBrakeEngage = !eBrakeEngage;
            //Serial.println ("eBrake Engage ");
      
          }
      }

}

void Output_Update()
{

// *********************************Parking***************************************
 if ( IgnitionStatus == 1 & SystemReady == 1 & Speed == 0 & ParkMode == 1 ) // This will only happend when the system is power up by the ignition button
     {
        digitalWrite(RWD_S_OutPin,LOW);        
        digitalWrite(RWD_LED1OutPin,LOW);      
        digitalWrite(NTRL_LED1OutPin,LOW); 
        digitalWrite(FWD_S_OutPin,LOW);
        digitalWrite(FWD_LED1OutPin,LOW);       
        digitalWrite(PRK_LED1OutPin,HIGH);       // This is for testing purposes only, but ouput supposed to be OFF. If Output OFF, Only RED LED that is connected directly to VCC will be lit 
     }

 // *********************************Reverse Motion***************************************
    if ( IgnitionStatus == 1 & SystemReady == 1 & ReverseMotion == 1 & ForwardMotion == 0  & NeutralMotion == 0 & Speed == 0 & ParkMode == 0 ) 
     {
        digitalWrite(PRK_LED1OutPin,LOW);         
        digitalWrite(NTRL_LED1OutPin,LOW);     
        digitalWrite(FWD_S_OutPin,LOW);
        digitalWrite(FWD_LED1OutPin,LOW);         
        digitalWrite(RWD_S_OutPin,HIGH);      
        digitalWrite(RWD_LED1OutPin,HIGH);   
        //Serial.println(" System In Reverse Motion ");
     }

 // *********************************Neutral Motion***************************************

      if ( IgnitionStatus == 1 & SystemReady == 1 & ReverseMotion == 0 & ForwardMotion == 0 & NeutralMotion == 1 & Speed == 0 & ParkMode == 0  ) 
     {
        digitalWrite(PRK_LED1OutPin,LOW); 
        digitalWrite(RWD_S_OutPin,LOW);     
        digitalWrite(RWD_LED1OutPin,LOW);         
        digitalWrite(FWD_S_OutPin,LOW);       
        digitalWrite(FWD_LED1OutPin,LOW);    
  
        digitalWrite(NTRL_LED1OutPin,HIGH);      
        //Serial.println(" System In Neutral Motion ");
     }

 // *********************************Forward Motion***************************************
    if ( IgnitionStatus == 1 & SystemReady == 1 & ReverseMotion == 0 & ForwardMotion == 1 & NeutralMotion == 0 & Speed == 0 & ParkMode == 0  ) 
      {
        digitalWrite(PRK_LED1OutPin,LOW);  
        digitalWrite(RWD_S_OutPin,LOW);    
        digitalWrite(RWD_LED1OutPin,LOW);       
        digitalWrite(NTRL_LED1OutPin,LOW);     
        digitalWrite(FWD_S_OutPin,HIGH);      
        digitalWrite(FWD_LED1OutPin,HIGH);     
        //Serial.println("System In Forward Motion ");        
      } 

 // *********************************AWD Mode***************************************
    //if ( IgnitionStatus == 1 & SystemReady == 1 & Speed == 0 & AWDMode == 1) 
      //{
        // Make sure Button Works but for now nothing will be done. I'm planning running the car in AWD at all times
        //Serial.println(" AWD Mode Selected ");        
      //} 

  // *********************************Ebrake***************************************
    if ( IgnitionStatus == 1 & SystemReady == 1 & Speed == 0 ) 
      {
        // Make sure Button Works but for now nothing will be done. I'm planning running the car in AWD at all times
  
        //Serial.println(" Ebrake Engage ");        
      }
  
}

/*void ModbusMessage_To_Master()
{
 //digitalWrite(Tx_Rx,HIGH);   // HIGH = Transmitting Data to  Serial
 Serial1.print ("<");
 Serial1.print (Slave_Address);
 Serial1.print (";");
 Serial1.print (ParkMode);
 Serial1.print (";");
 Serial1.print (ReverseMotion);
 Serial1.print (";");
 Serial1.print (NeutralMotion);
 Serial1.print (";");
 Serial1.print (ForwardMotion);
 Serial1.print (";");
 Serial1.print (eBrakeEngage);
 Serial1.print (";");
 Serial1.print (AWDMode);
 Serial1.print (">");
 Serial1.println (" ");
 digitalWrite(Tx_Rx,LOW);   // LOW = Receiving Data from Serial
 delay (5);

}

void ModbusMessage_From_Master()
{

  digitalWrite(Tx_Rx,LOW);      // LOW = Receiving Data from Serial
  if(Serial1.available())        // Check if there is Data in the serial buffer
   {
    if(Serial1.read()=='<')      // New Message from Master. 
     {
        int TargetAddress = Serial1.parseInt();    // 
        if ( TargetAddress == Slave_Address)
          {
            Serial.println (" We have a message from Master");
            Speed = Serial1.parseInt();
            Serial.println (Speed);
          }
        else 
         {
           Serial.println (" No New Message");
         }
     }
  
   }
}
*/
void i2cMessage_From_Master()
{

  if (Wire.available()) 
    { 
      
      Speed = Wire.read();
    
      Serial.print("Speed =  ");
      Serial.println (Speed);
     
    }
  
}

// requests data handler function
void i2cMessage_To_Master()
{
   ButtonStatus [0] = ParkMode ; 
   ButtonStatus [1] = ReverseMotion;
   ButtonStatus [2] = NeutralMotion;
   ButtonStatus [3] = ForwardMotion;
   ButtonStatus [4] = eBrakeEngage;
   ButtonStatus [5] = AWDMode;
   //ButtonStatus [6] = 170;    // for whatever reason this one make the outputs to randomly go ON & OFF
  for (int i = 0; i <= 7; i++)
    {
    Wire.write(ButtonStatus [i] ); // Send to Master all button Status
    }
}

Master Code:

#include <Wire.h>   // Library for i2C communications
int ButtonStatus [6];
int Speed;
void setup() 
{
  Wire.begin(); //Blank () argument is in Master Mode, (#) is in slave Mode
  Serial.begin (9600);
  Speed = 0;
}

void loop() 
{

 i2CMessage_From_Slave();
 i2CMessage_To_Slave();
 Speed = Speed + 1 ;
 if (Speed >= 200)
  {
    Speed = 0;
  }
   
}

void i2CMessage_From_Slave()
{
    Wire.requestFrom(10, 6);    // Request button Status from slave 10, data size = 6 INT
  
  if(Wire.available()) 
    {   
        for (int i = 0; i <= 6; i++)
          {   
            ButtonStatus [i] = Wire.read();
            Serial.println (ButtonStatus [i]);
          }
    }
}

void i2CMessage_To_Slave()
{
  Wire.beginTransmission(10);     // informs the bus that we will be sending data to slave device 10 
  Wire.write(Speed);              // send Speed Value
  Wire.endTransmission();        // informs the bus and the slave device that we have finished sending data
 
}

Which library are you using? The OnReceive callback takes an argument howMany and should read exactly that number of bytes.

You should not try to send more bytes than requested. Exit from the loop as soon as Wire.write() returns an error code (0).

Diettrich,
the problem is on the slave code...on the subroutine

void i2cMessage_From_Master()
{

  if (Wire.available()) 
    { 
      
      Speed = Wire.read();
    
      Serial.print("Speed =  ");
      Serial.println (Speed);
     
    }
  
}

if I take off the wire.read, the button are being read and the corresponding LED's are lit, but if leave the "wire.read" the buttons and LED's don't update.

Sorry about my ignorance but I'm not clear when you are saying "The OnReceive callback takes an argument how Many and should read exactly that number of bytes."
The subroutine supposed to read an integer coming from the master, so you are suggesting that I put an argument like so:

void i2cMessage_From_Master(2)
{

  if (Wire.available()) 
    { 
      
      Speed = Wire.read();
    
      Serial.print("Speed =  ");
      Serial.println (Speed);
     
    }
  
}

No. See the IDE WireSlave example.

The Master has no delay in the loop(), that means you are constantly pounding the I2C bus with I2C sessions. A slow Arduino board as a I2C Slave should have "time to breath". See my notes on Github: Arduino in Target mode · Koepel/How-to-use-the-Arduino-Wire-library Wiki · GitHub

The Slave has "Serial" functions in the onReceive handler.
The Wire library is interrupt driven. Both the onReceive and onRequest handlers (your i2cMessage_From_Master and i2cMessage_To_Master functions) are called from a interrupt.
The "Serial" of a Arduino Micro board is a serial-over-usb via USB hardware inside the microcontroller. It is scary to call such a "Serial" function from a interrupt. Can you remove the Serial.print() and Serial.println() ?

There is a third thing: I don't know how well the Micro boards is doing as a I2C Slave. I assume it can work.

1 Like

I tried what you suggest still in the same boat. also tried what @Koepel suggested as well, no luck, still doing the same. The slave is receiving the value from the master but is not able to update the outputs See the modifications for the Master & Slave as per your recommendations.

Master

void i2CMessage_To_Slave()
{
  Wire.beginTransmission(10);     // informs the bus that we will be sending data to slave device 10 
  Wire.write(Speed);              // send Speed Value
  Wire.endTransmission();        // informs the bus and the slave device that we have finished sending data
  delay (100);
}

Also increase the delay up to 500 ms (more than that is undesirable for my application) still the same.

Slave code

void i2cMessage_From_Master(int howmany)
{
 
  while ( Wire.available()) 
    { 
      
      Speed = Wire.read();
    
      Serial.print("Speed =  ");
     Serial.println (Speed);
     
    }
  
}

Why that? It may block the slave answer.

You already have been advised not to use Serial in an event handler.

===>

byte ButtonStatus [6];  // Store all Button Status to be sent on the i2C bus.`

====>

void i2cMessage_From_Master(int howMany)
{
     {
       Speed = Wire.read();
       flag = true;  //use this globally declared flag in the loop() function to print speed
     }
}

===>

void i2cMessage_To_Master()
{
   Wire.write(digitalRead(ParkMode)) ; 
   Wire.write(digitalRead(ReverseMotion));
   Wire.write(digitalRead(NeutralMotion));
   Wire.write(digitalRead(ForwardMotion));
   Wire.write(digitalRead(eBrakeEngage));
   Wire.write(digitalRead(AWDMode));
}

}

===>
  bytel eBrakeEngage;
  byte ParkMode;
  byte AWDMode;
  byte NeutralMotion;
  byte ForwardMotion;
  byte ReverseMotion;

  byte Speed = 0;

===>

void loop() 
{
 i2CMessage_From_Slave();
 i2CMessage_To_Slave();
 Speed = Speed + 1 ;
 if (Speed == 200)
  {
    Speed = 0;
  }
  delay(100);  
}

:index_pointing_at_the_viewer: ROCK....I like your way!!! pointing what I'm doing wrong and MOST important... how can be fixed. I'll implement all this suggestions and see how it goes.

Thanks

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.