3

I am currently researching on I2C, and I made a configuration that has MasterWriter that transmits data to a SlaveReceiver that will store the data received from Master, into an SD card, and then transmit the same line of data by GPRS onto my back end.

Now the new requirement needs some kind of a caching system, so that if the GPRS failed to transmit, it'll instead save into the SD card under a different filename (the previous step of saving into SD Card is still done but it's for another purpose), so when the GSM signal needed has recovered it'll read the cached/backed-up data, and then transmit the backed-up data. So that means I'll use the loop block (which currently only has one line of delay function).

My question is, will the triggered function "receiveEvent" registered in setup for Wire Slave :

Wire.onReceive(receiveEvent);

conflict with the ongoing loop block where the system reads the backed-up data and sends the backed-up data?

Here's my SlaveReceiver code :


#define ONLINE_MODE 1 //  turn on for GSM.
#define CACHE_MODE 1 //   turn on for SD-based caching
#define I2C_MODE 1  //    turn on for I2C mode SDA-SCL

#include <Arduino.h>
#include "Slave_GSM_SD.h"

#if ONLINE_MODE
// Sim900
#include "GSM.h"
SIM900 gprs;
#endif

#if I2C_MODE
#include <Wire.h>
#endif

#if CACHE_MODE
//SD Card
#include <SPI.h>
#include <SD.h>
#define DEBUG_SD 1
#define SD_CS_PIN 4
File cacheFile;
#endif

void setup()
{
    console.begin(9600);
    while (!console);

    #if CACHE_MODE
     //SD Card
    Serial.print(F("Initializing SD card..."));

    if (!SD.begin(SD_CS_PIN)) {
      Serial.println(F("initialization failed!"));
      while (1);
    }
    // open the file. note that only one file can be open at a time,
    // so you have to close this one before opening another.
    cacheFile = SD.open("CACHE.txt", FILE_WRITE);
    Serial.println(F("SD Card initialization done."));
    #endif

    #if ONLINE_MODE
    gprsSetup();
    #endif

    #if I2C_MODE
    Wire.begin(8);                // join i2c bus with address #8
    Wire.onReceive(receiveEvent); // register event
    #endif
    console.println(F("SLAVE GSM SD READY"));

}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
// [Q] : will this interrupt/conflict with loop() if loop is still ongoing?
void receiveEvent(int howMany) {
  #if DEBUG_I2C
  Serial.print(F("available bytes : "));
  Serial.print(howMany);
  Serial.print(F(" or "));
  Serial.println(Wire.available());
  #endif

  #if I2C_MODE
  char jsonData[128]="";
  while (Wire.available()) { // loop through all but the last
    char c = Wire.read(); // receive byte as a character

    int len = strlen(jsonData);
    jsonData[len] = c;
    jsonData[len+1] = '\0';

    #if DEBUG_I2C
    Serial.print(c);         // print the character
    #endif

    if (c == '}'){

      #if CACHE_MODE
      writeToSd(jsonData);
      #endif

      #if ONLINE_MODE
      gprsSequence(jsonData);
      #endif

      #if DEBUG_I2C
      Serial.println(F("SAVING TRIGGERED"));
      Serial.println(jsonData);
      #endif
    }

  }
  #endif
}

void writeToSd(char *jsonToWrite){

  #if CACHE_MODE
  // open the file. note that only one file can be open at a time,
 // so you have to close this one before opening another.
  cacheFile = SD.open("CACHE.txt", FILE_WRITE);
  if (cacheFile) {
    cacheFile.write(jsonToWrite);
    cacheFile.close();
  }

  #if DEBUG_SD
  Serial.println(F(" << "));
  // re-open the file for reading:
    cacheFile = SD.open("CACHE.txt");
    if (cacheFile) {
    Serial.println("CACHE.txt:");

    // read from the file until there's nothing else in it:
    while (cacheFile.available()) {
      Serial.write(cacheFile.read());
    }
      // close the file:
      cacheFile.close();
    } else {
      // if the file didn't open, print an error:
      Serial.println(F("error opening CACHE.txt"));
    }
  #endif
  #endif

}


void loop() {
    // i wanted to code the cached-data sending here
    delay(2000);
}


void gprsSetup() {
  //... cleared for brevity
}

void gprsSequence(char *jsonData) {
    //... cleared for brevity. the point is, this function is to send the jsonData by GPRS
}
0

1 Answer 1

2

Generally the onReceive callback function is called from an ISR in the Wire library. That means, that no other interrupts can be handled during that time. For this reason an ISR always should be as short as possible. Writing to the SD card is a rather long process and it also uses interrupts, so it might block forever, when used in an ISR (though I'm not sure, since I didn't look through the SD library). You should do the SD card stuff outside of the ISR. In the ISR you can receive the I2C message and then set a flag variable, that will tell the code in the loop() function, that new data is available and that it can start processing it. That way your ISR stays short and the other operations will not be blocked by it. Do the same with the direct sending of GPRS data.

Also note, that transmitting Serial data inside of an ISR also does not work for the same reason (Serial relies on interrupts). The calls to Serial.print() or Serial.write() will only fill the libraries internal buffer. The data will get transmitted after the ISR was left. If you try to print/write data to Serial, when the buffer is full, the library will block, until the buffer has again space for sending the data, which will never happen inside the ISR. So you should be cautious with writing Serial inside an ISR. A bit doesn't hurt, but it can get a big problem.

will the triggered function "receiveEvent" registered in setup for Wire Slave Wire.onReceive(receiveEvent); conflict with the ongoing loop block where the system reads the backed-up data and sends the backed-up data?

From the general logic there cannot happen more than 1 specific data operation on the SD card at the same time. If you are currently writing one file, you cannot simply break the transmission through an interrupt and writing another file without breaking things. The transaction has to be completed, or you will fastly corrupt your file system. But, as I wrote above, this isn't really possible either way, because the SD card calls will not work properly inside of an ISR. So just put all the GPRS and SD card code into the loop() function and trigger the corresponding code blocks with a flag from the ISR. That way nothing can conflict here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.