ESP32 LoRa & SD Card Logging Data Issue

Good Evening Everyone,

Project Objective
I'm currently being stumped on a problem here for a few days now and I've resorted to finding some help online. The overall objective of this project is to have two modules. One is a Transmitter (TX) and the other a Receiver (RX). Both run on an ESP32 using an SX1276 and M10Q UBLOX GPS Module.

This problem is occurring strictly on the Receiver (RX) end.

I'd like to incorporate an SD Card Reader (preferably a MicroSD Card) to log incoming data (Latitude, Longitude, Altitude, and Velocity) from the Transmitter (TX) using LoRa capabilities and to store it onto the SD Card. However, I'm running into the topic of SPI functions when trying to utilize both the LoRa module and SD Card Module.

I have tested this code and everything works as it's designed. Although when incorporating the SD Card Module and connecting it as follows:

VCC - 5V
GND - GND
MISO - GPIO12
MOSI - GPIO13
SCK - GPIO14
CS - GPIO15

I get "Card Mounting" errors. Although, the LoRa Connection status comes back as "Successful". I'm thinking that these two might not work well together for some odd reason, but maybe one of you might know. If you can help, that'd be great! Thanks!

//(Including External Libraries)
#include <SPI.h>
#include <LoRa.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <TinyGPSPlus.h>
#include <SD.h>
//(Defines Constants w/ A Name, Value, or Pin Number)
//LoRa
#define SCK 18
#define MISO 19
#define MOSI 23
#define SS 5
#define RST 35
#define DIO0 2
//GPS Frequency Band
#define BAND 915E6
//SSD1306 OLED Display
#define OLED_SDA 21
#define OLED_SCL 22
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
//User Interface Buttons & Actions
#define UP_BUTTON_PIN 25
#define DOWN_BUTTON_PIN 26
#define ENTER_BUTTON_PIN 4
//Piezo Buzzer
#define BUZZER_PIN 27
//SD Card Reader
#define SD_MOSI 13
#define SD_MISO 12
#define SD_SCK 14
#define SD_CS 15


TinyGPSPlus gps;


float latitude = 0.0;       // Initialize latitude to 0.0
float longitude = 0.0;      // Initialize longitude to 0.0
float latitude2 = 0.0;       // Initialize latitude to 0.0
float longitude2 = 0.0;      // Initialize longitude to 0.0
float altitude = 0.0;       // Initialize altitude to 0.0
float velocity = 0.0;          // Initialize speed to 0.0
float hdop = 0.0;          // Initialize HDOP to 0.0
float maxAltitude = 0.0;  // Initialize maximum altitude to 0.0
float maxVelocity = 0.0;     // Initialize maximum speed to 0.0



double calculateDistance(float lat1, float lon1, float lat2, float lon2) {
  const double EarthRadius = 6371000.0; // Radius of the Earth in meters

  // Convert latitude and longitude from degrees to radians
  double lat1Rad = radians(lat1);
  double lon1Rad = radians(lon1);
  double lat2Rad = radians(lat2);
  double lon2Rad = radians(lon2);

  // Haversine formula
  double dLat = lat2Rad - lat1Rad;
  double dLon = lon2Rad - lon1Rad;
  double a = sin(dLat / 2) * sin(dLat / 2) + cos(lat1Rad) * cos(lat2Rad) * sin(dLon / 2) * sin(dLon / 2);
  double c = 2 * atan2(sqrt(a), sqrt(1 - a));
  double distance = EarthRadius * c;

  return distance;
}






//(GPS Declarations)
static const int RXPin = 16;
static const int TXPin = 17;
static const uint32_t GPSBaud = 9600;

//(SSD1306 Display Screen Size)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

//(Main Menu)
const int MENU_OPTION_COUNT = 5;
const char* MENU_OPTIONS[MENU_OPTION_COUNT] = {
  "Launch Status",
  "Telemetry",
  "Launch Results",
  "Locator",
  "Landing Site"
};

//(Main Menu WiFi Icon)
const unsigned char wifiicon[] PROGMEM  ={
  0x00, 0xff, 0x00, 0x7e, 0x00, 0x18,0x00, 0x00
};

