Multiple Wire.OnRequest handlers

Hi,

Im just playing with some ideas at the moment for a project where I know the code/sram usage will be too much for a single 328 and pin usage will be high. I know pin usage can be resolved with shift registers etc but I am really liking the idea of using a couple of 328s over I2C and keeping things very modular.

I'd really like to be able to make different requests to a 328, as an example, one handler for writing data to an SD card and some other handlers to read, interpret and return that data in different ways.

I havent come across any examples on how this might be achieved so wondered if it is even possible?

oooh, would I do something like

beginTransmission(), 
send('someMethodName'), 
endTransmission()
requestFrom()

Having a the onRequest handler read the string that was sent with send() and call some function based on that?

Thanks in advance.

I hope I have interpreted your question correctly....

One of my projects requires different information to be sent/received from arduino to arduino, and what I did was basically how you have indicated.

Wire.beginTransmission(Address);
Wire.send(Command);
Wire.endTransmission();
Wire.requestFrom(Address, 1);      // request 1 bytes from slave device

So basically you send a 'Command' from the Master to the Slave, and then you request data from it. In the Slave you save the command that you received, and then when the request for data comes in, you send the information based on what the command was.

If that makes sense.

So in the slave, in simple terms, I do this:

void setup()
{
  Wire.begin(Address);    //Slaves Address
  Wire.onRequest(requestEvent);
  Wire.onReceive(receiveEvent);
}

void loop()
{
   //calculate stuff
}

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

void requestEvent()
{
  if(CMD == 0x01)
  {
    //Send Stuff
  }
  if(CMD == 0x02)
  {
    //Send other Stuff
  }
}

Hope you get the idea.
Thats how I have done it anyway and it works well.

James

WanaGo:
So basically you send a 'Command' from the Master to the Slave, and then you request data from it. In the Slave you save the command that you received, and then when the request for data comes in, you send the information based on what the command was.

If that makes sense.

Yeah that's exactly what I need to do :slight_smile:

My only concern with what you have proposed is that the request handler function could grow quite large, do you know if it's possible to set the request event handler in the receive event based on the command passed in or do they both have to be declared in setup?

Cheers,

My only concern with what you have proposed is that the request handler function could grow quite large

Use a 2 stage architecture:

The requesthandler should only dispatch the request to the right function, something like below.
This keeps the requestEvent rather small and focussed. Every command has its own function.

void requestEvent()
{
  switch(CMD)
  {
  case 0x01: ABC(); break;
  case 0x02: XYZ(); break;
  case 0x03: PQR(); break;
  case 0x04: KLM(); break;
  default: break; // do nothing
  }
}

void ABC()
{
  byte value = readsensor();
  send(value);
}

robtillaart:
The requesthandler should only dispatch the request to the right function, something like below.
This keeps the requestEvent rather small and focussed. Every command has its own function.

That's just perfect. Thanks.

even better

default: break; // do nothing

you could light up some error LED here iso doing nothing, to show that a not supported command was send. (think debug)

robtillaart:

My only concern with what you have proposed is that the request handler function could grow quite large

Use a 2 stage architecture:

The requesthandler should only dispatch the request to the right function, something like below.
This keeps the requestEvent rather small and focussed. Every command has its own function.

void requestEvent()

{
  switch(CMD)
  {
  case 0x01: ABC(); break;
  case 0x02: XYZ(); break;
  case 0x03: PQR(); break;
  case 0x04: KLM(); break;
  default: break; // do nothing
  }
}

void ABC()
{
  byte value = readsensor();
  send(value);
}

Yup what he said :slight_smile:

This is exactly what I've been looking for, but there are some things that I need some clarification on:

void ABC()
{
  byte value = readsensor();
  send(value);
}

It seems the function ABC() has a locally declared variable "value" that is updated by calling an external function: readsensor().
Am I correct there? If this is the case, sending one of those "CMD"s from the master to the slave might be a good way to initiate a state change on a pin from low to high, provided it is part of a function like readSensor(), right? Essentially, the master could send a slave a command from a list of functions the slave has, then call for that slave to execute the function corresponding to the command it just declared, whatever it entails.

