RockBLOCK, GPS and OLED sending a position

Hi. I am trying this project for my thesis and need some assistance. First, sorry in advance if I make any mistakes in the language or the code (this is not related to my degree).
In this project, I am trying to send a message using RockBLOCK 9603. The message is a position, that is a latitude and a longitude obtained by a GPS. When the message is sent, the OLED display will show something like "Sent. Lat: Lon: ". Here is the code I am using, in Arduino Uno.

#include <SoftwareSerial.h>
#include <TinyGPS++.h>  // TinyGPS++ library
TinyGPSPlus gps;        // Create a GPS object

#define RXpin A3  // GPS RX pin
#define TXpin A2  // GPS TX pin

SoftwareSerial GPSserial(RXpin, TXpin);  // Software serial for GPS

#include <U8g2lib.h>  // OLED library
#include <Wire.h>

U8G2_SSD1306_128X64_NONAME_1_HW_I2C disp(U8G2_R0, U8X8_PIN_NONE);  // OLED display

uint32_t startGetFixmS;
uint32_t endFixmS;

char latBuffer[10]; // Buffer for latitude
char lonBuffer[10]; // Buffer for longitude
char message[30];   // Final message to send

SoftwareSerial rockBlockSerial(10, 11); // RX, TX (Arduino <-> RockBLOCK)

bool messageSent = false;  // **Flag to track if message was sent**

void setup() {
    delay(1000);
    Serial.begin(115200);
    GPSserial.begin(9600);

    disp.begin();
    disp.setFont(u8g2_font_helvB14_tr);
    disp.clear();
    disp.firstPage();
    disp.setCursor(10, 15);
    disp.print(F("Waiting for GPS"));
    disp.nextPage();
    startGetFixmS = millis();

    rockBlockSerial.begin(19200); // RockBLOCK communication

    Serial.println("Initializing RockBLOCK 9603...");

    delay(2000); // Allow RockBLOCK to initialize

    checkSignalStrength();
}

void loop() {
    if (!messageSent && gpsWaitFix(5)) {  // **Send only if message has not been sent**
        Serial.println();
        Serial.println();
        Serial.print(F("Fix time: "));
        Serial.print(endFixmS - startGetFixmS);
        Serial.println(F(" ms"));

        sendMessage(message);  // **Send the message to RockBLOCK**
        messageSent = true;  // **Set flag to true so no more messages are sent**
    } else if (messageSent) {
        Serial.println("Message already sent. Halting transmission.");
        while (true);  // **Stop loop execution**
    }

    // Listen for responses from RockBLOCK
    while (rockBlockSerial.available()) {
        Serial.write(rockBlockSerial.read());  // Print response to Serial Monitor
    }
}

bool gpsWaitFix(uint16_t waitSecs) {
    uint32_t endwaitmS;
    uint8_t GPSchar;

    Serial.print(F("Waiting for GPS Fix ("));
    Serial.print(waitSecs);
    Serial.println(F(" seconds)"));

    endwaitmS = millis() + (waitSecs * 1000);

    while (millis() < endwaitmS) {
        if (GPSserial.available() > 0) {
            GPSchar = GPSserial.read();
            gps.encode(GPSchar);
            Serial.write(GPSchar);
        }

        if (gps.location.isUpdated()) {
            dtostrf(gps.location.lat(), 5, 2, latBuffer);
            dtostrf(gps.location.lng(), 5, 2, lonBuffer);

            snprintf(message, sizeof(message), "Lat:%s Lon:%s", latBuffer, lonBuffer);

            endFixmS = millis();
            return true;
        }
    }
    return false;
}

void checkSignalStrength() {
    Serial.println("Checking signal strength...");
    clearSerialBuffer();
    rockBlockSerial.println("AT+CSQ");
    delay(2000);
    readResponse();
}

void displayscreenSent() {
    disp.firstPage();
    do {
        disp.setFont(u8g2_font_helvB12_tr);
        disp.setCursor(10, 15);
        disp.print(F("Sent."));

        disp.setCursor(10, 35);
        disp.print(F("Lat: "));
        disp.print(latBuffer);

        disp.setCursor(10, 55);
        disp.print(F("Lon: "));
        disp.print(lonBuffer);
    } while (disp.nextPage());
}