//(Drawing a Filled Triangle for Selection Highlight)
void drawSelectionTriangle(int x, int y) {
  display.fillTriangle(x, y, x, y + 8, x + 4, y + 4, WHITE);
}

//(Variable Declarations for User Interface Control and Timing)
int currentOption = 0;
bool enterButtonPressed = false;
bool displayLaunchStatusPage = false;
unsigned long enterButtonPressStartTime = 0;

//(Variable Declarations for RSSI (Received Signal Strength Indicator) Measurement)
int rssi = 0;            // Store the RSSI value
int rssiPercentage = 0;  // Store the RSSI percentage

//(Button State Detection and Long-Press Check Functions)
bool isUpButtonPressed() {
  return digitalRead(UP_BUTTON_PIN) == LOW;
}
bool isDownButtonPressed() {
  return digitalRead(DOWN_BUTTON_PIN) == LOW;
}
bool isEnterButtonLongPressed() {
  return enterButtonPressed && (millis() - enterButtonPressStartTime >= 3000);
}

void handleEnterButton() {
  if (digitalRead(ENTER_BUTTON_PIN) == LOW) {
    if (!enterButtonPressed) {
      enterButtonPressStartTime = millis();
      enterButtonPressed = true;
    }
  } else {
    enterButtonPressed = false;
  }
}

//(Displaying a Startup Boot Screen)
void displayBootScreen() {
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);

  int logoWidth = strlen("ORBITFORWARD") * 6; // Each character is 6 pixels wide with font size 1
  int x = (SCREEN_WIDTH - logoWidth) / 2;

  display.setCursor(x, (SCREEN_HEIGHT - 8) / 2);
  display.println("ORBITFORWARD");
  display.display();

  // Play a Simple Startup Tune
  tone(BUZZER_PIN, 10000, 100); // Play a 400Hz tone for 200 milliseconds
  delay(150); // Delay for 100 milliseconds
  tone(BUZZER_PIN, 5000, 100); // Play a 500Hz tone for 200 milliseconds
  tone(BUZZER_PIN, 10000, 100); // Play a 400Hz tone for 200 milliseconds
  delay(150); // Delay for 100 milliseconds
  tone(BUZZER_PIN, 5000, 100); // Play a 500Hz tone for 200 milliseconds
  tone(BUZZER_PIN, 10000, 100); // Play a 400Hz tone for 200 milliseconds
  delay(150); // Delay for 100 milliseconds
  tone(BUZZER_PIN, 5000, 100); // Play a 500Hz tone for 200 milliseconds

  delay(2000); // Display boot screen for 5 seconds
}

//(Displaying the Main Menu with RSSI Information)
void displayMainMenu() {
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.println("MAIN MENU|");

  rssi = LoRa.packetRssi();
  rssiPercentage = map(rssi, -120, -40, 0, 99);
  if (rssiPercentage < 0) {
    display.drawBitmap(98,0,wifiicon,8,8,WHITE);
    display.setCursor(110, 0);
    display.println("N/A");
  } else {
    if (rssiPercentage > 99) rssiPercentage = 99;
    display.drawBitmap(98,0,wifiicon,8,8,WHITE);
    display.setCursor(110, 0);
    display.print(LoRa.packetRssi());
  }

  for (int i = 0; i < MENU_OPTION_COUNT; i++) {
    if (i == currentOption) {
      drawSelectionTriangle(5, (i * 10) + 9);
      display.setTextColor(WHITE);
    } else {
      display.setTextColor(WHITE);
    }
    display.setCursor(15, (i * 10) + 10);
    display.println(MENU_OPTIONS[i]);
  }
  display.display();
}

//(Displaying Launch Status Information)
void displayLaunchStatus() {
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);

  display.println("LAUNCH STATUS");
  display.println();

  display.print("LoRa Status: ");
    if (LoRa.begin(BAND)) {
      display.println("GO");
    } else {
      display.println("NO-GO");
    }

  display.print("GPS Status: ");
  display.println("GO");
  
  display.display();
}

