Conflict between SD library and IridiumSBD

Hi all. I'm currently trying to figure out my problem.

The code is meant to red a data string from another UNO via the RX pin then save the data string to SD.

The code and UNO also control the power button and shutter button on a cheap action cam.

Everything above works as it should with no problems.

The next part I need to implement is to send a string of data via a Rockblock 9603. As I am only just starting to integrate the Modem into the code I thought id start with the basicSend example on the IridiumSBD library.

I can get most of it to work until I ask the modem to //Send the message. At which point my serial console gives me "Error opening file for writing!"

If I comment that whole section for //Send the message then it works again.

Thinking its some sort of conflict but i cant get y head around it.

Any help appreciated.

#include <SD.h>
#include <SPI.h>
#include <IridiumSBD.h>
#include <SoftwareSerial.h>

// Pins for Camera control
#define CamPower_PIN 8
#define CamPhoto_PIN 9

// Variables for modem
#define DIAGNOSTICS true  // Change this to see diagnostics

SoftwareSerial ss(6, 7);
IridiumSBD modem(ss, 5);

// Variables for Camera state machine
unsigned long previousMillis = 0;
unsigned long interval = 0;  // Initialize with 0 to start immediately
int state = 0;

unsigned long previousHourMillis = 0;
unsigned long hourInterval = 50000;  //
bool isCameraRunning = false;        // Flag to control camera cycle execution

unsigned long previousModemMillis = 0;
unsigned long modemInterval = 50000;  //
bool isModemRunning = false;          // Flag to control modem cycle execution

// SD card settings
const int chipSelect = 4;  // SD card CS pin
File dataFile;

void setup() {
  // Initialize Serial communication for both RX and debugging at 115200 baud
  Serial.begin(115200);
  ss.begin(19200);

  // Initialize Camera control pins
  pinMode(CamPower_PIN, OUTPUT);
  pinMode(CamPhoto_PIN, OUTPUT);

  // Initialize SD card
  if (!SD.begin(chipSelect)) {
    Serial.println("SD card initialization failed!");
    return;
  }
  Serial.println("SD card initialized.");

  // Begin satellite modem operation
  int signalQuality = -1;
  int err;

  Serial.println("Starting modem...");
  err = modem.begin();
  Serial.println("Modem initialised");
  delay(1000);
  modem.sleep();
  if (err != ISBD_SUCCESS) {
    Serial.print("Begin failed: error ");
    Serial.println(err);
    if (err == ISBD_NO_MODEM_DETECTED)
      Serial.println("No modem detected: check wiring.");
  }
}

void runModemCycle() {
  unsigned long currentMillis = millis();

  int signalQuality = -1;
  int err;

  modem.begin();
  Serial.println("MODEM AWAKE");

  err = modem.getSignalQuality(signalQuality);
  if (err != ISBD_SUCCESS) {
    Serial.print("SignalQuality failed: error ");
    Serial.println(err);
    return;
  }

  Serial.print("On a scale of 0 to 5, signal quality is currently ");
  Serial.print(signalQuality);
  Serial.println(".");

  // Send the message
   Serial.print("Trying to send the message.  This might take several minutes.\r\n");
  err = modem.sendSBDText("Hello, world!");
  if (err != ISBD_SUCCESS)
  {
    Serial.print("sendSBDText failed: error ");
    Serial.println(err);
    if (err == ISBD_SENDRECEIVE_TIMEOUT)
      Serial.println("Try again with a better view of the sky.");
  }

  else
  {
    Serial.println("Hey, it worked!");
  }

}