If this is the case, I'm sitting pretty good. Still, how would something like an int or a float (2 and 4 bytes, respectively for an Arduino other than the Due) be transmitted from the variable "value" on the slave to the master? Do you need to transmit strictly bytes, or could the slave define "value" to be something like an int, float, unsigned long, etc.. and get passed an int/float/unsigned long from whatever function a slave's handler function calls?

Perhaps more importantly, if I'm anywhere close to being on the right track here, how do you go about passing any information from the function called in the handler function to the handler? Is a local variable somehow nested? i.e. If the term used for a variable agree between the function called within the handler, and what the handler ends up sending, is there anything the function called needs to do to pass that information to the handler function, other than hold it's own copy of that variable?

aurelius:
This is exactly what I've been looking for, but there are some things that I need some clarification on:

void ABC()

{
 byte value = readsensor();
 send(value);
}




It seems the function ABC() has a locally declared variable "value" that is updated by calling an external function: readsensor(). 
Am I correct there?

Yes;

If this is the case, sending one of those "CMD"s from the master to the slave might be a good way to initiate a state change on a pin from low to high, provided it is part of a function like readSensor(), right?

Yes;
Or you just say one of these

digitalWrite(PIN, ! digitalRead(PIN)): // toggle
digitalWrite(PIN, HIGH):
digitalWrite(PIN, LOW);

or a 1 ms pulse

digitalWrite(PIN, HIGH):
delay(1); 
digitalWrite(PIN, LOW);

or

if (analogRead(A0) > 314) digitalWrite(PIN, HIGH):
else digitalWrite(PIN, LOW);

Anything can be in a commandHandler

Essentially, the master could send a slave a command from a list of functions the slave has, then call for that slave to execute the function corresponding to the command it just declared, whatever it entails.

Yes, one step further you even can give parameters with the command, e.g. how lon a pulse should take or the precision of a sensor...

If this is the case, I'm sitting pretty good. Still, how would something like an int or a float (2 and 4 bytes, respectively for an Arduino other than the Due) be transmitted from the variable "value" on the slave to the master? Do you need to transmit strictly bytes, or could the slave define "value" to be something like an int, float, unsigned long, etc.. and get passed an int/float/unsigned long from whatever function a slave's handler function calls?

A float is just 4 bytes in memory, the standard trick for this is using a union which maps an array of 4 bytes and a float on the same memory. You fill in the float and send the 4 bytes. On the receiving edge you do similar. receive 4 bytes and use the float.

Perhaps more importantly, if I'm anywhere close to being on the right track here, how do you go about passing any information from the function called in the handler function to the handler? Is a local variable somehow nested? i.e. If the term used for a variable agree between the function called within the handler, and what the handler ends up sending, is there anything the function called needs to do to pass that information to the handler function, other than hold it's own copy of that variable?

Just use a global variable or pass a variable by reference.

Rob... wow. Excellent and comprehensive answers. Thank you. I still have some questions for clarification, which you seem to be extraordinarily apt in soliciting answers from on this topic.

In respect to pin state changes, telling a pin to go high or low in a function is super easy, but I'm left wondering if the command handler itself might not be the best place to implement a change in state like this.

There are a couple examples I can think of where including a pin high/low designation in the handler might be a good thing: i.e. to indicate the "CMD" sent is being implemented (like turning on an LED to indicate a function is running). However, most of the time, it seems like there would be more control over how and under what circumstances something like a pin state on a slave goes from high to low if it were in the function designated by the handler. i.e. setting a pin high if a temperature probe reaches some threshold.

You indicated

Yes, one step further you even can give parameters with the command, e.g. how lon a pulse should take or the precision of a sensor...

Can you give an example on how this is done? I'm guessing the parameters one would pass to a function would need to be predefined on the slave's command handler functions in a way like this:

void requestEvent()
{
  switch(CMD)
  {
  case 0x01: ABC(some parameter); break;
  case 0x02: ABC(some other parameter); break;

It doesn't seem like there is an easy way to have the master dynamically update a parameter the slave's handler function uses, as receiveEvent() assigns to the variable CMD, and I can't think of a great way to update another variable without some complicated test. So... assuming that using the variable CMD to indicate which pre-defined parameter you want to pass to a given function like ABC(), what are the options for variables to pass, and how would ABC() interpret what it is passed?