//(Displaying Telemetry Information)
void displayTelemetry() {
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);

  int displayWidth = SCREEN_WIDTH;  // Get the display width

  display.setCursor(0, 0);
  display.println("TELEMETRY");
  display.println();

  // Calculate the X position for the labels and readings to be flush on the right side
  int labelX = 0;                 // X position for labels
  int readingX = displayWidth - 65;  // X position for readings (adjust as needed)

  // Display altitude label and reading
  display.setCursor(labelX, 16);
  display.print("ALTITUDE: ");
  display.setCursor(readingX, 16);
  display.println(altitude, 2);

  // Display speed label and reading
  display.setCursor(labelX, 24);
  display.print("VELOCITY: ");
  display.setCursor(readingX, 24);
  display.println(velocity, 2);

  display.display();
}

//(Displaying Launch Results Information)
void displayLaunchResults() {
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);

  display.println("LAUNCH RESULTS");
  display.println();

  display.print("MAX ALT: ");
  display.println(maxAltitude, 2);

  display.print("MAX SPEED: ");
  display.println(maxVelocity, 2);

  display.display();
}

//(Displaying Locator Information)
void displayLocator() {
  // Calculate distance
  double dist = calculateDistance(latitude2, longitude2, latitude, longitude);

  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);

  display.println("LOCATOR");
  display.println();

  // Display the calculated distance
  display.print("Distance: ");
  display.print(dist, 2); // Display distance with 2 decimal places
  display.println(" m");

  display.display();
}


//(Displaying Landing Site Information)
void displayLandingSite() {
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);

  display.println("LANDING SITE");
  display.println();

  display.print("LAT N: ");
  display.println(latitude, 6);

  display.print("LON W: ");
  display.println(longitude, 6);

  display.print("Distance Away: ");
  display.println("123.45 m");

  display.print("Bearing: ");
  display.println("270 deg");



  display.display();
}

void readGPSData() {
  while (Serial2.available() > 0) {
    if (gps.encode(Serial2.read())) {
      if (gps.location.isValid()) {
        latitude2 = gps.location.lat();
        longitude2 = gps.location.lng();
      }
    }
  }
}

