[SOLVED] i2c Master (Receiving - Requesting Data)

Hi there,

I'm trying to get data from my Slave device, if I put the code inside the "Loop", it's working, but I don't want an infinity loop on the Serial monitor, so I'm trying something else, but it's not showing nothing on the Serial monitor.

Any thoughts maybe?

Master code for receiving data:

#include "Wire.h"

#define I2C_ADDR  0x1A

String wholeData = "";
volatile bool received=0;

void setup() {
  Serial.begin(9600);
  Wire.begin();
  Wire.onReceive(receiveEvent);
}

void loop()
{
  Wire.requestFrom(I2C_ADDR, 1);

  if (received){
    Serial.println(F("receiveEvent Triggered"));
    Serial.print(F("wholeData="));
    Serial.println(wholeData);
    received = false;
  }

  delay(100);
}

void receiveEvent(int howMany)
{
  received = true;
  
  while (Wire.available())
  { 
    char inChr = Wire.read();
    wholeData += inChr;
  }

}

Regards

but I don't want an infinity loop on the Serial monitor,

So what do you want? Can't help much until we know.

Using the Master Reader example ( no receiveEvent necessary):

#include "Wire.h"

#define I2C_ADDR  0x1A

String wholeData = "";
volatile bool received=0;

void setup() {
  Serial.begin(9600);
  Wire.begin();
 }

void loop()
{
  Wire.requestFrom(I2C_ADDR, 1);

  while (Wire.available())
   {
     char inChr = Wire.read();
     wholeData += inChr;
   }

  Serial.println(F("receiveEvent Triggered"));
  Serial.print(F("wholeData="));
  Serial.println(wholeData);
  received = false; 

  delay(100);
}

groundFungus:
So what do you want? Can't help much until we know.

Using the Master Reader example ( no receiveEvent necessary):

#include "Wire.h"

#define I2C_ADDR  0x1A

String wholeData = "";
volatile bool received=0;

void setup() {
  Serial.begin(9600);
  Wire.begin();
}

void loop()
{
  Wire.requestFrom(I2C_ADDR, 1);

while (Wire.available())
  {
    char inChr = Wire.read();
    wholeData += inChr;
  }

Serial.println(F("receiveEvent Triggered"));
  Serial.print(F("wholeData="));
  Serial.println(wholeData);
  received = false;

delay(100);
}

Thank you for your fast feedback! :wink:

Yes, I had almost the same code approach as yours, but I wanted to do trough ISR procedure without a Loop if it is possible, because in the Serial terminal window, even there is no data received from the i2c request, it's scrolling down.

I moved one curly bracket to put the print statements inside the while available loop. Should have done it before, sorry. So now it should only print when data comes in.

#include "Wire.h"

#define I2C_ADDR  0x1A

String wholeData = "";
volatile bool received=0;

void setup() {
  Serial.begin(9600);
  Wire.begin();
 }

void loop()
{
  Wire.requestFrom(I2C_ADDR, 1);

  while (Wire.available())
   {
     char inChr = Wire.read();
     wholeData += inChr;
  
   //} moved to put prints inside loop   

    Serial.println(F("receiveEvent Triggered"));
    Serial.print(F("wholeData="));
    Serial.println(wholeData);
  } //******* now print in while loop

  delay(100);
}

groundFungus:
I moved one curly bracket to put the print statements inside the while available loop. Should have done it before, sorry. So now it should only print when data comes in.

#include "Wire.h"

#define I2C_ADDR  0x1A

String wholeData = "";
volatile bool received=0;

void setup() {
  Serial.begin(9600);
  Wire.begin();
}

void loop()
{
  Wire.requestFrom(I2C_ADDR, 1);

while (Wire.available())
  {
    char inChr = Wire.read();
    wholeData += inChr;
 
  //} moved to put prints inside loop

Serial.println(F("receiveEvent Triggered"));
    Serial.print(F("wholeData="));
    Serial.println(wholeData);
  } //******* now print in while loop

delay(100);
}

Nah, I'm still getting an infinite endless empty fast loop.

I wish only to print when, if I got any data requested and it's not empty.

You never clear wholeData, so once you receive a byte, you will print that same character over and over.

You request a byte on every pass through loop(). What makes you think that the slave isn't responding with a byte?

