Issue with Wire Library (onRequest() and requestFrom())

I have two arduinos, one that is hooked up to several sensors which periodically reads and writes the data to an SD card. The second arduino requests data to be sent by the first arduino from the SD card so that it can be printed by the second arduino. I have gotten data to be periodically sent by the Slave and read by the Master, but now that I am setting up a request system I’ve hit a roadblock. The data is no longer being read by the Master, if it is even read at all. Am I implementing the methods incorrectly? Thanks.

Simplified code is located in a reply

Slave (onRequest is located near end of code):

#include <SPI.h>
#include <SD.h>
#include <stdint.h>
#include <math.h>
#include <Wire.h>
#include "I2C_16.h"
#include "TMP006.h"
Sd2Card card;
SdVolume volume;
SdFile root;
File userFile;

uint8_t sensor1 = 0x40; // I2C address of TMP006, can be 0x40-0x47
uint16_t samples = 4; // # of samples per reading, can be 1/2/4/8/16
int wait = 15;
int sensorValue;
int sound[5];
int temp[5];
boolean go = true;
String userInput;
boolean pool;

int delay3 = 0;
int delay4 = 0;
int delay5 = 0;
int delay6 = 0;
int delay7 = 0;
int delay8 = 0;
float count;
float seconds;

void setup()
{
  Serial.begin(9600);
  Wire.begin(8);
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);
  Serial.println("Searching for SD Card...");
  if (!SD.begin(10)) {
    Serial.println("Cannot find SD card.");
    return;
  }
  Serial.println("SD Card found.");
  while (!Serial) {                                                      // Open serial communications and wait for port to open:
    ;
    delay(100);
  }
  Serial.println("Initializing SD card...");
  if (!card.init(SPI_HALF_SPEED, 10)) {                                  //***10 used to be: chipSelect***
    Serial.println("initialization failed.");
    go = false;
    return;
  }
  else {
    Serial.println("Card Initialized");
  }
  int sensor_temp = readDieTempC(sensor1);
  config_TMP006(sensor1, samples);
}

void loop()
{
  delay(1);
  if (delay3 == 5000) {
    sound[0] = analogRead(1);
    temp[0] = readObjTempC(sensor1);
  }
  if (delay4 == 10000) {
    sound[1] = analogRead(1);
    temp[1] = readObjTempC(sensor1);
  }
  if (delay5 == 15000) {
    sound[2] = analogRead(1);
    temp[2] = readObjTempC(sensor1);
  }
  if (delay6 == 20000) {
    sound[3] = analogRead(1);
    temp[3] = readObjTempC(sensor1);
  }
  if (delay7 == 25000) {
    sound[4] = analogRead(1);
    temp[4] = readObjTempC(sensor1);
  }
  if (delay8 == 30001) {
    int soundsum = sound[0] + sound[1] + sound[2] + sound[3] + sound[4];
    float sumAverage = (soundsum / 5);
    int tempsum = temp[0] + temp[1] + temp[2] + temp[3] + temp[4];
    float tempAverage = (tempsum / 5);

    count = millis();
    seconds = count / 1000;

    userFile = SD.open("mic.txt", FILE_WRITE);
    userFile.print(sumAverage);
    userFile.print("||");
    userFile.print(seconds);
    userFile.print("\n");
    userFile.println("");
    userFile.close();

    userFile = SD.open("temp.txt", FILE_WRITE);
    userFile.print(tempAverage);
    userFile.print("||");
    userFile.print(seconds);
    userFile.print("\n");
    userFile.println("");
    userFile.close();
    Serial.println("Printed");
    Serial.println("Sent.");    /*
      char x = 'x';
      Wire.beginTransmission(8);
      Wire.write(x);
      Serial.println("Printed.");
      Wire.endTransmission();
*/

    delay3 = 0;
    delay4 = 0;
    delay5 = 0;
    delay6 = 0;
    delay7 = 0;
    delay8 = 0;
  }

  Wire.onRequest(transmit);

  delay3 = delay3 + 1;
  delay4 = delay4 + 1;
  delay5 = delay5 + 1;
  delay6 = delay6 + 1;
  delay7 = delay7 + 1;
  delay8 = delay8 + 1;
}