void writeToSDCard(const String &data) {
  File dataFile = SD.open("data.txt", FILE_WRITE); // Open the data file in write mode

  if (dataFile) {
    dataFile.println(data); // Write the data to the file
    dataFile.close(); // Close the file
    Serial.println("Data written to SD card: " + data);
  } else {
    Serial.println("Error opening data file on SD card.");
  }
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Setup
void setup() {
  Serial.begin(115200); //Serial Communication w/ ESP32
  Serial2.begin(GPSBaud); //Serial Communication w/ NEO6M GPS
  LoRa.setSyncWord(0x12); //LoRa Sync Word Between Transmitter & Receiver
  SPI.begin(SCK, MISO, MOSI, SS);
  LoRa.setPins(SS, RST, DIO0);

  pinMode(UP_BUTTON_PIN, INPUT_PULLUP);
  pinMode(DOWN_BUTTON_PIN, INPUT_PULLUP);
  pinMode(ENTER_BUTTON_PIN, INPUT_PULLUP);

  Wire.begin(OLED_SDA, OLED_SCL);
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C, false, false)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;);
  }

  display.clearDisplay();
  displayBootScreen();

  if (!LoRa.begin(BAND)) {
    Serial.println("LoRa Connection Failed");
    while (1);
  }
  Serial.println("LoRa Connection Succeeded");

  // Initialize the SD card
  if (!SD.begin(SD_CS)) {
    Serial.println("SD card initialization failed!");
    while (1);
  }
  Serial.println("SD card initialized successfully.");

  pinMode(BUZZER_PIN, OUTPUT);
  noTone(BUZZER_PIN);
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Loop
void loop() {
  handleEnterButton(); //Responsible for Detecting/Managing Enter Button Presses
  readGPSData();
  if (isUpButtonPressed()) { //If the "UP" Button is Pressed...
    delay(200); //Delay of 0.2 Seconds
    currentOption--; //Moves to Previous Selection
    if (currentOption < 0) {
      currentOption = MENU_OPTION_COUNT - 1;
    }
  } else if (isDownButtonPressed()) { //If the "DOWN" Button is Pressed...
    delay(200); //Delay of 0.2 Seconds
    currentOption++;  //Moves to Previous Selection
    if (currentOption >= MENU_OPTION_COUNT) {
      currentOption = 0;
    }
  }

  if (isEnterButtonLongPressed()) { //If the "ENTER" Button is Pressed & Held...
    currentOption = 0;
    displayLaunchStatusPage = false; // Return to the main menu
  } else if (digitalRead(ENTER_BUTTON_PIN) == LOW) {
    delay(200); //Delay of 0.2 Seconds
    displayLaunchStatusPage = true; // Show selected page
  }

  if (displayLaunchStatusPage) { //If True, It Should Display a Specific Page
    switch (currentOption) { //Depends On What Value "currentOption" Is
      case 0: //If "0"
        displayLaunchStatus(); //Then Display Launch Status Screen
        break;
      case 1: //If "1"
        displayTelemetry(); //Then Display Telemetry Screen
        break;
      case 2: //If "2"
        displayLaunchResults(); //Then Display Launch Results Screen
        break;
      case 3: //If "3"
        displayLocator(); //Then Display Locator Screen
        break;
      case 4: //If "4"
        displayLandingSite(); //Then Display Landing Site Screen
        break;
    }
  } else { //If Untrue, Then It Should Display the Main Menu
    displayMainMenu(); //Display Main Menu
  }

// Process LoRa data
int packetSize = LoRa.parsePacket();
if (packetSize) {
    while (LoRa.available()) {
        String data = LoRa.readString();
        Serial.print(data);

        // Write the received data to the SD card
      writeToSDCard(data);
        
        // Search for and extract values based on the data labels
        int latIndex = data.indexOf("LAT:");
        if (latIndex != -1) {
          latitude = data.substring(latIndex + 4).toFloat();
        }
        
        int lonIndex = data.indexOf("LON:");
        if (lonIndex != -1) {
          longitude = data.substring(lonIndex + 4).toFloat();
        }
        
        int altIndex = data.indexOf("ALT:");
        if (altIndex != -1) {
            altitude = data.substring(altIndex + 4).toFloat();
            if (altitude > maxAltitude) {
              maxAltitude = altitude;
            }
        }
        
        int velocityIndex = data.indexOf("VELO:");
        if (velocityIndex != -1) {
            velocity = data.substring(velocityIndex + 5).toFloat();
            if (velocity > maxVelocity) {
              maxVelocity = velocity;
            }
        }
    }
}
}

you need two spi interfaces

in the include external libraries:

#include <SPI.h>
#include <FS.h>
#include <SD.h>

on top, in the variable declaration area:

SPIClass * vspi = NULL;              // VSPI LoRa
SPIClass * hspi = NULL;              // HSPI SD

in void setup():

  vspi = new SPIClass(VSPI);
  vspi->begin(SCK, MISO, MOSI, SS);   // LoRa SPI
  //loRa_SCLK 5 loRa_MISO 19 loRa_MOSI 27 loRa_SS 18 /
  pinMode(SS,   OUTPUT);
  pinMode(SCK,  OUTPUT);
  pinMode(MOSI, OUTPUT);
  pinMode(MISO,  INPUT);

  hspi = new SPIClass(HSPI);
  hspi->begin(SDCARD_SCLK, SDCARD_MISO, SDCARD_MOSI, SDCARD_CS);
  //SDCARD_SCLK 14 SDCARD_MISO 33 SDCARD_MOSI 15 SDCARD_CS 2
  pinMode(SDCARD_SCLK, OUTPUT);
  pinMode(SDCARD_MOSI, OUTPUT);
  pinMode(SDCARD_CS,   OUTPUT);
  pinMode(SDCARD_MISO, INPUT );

  if (!SD.begin())
  {
    Serial.println("SDv ");
    Serial.println();
  }

  if (SD.begin())
  {
    Serial.println("SD^ ");
    Serial.println();
    uint32_t cardSize = SD.cardSize() / (1024 * 1024);
    char outputString[2];
    itoa(cardSize, outputString, 10 );
    Serial.print("Card Size = ^ ");
    Serial.print(cardSize);
    Serial.print(" mb ");
  }