void runCameraCycle() {
  unsigned long currentMillis = millis();

  switch (state) {
    case 0:  // Power on camera
      if (currentMillis - previousMillis >= interval) {
        digitalWrite(CamPower_PIN, HIGH);
        Serial.println("cam power high");
        previousMillis = currentMillis;
        interval = 3000;  // Wait for 3 seconds
        state = 1;
      }
      break;

    case 1:  // Power off camera after 3 seconds
      if (currentMillis - previousMillis >= interval) {
        digitalWrite(CamPower_PIN, LOW);
        previousMillis = currentMillis;
        interval = 5000;  // Wait for 5 seconds before next action
        state = 2;
      }
      break;

    case 2:  // Switch to photo mode
      if (currentMillis - previousMillis >= interval) {
        digitalWrite(CamPower_PIN, HIGH);
        delay(300);  // Short delay
        digitalWrite(CamPower_PIN, LOW);
        Serial.println("Switching camera mode");
        previousMillis = currentMillis;
        interval = 1000;  // Wait 1 second before taking a photo
        state = 3;
      }
      break;

    case 3:  // Take one photo
      if (currentMillis - previousMillis >= interval) {
        digitalWrite(CamPhoto_PIN, HIGH);
        delay(100);  // Short delay for photo trigger
        digitalWrite(CamPhoto_PIN, LOW);
        Serial.println("took a photo");
        previousMillis = currentMillis;
        interval = 1000;  // Wait 1 second after taking the photo
        state = 4;
      }
      break;

    case 4:  // Switch to photo mode again
      if (currentMillis - previousMillis >= interval) {
        digitalWrite(CamPower_PIN, HIGH);
        delay(300);
        digitalWrite(CamPower_PIN, LOW);
        Serial.println("Switching camera mode");
        previousMillis = currentMillis;
        interval = 3000;  // Wait 3 seconds
        state = 5;
      }
      break;

    case 5:  // Repeat the switching action
      if (currentMillis - previousMillis >= interval) {
        digitalWrite(CamPower_PIN, HIGH);
        delay(300);
        digitalWrite(CamPower_PIN, LOW);
        Serial.println("Switching camera mode");
        previousMillis = currentMillis;
        interval = 3000;  // Wait 3 seconds
        state = 6;
      }
      break;

    case 6:  // Switch to video mode
      if (currentMillis - previousMillis >= interval) {
        digitalWrite(CamPower_PIN, HIGH);
        delay(300);
        digitalWrite(CamPower_PIN, LOW);
        Serial.println("video mode");
        previousMillis = currentMillis;
        interval = 1000;  // Wait 1 second before taking a video
        state = 7;
      }
      break;

    case 7:  // Start video recording (simulated by a photo action here)
      if (currentMillis - previousMillis >= interval) {
        digitalWrite(CamPhoto_PIN, HIGH);
        delay(100);
        digitalWrite(CamPhoto_PIN, LOW);
        Serial.println("taking a 20s video");
        previousMillis = currentMillis;
        interval = 20000;  // 20 seconds for video recording
        state = 8;
      }
      break;

    case 8:  // Power off after video recording
      if (currentMillis - previousMillis >= interval) {
        digitalWrite(CamPower_PIN, HIGH);
        Serial.println("cam power high");
        previousMillis = currentMillis;
        interval = 3000;  // Wait 3 seconds before powering off
        state = 9;
      }
      break;

    case 9:  // Final power off
      if (currentMillis - previousMillis >= interval) {
        digitalWrite(CamPower_PIN, LOW);
        Serial.println("cam off");
        previousMillis = currentMillis;
        interval = 3000;          // Wait before resetting the cycle (optional)
        state = 0;                // Reset to initial state
        isCameraRunning = false;  // Stop camera cycle
      }
      break;
  }
}

void logDataToSDCard() {
  // Check if there's any incoming data on the Serial (RX pin 0)
  if (Serial.available()) {
    String incomingData = Serial.readStringUntil('\n');  // Read until newline

    // Save the data to SD card
    dataFile = SD.open("datalog.txt", FILE_WRITE);

    if (dataFile) {
      // Serial print the data being saved
      Serial.print("String saved to SD card: ");
      Serial.println(incomingData);  // Print the incoming data

      // Write the incoming data to the file
      dataFile.println(incomingData);

      // Close the file
      dataFile.close();


    } else {
      Serial.println("Error opening file for writing!");
    }
  }
}

void loop() {
  unsigned long currentMillis = millis();

  // Check if an hour has passed and the camera cycle is not already running
  if (!isModemRunning && (currentMillis - previousModemMillis >= modemInterval)) {
    isModemRunning = true;  // Start camera cycle
    previousModemMillis = currentMillis;
    Serial.println("Starting modem cycle...");
  }

  // If camera cycle is running, continue to execute the state machine
  if (isModemRunning) {
    runModemCycle();
  }

  // Check if an hour has passed and the camera cycle is not already running
  if (!isCameraRunning && (currentMillis - previousHourMillis >= hourInterval)) {
    isCameraRunning = true;  // Start camera cycle
    previousHourMillis = currentMillis;
    Serial.println("Starting camera cycle...");
  }

  // If camera cycle is running, continue to execute the state machine
  if (isCameraRunning) {
    runCameraCycle();
  }

  // Continuously log incoming data to SD card
  logDataToSDCard();
}
```**strong text**

I guess you run of of memory

That’s a good point. I’ll look in to that and report back. Though I’m convinced it’s something to do with conflicting serial.

I could be wrong.

On the Uno you need to go all out with saving dynamic memory. Opening an SD file allocates 1/4 of the memory for the file buffer.

One important step is to edit all lines like this

      Serial.print("String saved to SD card: ");

to use the F macro (storing character strings in program memory):

      Serial.print(F("String saved to SD card: "));

Avoid using String objects on the Uno and similar processors, as they corrupt memory and cause crashes.

    String incomingData = Serial.readStringUntil('\n');  // Read until newline

Strings are never necessary on Arduino.

Opening a file, writing a line of data and closing it again is a common beginner mistake which vastly increases the SD card error rate, vastly increases the time required to write data, and vastly increases the power draw. Open the file once in setup() and close it again when all done collecting data.

    dataFile = SD.open("datalog.txt", FILE_WRITE);

    if (dataFile) {
      // Serial print the data being saved
      Serial.print("String saved to SD card: ");
      Serial.println(incomingData);  // Print the incoming data

      // Write the incoming data to the file
      dataFile.println(incomingData);

      // Close the file
      dataFile.close();

Thank you for the information. I’ll have a play about with what you have mentioned and report back.

Thanks so much for your detailed reply. It seems you were correct. After changing as many Serial.prints as I can to Serial.print(f the program now runs stable and as expected.

There are a few serial.prints in the code which IDE wont let me change to Serial.print(f and I get an error when compiling but on the whole the dynamic memory usage was reduced enough to run correctly.

Could you suggest a way to get rif of the Strings like you mentioned in your comment ? not sure where to start with that one..

The Serial Input Basics tutorial shows how to receive data without using Strings.

Perfect thanks