Go Down

Topic: Wire lib question: as I2C slave, how to read the request from master ? (Read 151 times) previous topic - next topic

AndreK

Please see attachment.
My I2C bus has a master that attempts to read from device address 0x0B , command 0x28.

So I have my little test program:

Code: [Select]
uint8_t req;
#include <Wire.h>

void setup()
{
 Serial.begin(115200);
 Wire.begin(0x0B);
 Wire.onRequest(requestEvent);
}

void loop()  {  delay (10); }

void requestEvent()
{
 Serial.print ("Req: ");
req=Wire.read();
 Serial.println(req);
req=Wire.read();
 Serial.println(req);
}




I expected it to respond to request to device 0x0B from master, then read the second byte (in screenshot 0x28)  , and respond accordingly .. to the command.

My program does enter apparently see a request, and terminal is filled with "255" as everything I try to read is "255"

How can I get the second byte of the request from master ?

gfvalvo

1. Really? 167 posts on the forum and you don't know to post code using Code Tags?

2. For a master to issue a command (like 0x28) and then read data from the Slave, it would have to first initiate an I2C write sequence to Slave 0x0B and send the data byte 0x28. It would then initiate a Repeated Start and I2C read request. So, your code will probably need to implement both Slave Receiver and Slave Sender. The former will receive and interpret the 0x28 command and then set up properly to handle the subsequent read sequence.
No technical questions via PM. They will be ignored. Post your questions in the forum so that all may learn.

GolamMostafa

Assuming that UNO is the I2C Master and NANO is the I2C Slave with address 0x0B; UNO will send the command byte 0x28 to NANO; in response, NANO will send the message "I am OK." to UNO. Here is the structure of codes (untested):

UNO-Master Codes:
Code: [Select]
#include<Wire.h>
char myMsg[10];

void setup()
{
    Serial.begin(9600);
    Wire.begin();
     Wire.onReceive(receiveEvent);
    //-------------------------
    Wire.beginTransmission(0x0B);
    Wire.write(0x28);                       //command byte
    byte busStatus = Wire.endTransmission();
    if(busStatus !=0)
    {
        Serial.print("I2C Bus communication problem...!");
        while(1);    //wait for ever
    }
    Serial.println("I2C Bus OK.");
    Wire.requestFrom(0x0B, 8);   //requesting 8-byte data from Slave
    for (byte i=0; i<8; i++)
    {
       myMsg[i] = Wire.read();
    }    
    myMsg[8] = '\0';  //null character
    Serial.print(myMsg);      //shows: I am OK.
}

void loop()
{
  
}


NANO-SLave Codes:
Code: [Select]
#include<Wire.h>
volatile bool flag = false;

void setup()
{
    Serial.begin(9600);
    Wire.begin(0x0B);
    //-------------------------
    Wire.onReceive(receiveEvent);
}

void loop()
{
    if(flag == true)
    {
        Wire.print("I am OK.");  //storing into transmit FIFO Buffer
        flag = false;
    }

}

void receiveEvent(int howMany)
{
    byte x = Wire.read(); //getting from receive FIFO Buffer
    if(x == 0x28)
    {
        flag = true;
    }
}

AndreK

Thank you.
I figured out that I should use Wire.onReceive , not "onRequest"
Then I could do  a .read and received the command byte just fine.

Then I will proceed to respond to the master by Wire.send  from the Arduino slave, sending the expected data.
The master does know the number of bytes expected, so after sending, do I need to call something to tell the master that the slave is done sending ?



gfvalvo

The master does know the number of bytes expected, so after sending, do I need to call something to tell the master that the slave is done sending ?
No, the master controls the whole process even when it's receiving. When it receives the last byte that it wants, it sends a NACK (instead of an ACK) followed by the Stop condition.
No technical questions via PM. They will be ignored. Post your questions in the forum so that all may learn.

GolamMostafa

If the Master does not know the expected number of bytes to receive from Slave, why is he executing the following code?
Code: [Select]
byte m = Wire.requestFrom(slaveAddress, n);

where:
n = number of expected/requested data bytes
m = actual number of bytes received (communication link may fail and then m < n).

GolamMostafa

I figured out that I should use Wire.onReceive , not "onRequest"
Wire.onReceive(receiveEvent) is triggered in response to --
Code: [Select]
Wire.beginTransmission(slaveAddress);
Wire.write(arg);
Wire.endTransmission();