the part that starts with uint32_t cardSize = SD.cardSize is optional, but useful

Hey there!

Thanks for the help on this. I implemented what you said into the code (I had to change the wording around such as "SDCARD_SCLK" to "SD_SCK") and everything uploaded correctly.

However, when inspecting the Serial Monitor it displays:

LoRa Connection Succeeded
SDv

and nothing else...

I believe it's still not recognizing the SD Card. Although I may be wrong. Here is the updated code so everyone can take a look at it.

//(Including External Libraries)
#include <SPI.h>
#include <LoRa.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <TinyGPSPlus.h>
#include <SD.h>
#include <FS.h>
//(Defines Constants w/ A Name, Value, or Pin Number)
//LoRa
#define SCK 18
#define MISO 19
#define MOSI 23
#define SS 5
#define RST 35
#define DIO0 2
//GPS Frequency Band
#define BAND 915E6
//SSD1306 OLED Display
#define OLED_SDA 21
#define OLED_SCL 22
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
//User Interface Buttons & Actions
#define UP_BUTTON_PIN 25
#define DOWN_BUTTON_PIN 26
#define ENTER_BUTTON_PIN 4
//Piezo Buzzer
#define BUZZER_PIN 27
//SD Card Reader
#define SD_MOSI 13
#define SD_MISO 12
#define SD_SCK 14
#define SD_CS 15


TinyGPSPlus gps;

SPIClass * vspi = NULL;              // VSPI LoRa
SPIClass * hspi = NULL;              // HSPI SD


float latitude = 0.0;       // Initialize latitude to 0.0
float longitude = 0.0;      // Initialize longitude to 0.0
float latitude2 = 0.0;       // Initialize latitude to 0.0
float longitude2 = 0.0;      // Initialize longitude to 0.0
float altitude = 0.0;       // Initialize altitude to 0.0
float velocity = 0.0;          // Initialize speed to 0.0
float hdop = 0.0;          // Initialize HDOP to 0.0
float maxAltitude = 0.0;  // Initialize maximum altitude to 0.0
float maxVelocity = 0.0;     // Initialize maximum speed to 0.0



double calculateDistance(float lat1, float lon1, float lat2, float lon2) {
  const double EarthRadius = 6371000.0; // Radius of the Earth in meters

  // Convert latitude and longitude from degrees to radians
  double lat1Rad = radians(lat1);
  double lon1Rad = radians(lon1);
  double lat2Rad = radians(lat2);
  double lon2Rad = radians(lon2);

  // Haversine formula
  double dLat = lat2Rad - lat1Rad;
  double dLon = lon2Rad - lon1Rad;
  double a = sin(dLat / 2) * sin(dLat / 2) + cos(lat1Rad) * cos(lat2Rad) * sin(dLon / 2) * sin(dLon / 2);
  double c = 2 * atan2(sqrt(a), sqrt(1 - a));
  double distance = EarthRadius * c;

  return distance;
}






//(GPS Declarations)
static const int RXPin = 16;
static const int TXPin = 17;
static const uint32_t GPSBaud = 9600;

//(SSD1306 Display Screen Size)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

//(Main Menu)
const int MENU_OPTION_COUNT = 5;
const char* MENU_OPTIONS[MENU_OPTION_COUNT] = {
  "Launch Status",
  "Telemetry",
  "Launch Results",
  "Locator",
  "Landing Site"
};

//(Main Menu WiFi Icon)
const unsigned char wifiicon[] PROGMEM  ={
  0x00, 0xff, 0x00, 0x7e, 0x00, 0x18,0x00, 0x00
};

//(Drawing a Filled Triangle for Selection Highlight)
void drawSelectionTriangle(int x, int y) {
  display.fillTriangle(x, y, x, y + 8, x + 4, y + 4, WHITE);
}

//(Variable Declarations for User Interface Control and Timing)
int currentOption = 0;
bool enterButtonPressed = false;
bool displayLaunchStatusPage = false;
unsigned long enterButtonPressStartTime = 0;

//(Variable Declarations for RSSI (Received Signal Strength Indicator) Measurement)
int rssi = 0;            // Store the RSSI value
int rssiPercentage = 0;  // Store the RSSI percentage