void sendMessage(const char* msg) {
    Serial.println("Sending message: " + String(msg));

    clearSerialBuffer();
    rockBlockSerial.print("AT+SBDWT="); 
    rockBlockSerial.println(msg);
    delay(2000);

    if (!waitForResponse("OK")) {
        Serial.println("Error writing message to buffer.");
        return;
    }

    clearSerialBuffer();
    rockBlockSerial.println("AT+SBDIX");
    delay(8000);

    readResponse();

    Serial.println("Message sent successfully!");
    
    // **Update OLED with "Sent. Lat: <value> Lon: <value>"**
    displayscreenSent(); 

    messageSent = true;  // Mark message as sent
}

bool waitForResponse(String expected) {
    unsigned long startTime = millis();
    String response = "";

    while (millis() - startTime < 5000) {
        while (rockBlockSerial.available()) {
            char c = rockBlockSerial.read();
            response += c;
        }
        if (response.length() > 0) {
            Serial.println("RockBLOCK response: " + response);
            if (response.indexOf(expected) != -1) return true;
        }
    }
    Serial.println("Error: Expected '" + expected + "'got: " + response);
    return false;
}

void clearSerialBuffer() {
   while (rockBlockSerial.available()) {
       rockBlockSerial.read();
   }
}

void readResponse() {
   while (rockBlockSerial.available()) {
       char c = rockBlockSerial.read();
       Serial.write(c);
       delay(5);
    }
    Serial.println();
}


However, when I run this code I do not get a GPS fix. I know it works because I have been making different tests and it works properly, as well as the RockBLOCK and the OLED. I am using the 5v pin in the Arduino Uno. If anyone has any idea of the issue, please let me know. Thank you so much.

Using 2 instances of SoftwareSerial is almost certainly doomed to fail. I have not seen anyone do that successfully beyond trivial applications

I suggest that you consider using an Arduino board with multiple hardware UARTs to completely separate the 3 serial interfaces used by your sketch

I only have this Arduino Uno. Is there any alternative?

There is a remote chance that you might get it working using the SoftwareSerial listen() function, but I don't hold out much hope

Then, you suggest that I use another Arduino right? Which one could I use? What would be the solution?

See the software serial reference link below. You can switch between software serial interfaces, but its much much much easier to use an Arduino with two hardware serial ports;

https://docs.arduino.cc/learn/built-in-libraries/software-serial/

The obvious choice that would mean very few changes to your sketch and/or IDE would be a Mega 2560 which has 4 hardware UARTs

Thank you so much both of you! I will check the reference link and I will try to look for Arduino Mega 2560.

Just in case, do you know if a GPS module can be connected to Arduino Uno and can work not using Software Serial? I have a Adafruit Ultimate GPS Breakout

My tutor suggested an alternative: initializing GPS, getting a GPS, ending the comunicaction and then start the software serial comunication with the RockBLOCK. I will try that but I wanted to ask if anybody knows if that would work.

You can do that by connecting the GPS to the Serial interface on pins 0 and 1 of the Uno but will lose the ability to print to that interface, ie the Serial monitor output. You may also need to disconnect the GPS when uploading code to the Uno as that too uses the Serial interface

That might work but would be very clumsy as you would need to constantly start and stop the two SoftwareSerial interfaces and wait until they were established again before using them

Thank you! I will try.

Please post your new sketch in a new reply to this topic whether or not it works

Right. I am trying this code now.

#include <SoftwareSerial.h>  // Include SoftwareSerial library for communication with GPS and RockBLOCK
#include <TinyGPS++.h>       // Include TinyGPS++ library to work with GPS data
TinyGPSPlus gps;             // Create a TinyGPSPlus object for GPS processing

#define RXpin A3             // GPS RX pin (receives data from GPS)
#define TXpin A2             // GPS TX pin (sends data to GPS)

SoftwareSerial GPSserial(RXpin, TXpin);  // Initialize SoftwareSerial for GPS communication

#include <U8g2lib.h>        // Include U8g2 library for OLED display
#include <Wire.h>            // Include Wire library for I2C communication with OLED

U8G2_SSD1306_128X64_NONAME_1_HW_I2C disp(U8G2_R0, U8X8_PIN_NONE);  // Create an OLED display object (128x64 resolution)

uint32_t startGetFixmS;       // Variable to store the timestamp when GPS fix starts
uint32_t endFixmS;           // Variable to store the timestamp when GPS fix ends

char latBuffer[10];           // Buffer to store the latitude as a string
char lonBuffer[10];           // Buffer to store the longitude as a string
char message[30];             // Final message to be sent (latitude and longitude)