PaulS:
You never clear wholeData, so once you receive a byte, you will print that same character over and over.

You request a byte on every pass through loop(). What makes you think that the slave isn't responding with a byte?

Yes, I noticed all of that what you mentioned, but I don't know how to achieve that through ISR.

Did you see my first posted code with those combinations bellow?

Wire.onReceive(receiveEvent);

...

Wire.requestFrom(I2C_ADDR, 1);

...

void receiveEvent(int howMany)
{
  received = true;
  
  while (Wire.available())
  { 
    char inChr = Wire.read();
    wholeData += inChr;
  }

}

But, I just can't get it work.

But, I just can't get it work.

The original code was talking to something unknown.

That something may, or may not, have generated a response to an I2C request.

The response may, or may not, have triggered the onReceive event handler.

"It doesn't work" gives us nothing to go on to try to help you.

PaulS:
The original code was talking to something unknown.

That something may, or may not, have generated a response to an I2C request.

The response may, or may not, have triggered the onReceive event handler.

"It doesn't work" gives us nothing to go on to try to help you.

To simplify it all, I just wanna get/request one char from the Slave device, and the Slave device is sending only one char (there is no other option from the Slave device), nothing else.

Without Loop, but with onReceive event when I request it, if it is possible.

Regards

beic:
To simplify it all, I just wanna get/request one char from the Slave device, and the Slave device is sending only one char (there is no other option from the Slave device), nothing else.

Without Loop, but with onReceive event when I request it, if it is possible.

Regards

So, what DOES the code in the initial post actually do?

If the slave sends ONE character, why do you need a String to store one character?

PaulS:
So, what DOES the code in the initial post actually do?

Wanted to request one char from the slave and to display it on the Serial monitor.

PaulS:
If the slave sends ONE character, why do you need a String to store one character?