The Slave goes to the receiveEvent() interrupt context, sets a flag to allow the loop() function to execute Wire.read() codes for collecting data from the local FIFO Buffer and processing them.

Wire.onRequest(sendEvent) is triggered in response to --
Code: [Select]
Wire.requestFrom(slaveAddress, n);
The Slave goes to sendEvent() interrupt context, sets a flag to allow the loop() function to execute Wire.write/print() codes to store data onto the local FIFO Buffer of the Master.

AndreK

You are correct about NACK,
my problem is now that with the code
Code: [Select]
uint8_t req;
#include <Wire.h>

void setup()
{
 Serial.begin(115200);
 Wire.begin(0x0B);
 Wire.onRequest(requestEvent);
}

void loop()  {  delay (10); }

void receiveEvent(int howMany)
{
 int x = Wire.read();    // receive command byte
  if (x == 0x28) { sendresponse(); }




In the attached screenshot:
I still see the proper request from the master to the slave (11d 28d)  - then I would expect some idle time. (but instead, my   Wire.write(0x0B); hammers out 0x0b (11d) right away.
then the row of SCL pulses for 11 bytes follows, but with no data.  (that is the 11 bytes my slave(Arduino) was supposed to reply to the master's request)

I suspect sending a response by using .write is just wrong,I should have put those data into a buffer that would transmit them on master's request?

GolamMostafa

State very clearly --
1.  What is your Master -- UNO, NANO, MEGA, DUE or Node?
2.  What is your Slave -- UNO, NANO, MEGA, DUE, Node or sensor/device?
3.  How have connected them -- UART, I2C or SPI?
4.  What is the 7-bit the I2C Slave address?
5.  What message/data do you want to send from Master to Slave?
6.  What message do you expect to receive from Slave?
7.  Are there any code based manual handshaking between Master and Slave? If so,state those.

You observe the results first and then you can study the timings of the I2C Communication using scope/logic analyzer -- an academic study.

AndreK

most of those questions are answered above:
1 master is some other electronics, controller is not relevant , but ARM based.
2: the Nano (328P) is supposed to behave as a slave.
3: I2C
4: 0x0B
5: the master (beyond my control) sends a Write to 0x0B with a command as the second byte.   The Slave (Arduino) must respond to word/block request  by sending Read 0x0B and the rest of the response depends on the command byte - response is 4--12 bytes including 8-bitCRC.
6: see above
7: no special handshake, normal 7-bit I2C



my testcode now is something like:

Code: [Select]

uint8_t req;
bool requested=false;
#include <Wire.h>

void setup()
{
 Wire.begin(0x0B);
 Wire.onRequest(requestEvent);
 Wire.onReceive(receiveEvent);
}

void loop()  {  delay (10); }

void receiveEvent(int howMany)
{
 int x = Wire.read();    // receive command byte
  if (x == 0x28) { prepare28(); requested=true; }
  if (x == 0x29) { prepare29(); requested=true; }

}



void requestEvent()
{
  if (requested == true) {
    Wire.write(Buffer, Buff_p);  //write data response for the recieived command
    requested = false;
  }
}





The challenges are:
1: whenever I register Arduino as slave, ( Wire.begin(0x0B);  )    it will automatically respond to any request, if the buffer is not filled yet,  the  Wire.onRequest(requestEvent);   Wire.onReceive(receiveEvent);   are commented out, there is NO expectation of anything working... yet see  screenshot "slave nothing more".

Ardunio will respond with some garbage or empty buffer anytime once registered as slave.

2: if I *uncomment* the two Wire.onRe*  lines,  and have no handling for command 0x90 , yep, Arduino will still respond to 0x90.

3: this ... is so far not the main issue that prevents me from emulating the I2C slave I emulate..  what is worse, is that the resonse have no option to inter-byte delay (the unmodified device waits 0.1ms ) between bytes, I suspect that the fact Arduino responds "instantly" may be causing some of the strange dropouts.

In other words : I wish for more granular I2C interaction.  I imagine this is somehow achievable:
on master Write master contacts slave, a function is called, I check the command, then I am free to send bytes in response at the desired rate (with delay, if needed).  


4: I do not understand why it seems to be necessary to use the "requested" flag. 




Go Up