//(Button State Detection and Long-Press Check Functions)
bool isUpButtonPressed() {
  return digitalRead(UP_BUTTON_PIN) == LOW;
}
bool isDownButtonPressed() {
  return digitalRead(DOWN_BUTTON_PIN) == LOW;
}
bool isEnterButtonLongPressed() {
  return enterButtonPressed && (millis() - enterButtonPressStartTime >= 3000);
}

void handleEnterButton() {
  if (digitalRead(ENTER_BUTTON_PIN) == LOW) {
    if (!enterButtonPressed) {
      enterButtonPressStartTime = millis();
      enterButtonPressed = true;
    }
  } else {
    enterButtonPressed = false;
  }
}

//(Displaying a Startup Boot Screen)
void displayBootScreen() {
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);

  int logoWidth = strlen("ORBITFORWARD") * 6; // Each character is 6 pixels wide with font size 1
  int x = (SCREEN_WIDTH - logoWidth) / 2;

  display.setCursor(x, (SCREEN_HEIGHT - 8) / 2);
  display.println("ORBITFORWARD");
  display.display();

  // Play a Simple Startup Tune
  tone(BUZZER_PIN, 10000, 100); // Play a 400Hz tone for 200 milliseconds
  delay(150); // Delay for 100 milliseconds
  tone(BUZZER_PIN, 5000, 100); // Play a 500Hz tone for 200 milliseconds
  tone(BUZZER_PIN, 10000, 100); // Play a 400Hz tone for 200 milliseconds
  delay(150); // Delay for 100 milliseconds
  tone(BUZZER_PIN, 5000, 100); // Play a 500Hz tone for 200 milliseconds
  tone(BUZZER_PIN, 10000, 100); // Play a 400Hz tone for 200 milliseconds
  delay(150); // Delay for 100 milliseconds
  tone(BUZZER_PIN, 5000, 100); // Play a 500Hz tone for 200 milliseconds

  delay(2000); // Display boot screen for 5 seconds
}

//(Displaying the Main Menu with RSSI Information)
void displayMainMenu() {
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.println("MAIN MENU|");

  rssi = LoRa.packetRssi();
  rssiPercentage = map(rssi, -120, -40, 0, 99);
  if (rssiPercentage < 0) {
    display.drawBitmap(98,0,wifiicon,8,8,WHITE);
    display.setCursor(110, 0);
    display.println("N/A");
  } else {
    if (rssiPercentage > 99) rssiPercentage = 99;
    display.drawBitmap(98,0,wifiicon,8,8,WHITE);
    display.setCursor(110, 0);
    display.print(LoRa.packetRssi());
  }

  for (int i = 0; i < MENU_OPTION_COUNT; i++) {
    if (i == currentOption) {
      drawSelectionTriangle(5, (i * 10) + 9);
      display.setTextColor(WHITE);
    } else {
      display.setTextColor(WHITE);
    }
    display.setCursor(15, (i * 10) + 10);
    display.println(MENU_OPTIONS[i]);
  }
  display.display();
}

//(Displaying Launch Status Information)
void displayLaunchStatus() {
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);

  display.println("LAUNCH STATUS");
  display.println();

  display.print("LoRa Status: ");
    if (LoRa.begin(BAND)) {
      display.println("GO");
    } else {
      display.println("NO-GO");
    }

  display.print("GPS Status: ");
  display.println("GO");
  
  display.display();
}

//(Displaying Telemetry Information)
void displayTelemetry() {
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);

  int displayWidth = SCREEN_WIDTH;  // Get the display width

  display.setCursor(0, 0);
  display.println("TELEMETRY");
  display.println();

  // Calculate the X position for the labels and readings to be flush on the right side
  int labelX = 0;                 // X position for labels
  int readingX = displayWidth - 65;  // X position for readings (adjust as needed)

  // Display altitude label and reading
  display.setCursor(labelX, 16);
  display.print("ALTITUDE: ");
  display.setCursor(readingX, 16);
  display.println(altitude, 2);

  // Display speed label and reading
  display.setCursor(labelX, 24);
  display.print("VELOCITY: ");
  display.setCursor(readingX, 24);
  display.println(velocity, 2);

  display.display();
}