void transmit() {
  userFile = SD.open("temp.txt");
  if (userFile) {
    if (userFile.available() >= 1) {
      while (userFile.available()) {
        Wire.beginTransmission(8);
        for (int i; i = 0; i < 12) {
          Wire.write(userFile.read());
          Wire.endTransmission();
          i = i + 1;
        }
      }
    }
    else{
      Serial.println("No data available.");
    }
  }
  else {
    Serial.println("Error opening file.");
  }
  Serial.println("Sent.");
}

Master:

#include <Wire.h>

String userInput;
boolean go = true;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.setTimeout(4500);
  Wire.begin(8);

}

void loop() {
  if (go == true) {
    Serial.println("we goin");
    Wire.requestFrom(8 , 13);
  }
  go = false;
  Wire.onReceive(event);
}

void event(int howMany) {
  while (Wire.available()) {
    char c = Wire.read();
    Serial.print(c);
  }
  Serial.print("\n");
  Serial.println("done");
}

I've had mixed results with the Wire library. I used a Mega 2560 as a slave and a Due as a master, but now and then, the Mega quits sending data or the Due quits receiving data. The Due says the Mega is sending, but nothing shows up. It might just be me.

edit:...and yes, I'm using a logic level converter between the two devices.

I hope that's not the issue, but the programming is just as likely to be the culprit. I'm very new to both c++ and arduino, so I'm hoping it is an error on my end!

Even this simplified code fails to allow the Master to receive and/or print the sent data:

Master:

#include <Wire.h>

String userInput;
boolean go = true;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.setTimeout(4500);
  Wire.setTimeout(4000);
  Wire.begin(8);

}

void loop() {
  if (go == true) {
    Serial.println("we goin");
    Wire.requestFrom(8 , 13);
  }

  go = false;
  Wire.onReceive(event);
}

void event(int howMany) {
  Serial.println("we here boyz");
  while (Wire.available()) {
    char c = Wire.read();
    Serial.print(c);
  }
  Serial.print("\n");
  Serial.println("done");
}

Slave:

#include <SPI.h>
#include <SD.h>
#include <stdint.h>
#include <math.h>
#include <Wire.h>
#include "I2C_16.h"
#include "TMP006.h"
Sd2Card card;
SdVolume volume;
SdFile root;
File userFile;

void setup() {
  Serial.begin(9600);
  Wire.begin(8);
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);
  Serial.println("Searching for SD Card...");
  if (!SD.begin(10)) {
    Serial.println("Cannot find SD card.");
    return;
  }
  Serial.println("SD Card found.");
  while (!Serial) {                                                      // Open serial communications and wait for port to open:
    ;
    delay(100);
  }
  Serial.println("Initializing SD card...");
  if (!card.init(SPI_HALF_SPEED, 10)) {                                  //***10 used to be: chipSelect***
    Serial.println("initialization failed.");
    return;
  }
  else {
    Serial.println("Card Initialized");
  }
}

void loop() {
  Wire.onRequest(transmit);
}

void transmit() {
  userFile = SD.open("temp.txt");
  if (userFile) {
    if (userFile.available() >= 1) {
      while (userFile.available()) {
        Wire.beginTransmission(8);
        for (int i; i = 0; i < 12) {
          Wire.write(userFile.read());
          Wire.endTransmission();
          i = i + 1;
        }
      }
    }
    else {
      Serial.println("No data available.");
    }
  }
  else {
    Serial.println("Error opening file.");
  }
  Serial.println("Sent.");
}

With regard to the slave code. This is the slave sender example in the IDE library examples. Note how the onRequest event is registered setup() and how it points to the function that takes care of the event. The function is like an ISR (Interrupt Service Routine) in that it is outside of loop.

#include <Wire.h>

void setup()
{
  Wire.begin(2);                // join i2c bus with address #2
  Wire.onRequest(requestEvent); // register event
}

