Go Down

Topic: How do I make an I2C-slave Arduino just wait? [Kinda solved, hack job] (Read 1 time) previous topic - next topic

Verdris

Hi all,

For my project, I use two Arduinos rigged together via I2C.  Both boards are reading sensors, with the master board being responsible for datalogging.  I'm using Wire.RequestFrom() and Wire.onRequest() to handle data transfer.  Easy enough.

Currently, the slave board does nothing until it gets the request from the master, at which point it takes 0.02 seconds worth of data and transmits is back to the master.  This isn't the way I want to handle it, since the master board only requests information every x seconds.

What I'd like to do is have the slave board gather 1 second of integrated analog sensor data and then just "hang out" until it gets the request from the master board to transfer the time average.  I guess what I'm really asking is how does Wire.onRequest() behave after it executes the function it calls?  Does it go back to the beginning of the loop() function or does it return to the point in loop() where it was called?

Aeturnalus

Wire.onRequest() is, to the best of my knowledge, simply a hook into the I2C interrupt, so upon returning it should go back to whatever you had going on in loop() before the request came through.

Since you don't know when the request will come, you could implement a five-second rolling buffer (this can be implemented fairly easily with an array and modulus), and just send that back whenever it is requested.

Verdris

Then is there a command to start loop() from the top without running the whole way through?  Then I can just put a delay() at the end so it basically does nothing until it gets the interrupt request, and then restart loop() after the interrupt function is executed?

WizenedEE

Code: [Select]

bool valuerequested = false; //global variable

void onRequest() {//not sure how this syntax is...
   valuerequested = true;
}

void setup() {
  //Stuff
}

void loop() {
  //Read sensors
  while (!valuerequested); /*Busywait - don't do anything until valuerequested is true*/
  valuerequested = false;
}

Nick Gammon

Do what WizenedEE suggested (or make a rolling buffer). Then the moment the request is serviced the loop is resumed.

But I think the rolling buffer would be better, gather readings into a circular buffer and discard old readings, then the ISR can always return the latest reading.

Otherwise there is a danger that the interrupt occurs half-way through the 5 seconds, and everything is out of whack.
http://www.gammon.com.au/electronics

PaulS

Quote
Then is there a command to start loop() from the top without running the whole way through?

No. Don't even try to invent one.

Quote
Wire.onRequest() is, to the best of my knowledge, simply a hook into the I2C interrupt, so upon returning it should go back to whatever you had going on in loop() before the request came through.

Wire.onRequest() is called once in the program to register an event handler, for a specific event. That event handler is then called whenever the event occurs. Wire.onRequest() is not called when the event occurs.

When the event occurs, the registered handler is evoked. When that handler is done, execution resumes with the next machine instruction after the point where the interrupt occurred.

Aeturnalus


Quote
Then is there a command to start loop() from the top without running the whole way through?

No. Don't even try to invent one.

Quote
Wire.onRequest() is, to the best of my knowledge, simply a hook into the I2C interrupt, so upon returning it should go back to whatever you had going on in loop() before the request came through.

Wire.onRequest() is called once in the program to register an event handler, for a specific event. That event handler is then called whenever the event occurs. Wire.onRequest() is not called when the event occurs.

When the event occurs, the registered handler is evoked. When that handler is done, execution resumes with the next machine instruction after the point where the interrupt occurred.


I meant the handler invoked by onRequest--but that's besides the point, I think.

Wouldn't returning from loop() cause it to exit to the overarching while(true) loop and restart at the beginning? Not sure why there'd ever be a good reason to do this, though.

PaulS

Quote
Wouldn't returning from loop() cause it to exit to the overarching while(true) loop and restart at the beginning?

It would.

But, where do you put the return statement?

Aeturnalus

Like I said, I'm not sure why there would ever be a good reason to do this. It does, rather specifically, answer the OP's question though.

PaulS

Returning from loop does cause loop() to start again from the beginning, and that would address OPs "need".

It is not possible to do though. You have no idea whether a particular instruction is being executed before or after an interrupt occurred. So, you would need to do something in the interrupt that could be checked before every instruction in loop. If that flag was true, return.

The problem with this, of course, is that a single C/C++ instruction often maps to several machine instructions, so you do not have the granularity of control that you need. Not to mention the overhead needed to check after every C/C++ statement.

Verdris


Like I said, I'm not sure why there would ever be a good reason to do this. It does, rather specifically, answer the OP's question though.


The reason is because it takes the slave Arduino 1 full second to log its sensor data, while the time the master takes to log data is quite variable.  I need to find a way for the slave to hang out while the master finishes its reading cycle, then send over the collected data and start collection over again.

Verdris


Code: [Select]

bool valuerequested = false; //global variable

void onRequest() {//not sure how this syntax is...
  valuerequested = true;
}

void setup() {
 //Stuff
}

void loop() {
 //Read sensors
 while (!valuerequested); /*Busywait - don't do anything until valuerequested is true*/
 valuerequested = false;
}



This worked perfectly, however, instead of while(!valuerequested);, I had to use while(valuerequested = false);.  Apparently there's some distinction.  For those interested, here's the current prototype codes:

For the Master:
Code: [Select]
#include <Wire.h>
int received;

void setup()
{
 //Serial.begin(115200);
 Wire.begin();
}

void loop()
{
 delay(2000);
 Wire.beginTransmission(2);
 Wire.requestFrom(2, 1);
 received = Wire.receive();
 Wire.endTransmission();
}
 


For the Slave:
Code: [Select]
#include <Wire.h>

//hardware defines, libraries, global variables, etc. go here

boolean toggle = false;

void setup()
{
 Serial.begin(115200);
 Wire.begin(2);
 Wire.onRequest(Event);
 
 //pin initializations, etc. go here
}

void loop()
{
 catalogtime = millis();

 //Sensor reading algorithm goes here
 
 ReadingEnd = millis();
 ReadingInterval = ReadingEnd - catalogtime; //Move timestamp function to master board
 
 while(toggle = false);
 {
   // haha empty loop
 }
 toggle = false;
}

void Event()
{
 Wire.send("3");
 //Serial.print(data) goes here

 //SDcard datalogging goes here
 
 toggle = true;
 
 return;
}


Obviously I didn't include ALL the code the master is running, since the analysis of several sensors plus GPS data isn't relevant here, but that's the basic structure.  It's working, but it feels like I hacked it together without any sense of elegance.  Does anyone have any ideas to refine things?

michinyon

You might want to be careful with that 

while( toggle = false )

Verdris


You might want to be careful with that 

while( toggle = false )


No, I'm pretty happy with it.  I want the board to trap itself in a loop until it gets interrupted by Event(), and once Event() sets toggle=true then the board can escape the loop and resume measurements.

ryschwith

I think michinyon is suggesting that you'll want to take a closer look at that statement before you run it. There might be an error in it as written.

Go Up