//(Displaying Launch Results Information)
void displayLaunchResults() {
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);

  display.println("LAUNCH RESULTS");
  display.println();

  display.print("MAX ALT: ");
  display.println(maxAltitude, 2);

  display.print("MAX SPEED: ");
  display.println(maxVelocity, 2);

  display.display();
}

//(Displaying Locator Information)
void displayLocator() {
  // Calculate distance
  double dist = calculateDistance(latitude2, longitude2, latitude, longitude);

  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);

  display.println("LOCATOR");
  display.println();

  // Display the calculated distance
  display.print("Distance: ");
  display.print(dist, 2); // Display distance with 2 decimal places
  display.println(" m");

  display.display();
}


//(Displaying Landing Site Information)
void displayLandingSite() {
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);

  display.println("LANDING SITE");
  display.println();

  display.print("LAT N: ");
  display.println(latitude, 6);

  display.print("LON W: ");
  display.println(longitude, 6);

  display.print("Distance Away: ");
  display.println("123.45 m");

  display.print("Bearing: ");
  display.println("270 deg");



  display.display();
}

void readGPSData() {
  while (Serial2.available() > 0) {
    if (gps.encode(Serial2.read())) {
      if (gps.location.isValid()) {
        latitude2 = gps.location.lat();
        longitude2 = gps.location.lng();
      }
    }
  }
}