SoftwareSerial rockBlockSerial(10, 11); // RX, TX pins for communication with RockBLOCK (pins 10 and 11)

bool messageSent = false;     // Flag to track if the message has been sent to RockBLOCK

void setup() {
    delay(1000);  // Wait for a second before starting setup
    Serial.begin(115200);   // Start Serial Monitor communication at 115200 baud
    GPSserial.begin(9600);  // Initialize GPS communication at 9600 baud

    disp.begin();  // Initialize OLED display
    disp.setFont(u8g2_font_helvB14_tr);  // Set font for the display
    disp.clear();  // Clear the display screen
    disp.firstPage();  // Start drawing on the first page of OLED
    disp.setCursor(10, 15);  // Set cursor position on screen
    disp.print(F("Waiting for GPS"));  // Display message to indicate waiting for GPS fix
    disp.nextPage();  // Display content on OLED screen
    startGetFixmS = millis();  // Record the start time of the GPS fix

    //rockBlockSerial.begin(19200); // Initialize RockBLOCK communication at 19200 baud

    //Serial.println("Initializing RockBLOCK 9603...");  // Print message to Serial Monitor

    delay(2000); // Wait for 2 seconds to allow RockBLOCK to initialize

    //checkSignalStrength();  // Check the signal strength of RockBLOCK
}

void loop() {
    if (!messageSent && gpsWaitFix(5)) {  // If message is not sent and GPS fix is acquired within 5 seconds
        Serial.println();
        Serial.println();
        Serial.print(F("Fix time: "));
        Serial.print(endFixmS - startGetFixmS);  // Print the time taken to get GPS fix
        Serial.println(F(" ms"));
        GPSserial.end();
        delay(1000);

        rockBlockSerial.begin(19200);
        Serial.println("Initializing RockBLOCK 9603..."); 

        checkSignalStrength();

        sendMessage(message);  // Send the message containing latitude and longitude to RockBLOCK
        messageSent = true;  // Set flag to true indicating message has been sent
    } else if (messageSent) {  // If message has already been sent
        Serial.println("Message already sent. Halting transmission.");
        while (true);  // Halt the loop execution indefinitely
    }

    // Listen for responses from RockBLOCK and print them to the Serial Monitor
   // while (rockBlockSerial.available()) {
    //    Serial.write(rockBlockSerial.read());  // Print response from RockBLOCK to Serial Monitor
    //}
}

// Function to wait for GPS fix
bool gpsWaitFix(uint16_t waitSecs) {
    uint32_t endwaitmS;
    uint8_t GPSchar;

    Serial.print(F("Waiting for GPS Fix ("));
    Serial.print(waitSecs);
    Serial.println(F(" seconds)"));  

    endwaitmS = millis() + (waitSecs * 1000);  // Set the timeout duration for waiting for GPS fix

    while (millis() < endwaitmS) {  // Loop until the specified time has passed
        if (GPSserial.available() > 0) {  // If there is data available from the GPS
            GPSchar = GPSserial.read();  // Read the incoming GPS data
            gps.encode(GPSchar);  // Encode the GPS data for TinyGPS++
            Serial.write(GPSchar);  // Print the raw GPS data to Serial Monitor for debugging
        }

        if (gps.location.isUpdated()) {  // If new GPS location data is available
            dtostrf(gps.location.lat(), 6, 4, latBuffer);  // Convert latitude to string and store it in latBuffer
            dtostrf(gps.location.lng(), 6, 4, lonBuffer);  // Convert longitude to string and store it in lonBuffer

            snprintf(message, sizeof(message), "Lat:%s Lon:%s", latBuffer, lonBuffer);  // Format the message with latitude and longitude

            endFixmS = millis();  // Record the time when GPS fix was obtained
            return true;  // Return true indicating a GPS fix was acquired
        }
    }
    return false;  // Return false if GPS fix is not acquired within the timeout
}

// Function to check RockBLOCK signal strength
void checkSignalStrength() {
    Serial.println("Checking signal strength..."); 
    clearSerialBuffer();  // Clear any data in RockBLOCK serial buffer
    rockBlockSerial.println("AT+CSQ");  // Send command to check signal strength
    delay(2000);  // Wait for 2 seconds
    readResponse();  // Read and print the response from RockBLOCK
}