Because I'm a VB programmer and I'm using variables and strings for data processing ... (unfortunately I'm not a C guru).

Please post the slave code.

So far, all we have is a description of what you want. No description of what actually happens. We can not help you fix the code without knowing what is wrong with it.

Do you KNOW that the slave gets the request for a single byte? How do you know that?

Do you KNOW that the onReceive handler does, or does not, get called? How do you know that?

Your master code for receiving data is trying to use Wire.onReceive(receiveEvent) as a master function. It is typically a function used by the slave.

I'm not sure it is even called in the master when data arrives in response to Wire.requestFrom().

If the master is receiving data you don't want, then the solution is to manage how Wire.RequestFrom() is used.

Yeah...

However, this code bellow is working almost like I wish, but there is a Serial scrolling again....agrrrr

#include "Wire.h"

#define I2C_ADDR  0x1A

void setup() {
  Wire.begin();
  Serial.begin(9600);
}

void loop() {
  Wire.requestFrom(I2C_ADDR, 1);
  
  while (Wire.available())
  {
    char data = Wire.read();
    int len = strlen(data);
    if(len>=0){
      Serial.println(data);
      memset(data, 0, sizeof(data));
    }
  }
  delay(100);
}

Here's a way to control the request for data. Your code will need to set requestNewData= true when you want to request new data.

You do not need to test for Wire.available.The Wire.requestFrom function does not return until either the requested data is fully available, or an error occurred. Building in a wait for Wire.available simply makes it possible for the code to hang forever if the data is not available. See Nick Gammon's i2c tutorial at Gammon Forum : Electronics : Microprocessors : I2C - Two-Wire Peripheral Interface - for Arduino

That tutorial will also give you some ideas for error checking.

#include "Wire.h"

#define I2C_ADDR  0x1A
boolean requestNewData = true;

void setup() {
  Wire.begin();
  Serial.begin(9600);
}

void loop() {
  if (requestNewData == true)
  {

    Wire.requestFrom(I2C_ADDR, 1);
   // while (Wire.available())
   // {
      char data = Wire.read();
    //  int len = strlen(data);
    //  if (len >= 0) {
        Serial.println(data);
        requestNewData = false;
      //  memset(data, 0, sizeof(data));
     // }
   // }
  }
  delay(100);
}

cattledog:
Here's a way to control the request for data. Your code will need to set requestNewData= true when you want to request new data.

You do not need to test for Wire.available.The Wire.requestFrom function does not return until either the requested data is fully available, or an error occurred. Building in a wait for Wire.available simply makes it possible for the code to hang forever if the data is not available.

Your code seems so reasonable, but unfortunately I don't get any Serial output with it:

#include "Wire.h"

#define I2C_ADDR  0x1A
boolean requestNewData = true;

void setup() {
  Wire.begin();
  Serial.begin(9600);
}

void loop()
{
  if (requestNewData == true)
  {
    Wire.requestFrom(I2C_ADDR, 1);
    char data = Wire.read();
    Serial.println(data);
    requestNewData = false;
  }
  delay(100);
}

How many requests is that code going to send to the slave?

We still don't know what the slave is. We still don't have proof that the request is getting to the slave. We still don't have proof that the slave does not reply.

Usually, one allows more than a few nanoseconds for the request to get to the slave, for the slave to handle the request, for the slave to generate a response, and for the response to get back to the slave.

Try this:

  if (requestNewData == true)
  {
    Serial.println("Sending request to slave...");

    Wire.requestFrom(I2C_ADDR, 1);

    while(Wire.available() == 0)
    {
       // wait for a reply
    }

    char data = Wire.read();
    Serial.println(data);
    requestNewData = false;
  }

It will still generate a single request, and block until the slave responds.

But, if the slave DOES respond, with data that you expect/seems reasonable, then we can show you how to use the boolean to control sending a request to slave and, asynchronously, getting response, WITHOUT blocking.

One step at a time, though.

PaulS:
How many requests is that code going to send to the slave?

We still don't know what the slave is. We still don't have proof that the request is getting to the slave. We still don't have proof that the slave does not reply.

Usually, one allows more than a few nanoseconds for the request to get to the slave, for the slave to handle the request, for the slave to generate a response, and for the response to get back to the slave.

Try this:

  if (requestNewData == true)

{
   Serial.println("Sending request to slave...");

Wire.requestFrom(I2C_ADDR, 1);

while(Wire.available() == 0)
   {
      // wait for a reply
   }

char data = Wire.read();
   Serial.println(data);
   requestNewData = false;
 }




It will still generate a single request, and block until the slave responds.

But, if the slave DOES respond, with data that you expect/seems reasonable, then we can show you how to use the boolean to control sending a request to slave and, asynchronously, getting response, WITHOUT blocking.

One step at a time, though.

Unforunately your example didn't worked too, but here is my Slave code and it's working if the Master has a Serial loop, so I'm getting the results with it.

I tought that the Master has also a listener ISR availability, no?

Here is my actual Slave code, and it's piece of cake:

#include "Wire.h"
#include "Keypad.h"

#define INT_DEBUG

#define I2C_ADDR  0x1A

char pKey;

const byte nRows = 4;
const byte nCols = 4;

char kMap[nRows][nCols]= 
{
{'1', '2', '3', 'A'}, 
{'4', '5', '6', 'B'}, 
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};

byte rPins[nRows] = {9,8,7,6};
byte cPins[nCols]= {5,4,3,2};

Keypad myKeyPad = Keypad(makeKeymap(kMap), rPins, cPins, nRows, nCols);

void setup()
{
  #ifdef INT_DEBUG
    Serial.begin(9600);
    Serial.println(F("I2C Matrix Keypad here..."));
  #endif
  Wire.begin(I2C_ADDR);
  Wire.onRequest(requestEvent);
}

void loop()
{
  pKey = myKeyPad.getKey();
  
  if (pKey != NO_KEY)
  {
    #ifdef INT_DEBUG
      Serial.println(pKey);
    #endif
  }

  delay(100);
}

void requestEvent()
{
  if (pKey != NO_KEY)
  {
    Wire.write(pKey);
  }
    memset((void*)pKey, 0, sizeof(pKey));
}

btw...my hardware confiration is: Arduino UNO as a Master and ATmega8 Slave as a Kaypad repetitor.

So, what I wish to achieve from all of it, when I press any key on the Slave, the Master need to receive it.

void requestEvent()
{
  if (pKey != NO_KEY)
  {
    Wire.write(pKey);
  }
    memset((void*)pKey, 0, sizeof(pKey));
}

You are NOT allowed to not respond to a request for data.