void writeToSDCard(const String &data) {
  File dataFile = SD.open("data.txt", FILE_WRITE); // Open the data file in write mode

  if (dataFile) {
    dataFile.println(data); // Write the data to the file
    dataFile.close(); // Close the file
    Serial.println("Data written to SD card: " + data);
  } else {
    Serial.println("Error opening data file on SD card.");
  }
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Setup
void setup() {
  Serial.begin(115200); //Serial Communication w/ ESP32
  Serial2.begin(GPSBaud); //Serial Communication w/ NEO6M GPS
  LoRa.setSyncWord(0x12); //LoRa Sync Word Between Transmitter & Receiver
  SPI.begin(SCK, MISO, MOSI, SS);
  LoRa.setPins(SS, RST, DIO0);

  pinMode(UP_BUTTON_PIN, INPUT_PULLUP);
  pinMode(DOWN_BUTTON_PIN, INPUT_PULLUP);
  pinMode(ENTER_BUTTON_PIN, INPUT_PULLUP);

  Wire.begin(OLED_SDA, OLED_SCL);
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C, false, false)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;);
  }

  display.clearDisplay();
  displayBootScreen();

  if (!LoRa.begin(BAND)) {
    Serial.println("LoRa Connection Failed");
    while (1);
  }
  Serial.println("LoRa Connection Succeeded");

  vspi = new SPIClass(VSPI);
  vspi->begin(SCK, MISO, MOSI, SS);   // LoRa SPI
  //loRa_SCLK 5 loRa_MISO 19 loRa_MOSI 27 loRa_SS 18 /
  pinMode(SS,   OUTPUT);
  pinMode(SCK,  OUTPUT);
  pinMode(MOSI, OUTPUT);
  pinMode(MISO,  INPUT);

  hspi = new SPIClass(HSPI);
  hspi->begin(SD_SCK, SD_MISO, SD_MOSI, SD_CS);
  //SDCARD_SCLK 14 SDCARD_MISO 33 SDCARD_MOSI 15 SDCARD_CS 2
  pinMode(SD_SCK, OUTPUT);
  pinMode(SD_MOSI, OUTPUT);
  pinMode(SD_CS,   OUTPUT);
  pinMode(SD_MISO, INPUT );

  if (!SD.begin())
  {
    Serial.println("SDv ");
    Serial.println();
  }

  if (SD.begin())
  {
    Serial.println("SD^ ");
    Serial.println();
    uint32_t cardSize = SD.cardSize() / (1024 * 1024);
    char outputString[2];
    itoa(cardSize, outputString, 10 );
    Serial.print("Card Size = ^ ");
    Serial.print(cardSize);
    Serial.print(" mb ");
  }


  pinMode(BUZZER_PIN, OUTPUT);
  noTone(BUZZER_PIN);
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Loop
void loop() {
  handleEnterButton(); //Responsible for Detecting/Managing Enter Button Presses
  readGPSData();
  if (isUpButtonPressed()) { //If the "UP" Button is Pressed...
    delay(200); //Delay of 0.2 Seconds
    currentOption--; //Moves to Previous Selection
    if (currentOption < 0) {
      currentOption = MENU_OPTION_COUNT - 1;
    }
  } else if (isDownButtonPressed()) { //If the "DOWN" Button is Pressed...
    delay(200); //Delay of 0.2 Seconds
    currentOption++;  //Moves to Previous Selection
    if (currentOption >= MENU_OPTION_COUNT) {
      currentOption = 0;
    }
  }

  if (isEnterButtonLongPressed()) { //If the "ENTER" Button is Pressed & Held...
    currentOption = 0;
    displayLaunchStatusPage = false; // Return to the main menu
  } else if (digitalRead(ENTER_BUTTON_PIN) == LOW) {
    delay(200); //Delay of 0.2 Seconds
    displayLaunchStatusPage = true; // Show selected page
  }

  if (displayLaunchStatusPage) { //If True, It Should Display a Specific Page
    switch (currentOption) { //Depends On What Value "currentOption" Is
      case 0: //If "0"
        displayLaunchStatus(); //Then Display Launch Status Screen
        break;
      case 1: //If "1"
        displayTelemetry(); //Then Display Telemetry Screen
        break;
      case 2: //If "2"
        displayLaunchResults(); //Then Display Launch Results Screen
        break;
      case 3: //If "3"
        displayLocator(); //Then Display Locator Screen
        break;
      case 4: //If "4"
        displayLandingSite(); //Then Display Landing Site Screen
        break;
    }
  } else { //If Untrue, Then It Should Display the Main Menu
    displayMainMenu(); //Display Main Menu
  }

// Process LoRa data
int packetSize = LoRa.parsePacket();
if (packetSize) {
    while (LoRa.available()) {
        String data = LoRa.readString();
        Serial.print(data);

        // Write the received data to the SD card
      writeToSDCard(data);
        
        // Search for and extract values based on the data labels
        int latIndex = data.indexOf("LAT:");
        if (latIndex != -1) {
          latitude = data.substring(latIndex + 4).toFloat();
        }
        
        int lonIndex = data.indexOf("LON:");
        if (lonIndex != -1) {
          longitude = data.substring(lonIndex + 4).toFloat();
        }
        
        int altIndex = data.indexOf("ALT:");
        if (altIndex != -1) {
            altitude = data.substring(altIndex + 4).toFloat();
            if (altitude > maxAltitude) {
              maxAltitude = altitude;
            }
        }
        
        int velocityIndex = data.indexOf("VELO:");
        if (velocityIndex != -1) {
            velocity = data.substring(velocityIndex + 5).toFloat();
            if (velocity > maxVelocity) {
              maxVelocity = velocity;
            }
        }
    }
}
}```

do you have a card inserted? it won't initiate just the module, it requires a card

Yes. I uploaded it without the SD card in the slot, inserted it when it finished uploading, and pushed the RESET button on the ESP32. It still showed this…

Also, the SD card has been formatted properly too.

If its one of the often used 5V SD card modules, they dont work properly when there is another SPI device on the SPI device, show us a picture of your module.

As a general comment I have not found a LoRa device and a SD card on the same SPI bus that reliable when used on an ESP32. Its better to run the SD card on a seperate SPI bus or indeed the built in MMC hardware interface that the ESP32 has.

Here’s a picture of the SD Card Module I’m using:

Thats the one, it will stop other devices on the SPI bus working. Its not for 3V3 logic level Arduinos as well.

An SD card does not need logic level conversion circuits to work on an ESP32.

For a ESP32 you really should be using a SD module that looks like this;

Screenshot - 15_10_2023 , 16_13_38

Gotcha. I’ll order these right now. Do you think it’s the code at all too or does the code look fine to you? I’ll give you an update on how it goes when I swap the modules.

Do these look good to you?

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.