// Function to update the OLED screen after sending the message
void displayscreenSent() {
    disp.firstPage();  // Start drawing on the first page of OLED
    do {
        disp.setFont(u8g2_font_helvB12_tr);  // Set smaller font for latitude and longitude
        disp.setCursor(10, 15);  // Set cursor position for displaying "Sent."
        disp.print(F("Sent."));

        disp.setCursor(10, 35);  // Set cursor position for latitude display
        disp.print(F("Lat: "));
        disp.print(latBuffer);  // Display the latitude

        disp.setCursor(10, 55);  // Set cursor position for longitude display
        disp.print(F("Lon: "));
        disp.print(lonBuffer);  // Display the longitude
    } while (disp.nextPage());  // Render the content on the OLED screen
}

// Function to send the message to RockBLOCK
void sendMessage(const char* msg) {
    Serial.println("Sending message: " + String(msg));  // Print the message to be sent to Serial Monitor

    clearSerialBuffer();  // Clear any existing data in the RockBLOCK serial buffer

    // Send the message to RockBLOCK buffer
    rockBlockSerial.print("AT+SBDWT=");
    rockBlockSerial.println(msg);
    delay(2000);  // Wait for 2 seconds for the message to be written into the buffer

    // Wait for an "OK" response
    if (!waitForResponse("OK")) {  
        Serial.println("Error writing message to buffer.");  // Print error if message failed
        return;
    }

    clearSerialBuffer();  // Clear the serial buffer before sending the next command

    // Send the command to transmit the message
    Serial.println("Sending AT+SBDIX command...");
    rockBlockSerial.println("AT+SBDIX");
    
    // The RockBLOCK takes time to process this command
    Serial.println("Waiting for RockBLOCK response...");
    delay(10000);  // Give RockBLOCK up to 10 seconds to respond

    // Read and print the response from RockBLOCK
    readResponse();  

    Serial.println("Message sent successfully!");  // Indicate that the message was sent

    // Update the OLED display with the sent message
    displayscreenSent();
}

// Function to wait for a specific response from RockBLOCK
bool waitForResponse(String expected) {
    unsigned long startTime = millis();
    String response = "";

    while (millis() - startTime < 5000) {  // Wait for up to 5 seconds
        while (rockBlockSerial.available()) {
            char c = rockBlockSerial.read();  // Read incoming data from RockBLOCK
            response += c;  // Append the character to the response string
        }
        if (response.length() > 0) {  // If there is any response
            Serial.println("RockBLOCK response: " + response);  
            if (response.indexOf(expected) != -1) return true;  // Check if the expected response is received
        }
    }
    Serial.println("Error: Expected '" + expected + "' but got: " + response); 
    return false;  // Return false if expected response is not received
}

// Function to clear any data in the serial buffer
void clearSerialBuffer() {
    while (rockBlockSerial.available()) {
        rockBlockSerial.read();  // Read and discard any available data
    }
}

// Function to read the response from RockBLOCK
void readResponse() {
    String response = "";
    unsigned long startTime = millis();
    
    // Keep reading for up to 15 seconds to ensure we capture the full response
    while (millis() - startTime < 15000) {
        while (rockBlockSerial.available()) {
            char c = rockBlockSerial.read();  // Read incoming data from RockBLOCK
            response += c;  // Append character to response string
            Serial.write(c);  // Print each character as it's received
        }
        if (response.length() > 0 && response.indexOf("+SBDIX:") != -1) {
            break;  // If we detected the expected response, exit early
        }
    }

    if (response.length() > 0) {
        Serial.println("\nFull RockBLOCK Response: " + response);  // Print the full response
    } else {
        Serial.println("No response received from RockBLOCK.");
    }
}

With this code I get:

OK

Full RockBLOCK Response: AT+CSQ

+CSQ:0

OK

Sending message: Lat:37.3645 Lon:-5.9880
RockBLOCK response: Օ�U���� Lon:-5.9880

OK

Sending AT+SBDIX command...
Waiting for RockBLOCK response...
AT+SBDIX

+SBDIX: 32, 9, 2, 0, 0, 0

OK

Full RockBLOCK Response: AT+SBDIX

+SBDIX: 32, 9, 2, 0, 0, 0

OK

Message already sent. Halting transmission.

The number 32 in SBDIX indicates that there is no network available so the message was not sent. I know I still have to work on this code so that it gets clearer but right now it is enough for me just to get to send the message with the position. At least, the GPS works and the RockBLOCK works too.

I tried it today and it worked!! The message was sent and I could read it clearly in RockBLOCK Admin. I know this code must be perfected but the important thing is IT WORKS.