void loop()
{
  delay(100);
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent()
{
  Wire.write("hello "); // respond with message of 6 bytes
                       // as expected by master
}

Would it hang while waiting for the request in setup()? Or am I able to have other code running within loop(), receive a request, and have it jump back to the setup() to activate transmit()?

The I2C hardware generates an event in the background when a request comes in. The line in setup() tells the chip to refer the event to the requestEvent function. The requestEvent function then sends requested number of bytes to the master. You don’t call the requestEvent function in your code. It is automatically called whenever a request event happens.

It is a bit different on the master side. The master sends a request for a number of bytes. The master knows that the next bytes coming in are the requested bytes, so as soon as there are available bytes the master reads and deals with them. You don’t need nor should you use onReceive in the master receiver in this case.

I modified your master code. It should work when the slave sends.

#include <Wire.h>

String userInput;
boolean go = true;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.setTimeout(4500);
  Wire.setTimeout(4000);
  Wire.begin(8);

}

void loop()
{
  if (go == true)
  {
    Serial.println("we goin");
    Wire.requestFrom(8 , 13);    // requested bytes
    go = false;
    Serial.println("we here boyz");
    while (Wire.available())             // if bytes are available they are the one I asked for
    {
      char c = Wire.read();
      Serial.print(c);
    }
    Serial.print("\n");

  }
  Serial.println("done");
}

Thanks to your code, a connection between the two has been made! However, the bytes that are being received are all spurious characters. Is there a possible data-type issue, or something else? The data being read off of the SD card is as follows:

22.00||20.54

23.00||41.10

22.00||61.61

23.00||82.13

Code for Master:

#include <Wire.h>

String userInput;
boolean go = true;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.setTimeout(4500);
  Wire.setTimeout(4000);
  Wire.begin(9);

}

void loop()
{
  Wire.requestFrom(8 , 13);    // requested bytes
  go = false;

  while (Wire.available())             // if bytes are available they are the one I asked for
  {
    char c = Wire.read();
    Serial.print(c);
  }
  Serial.print("\n");
  Serial.println("done");
}

Further Simplified code for Slave:

#include <SPI.h>
#include <SD.h>
#include <stdint.h>
#include <math.h>
#include <Wire.h>
#include "I2C_16.h"
#include "TMP006.h"
Sd2Card card;
SdVolume volume;
SdFile root;
File userFile;

void setup() {
  Serial.begin(9600);
  Wire.begin(8);
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);
  Serial.println("Searching for SD Card...");
  if (!SD.begin(10)) {
    Serial.println("Cannot find SD card.");
    return;
  }
}

void loop() {
  Serial.println("here");
  Wire.onRequest(transmit);                                         //***********8
  delay(2000);
}

void transmit() {
  userFile = SD.open("temp.txt");
  if (userFile) {
    if (userFile.available() >= 1) {
      while (userFile.available()) {
        for (int i; i = 0; i < 12) {
          Wire.write(userFile.read());
          i = i + 1;
        }
      }
    }
    else {
      Serial.println("No data available.");
    }
  }
  else {
    Serial.println("Error opening file.");
  }
  Serial.println("Sent.");
}

Your slave code is not correct. You can not read and send a byte at a time

The behaviour of Wire.write() is asymmetric; its behaviour depends on whether you're a master or slave.

Calling write() from the master fills a 32-byte buffer which is transmitted when endTransmission() is called.

Calling write() from the slave sends the data immediately. If you call write(uint8_t), you send one byte and extra bytes requested by the master are garbage.

To send multiple bytes from the slave, you have to fill your own buffer and send it all at once. Read the data from the SD card into a buffer, then send it with a single command.

Wire.write(buffer,sizeof buffer);

Can the size of this buffer be larger than 32 bytes? And am i correct in assuming that 32 characters in a char buffer is 32 bytes?

Can the size of this buffer be larger than 32 bytes?

The wire.h library has a default limit of 32 bytes. There are two places in the Wire library which need changing to increase the buffer size. One is BUFFER_LENGTH in Wire.h and the other is the same definition in twi.h which is found in the utilities folder of the wire library.

And am i correct in assuming that 32 characters in a char buffer is 32 bytes?

Yes.