Lora + sd + esp32 chip

For the LoRa library, if your changing the SPI bus from default you would normally se this command, as detailed in the library API;

LoRa.setSPI(spi);

I have mixed LoRa devices and SD and TFT displays on the same SPI bus extensively, mainly on ESP32S3.

Whilst the combination works well with no crashes etc, I have noticed that the SD card can go offline under heavy use.

So my preferred setup is to leave the LoRa device and TFT display on the default SPI bus and put the SD card on the HSPI bus. This is more reliable.

Or use the SD card in MMC mode which eliminates the potential SPI conflict completely.

I also have not gone beyond version 2.0.14 of the ESP32 core for Arduino, too many issues with some well established libraries.

1 Like

could you share me a code example that works for you? so I can adapt mine and tell you if it works

Having the SD on its own SPI could be helpful as some SD cards don't play nice on the bus. They don't tri-state MISO when they're unselected.

Not really, I dont use the same LoRa library your using.

I would suggest you just setup a basic code that has both the LoRa device and SD card run on the default SPI bus.

Check that such a basic code initializes both the LoRa device and SD card without crashing.

Do post a picture or link for the SD card module\holder, as @gfvalvo has said some such module\holders dont work properly.

1 Like

try the following with SD card on VSPI and RFM95 on HSPI

file ESP32_RFM95_HSPI.ino

// ESP32 using HSPI connected to a RFM95W LoRa module

#include <SPI.h>
#include <LoRa.h>

// default HSPI pins used by RFM95 LoRa module
#define HSPI_SCK 14
#define HSPI_MISO 12
#define HSPI_MOSI 13
#define HSPI_CS 15
#define HSPI_RESET 4
#define HSPI_D0 2

SPIClass *hspi = new SPIClass(HSPI);  // HSPI object used by MFRC522

void SDsetup();
void SDloop();

void setup() {
  Serial.begin(115200);
  delay(2000);
  SDsetup();
  Serial.println("\n\nESP32 LoRa RFM95W Receiver");
  // void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN);
  //LoRa.setPins(8, 4, 7);  // for Lora 32u4
  LoRa.setSPI(*hspi);
  LoRa.setPins(HSPI_CS, HSPI_RESET, HSPI_D0);  //4, 2);  // for ESP32
  if (!LoRa.begin(866E6)) {
    Serial.println("Starting LoRa failed!");
    while (1)
      ;
  }
  Serial.println("Starting LoRa OK!");
}

void loop() {
  // try to parse packet
  int packetSize = LoRa.parsePacket();
  if (packetSize) {
    // received a packet
    Serial.print("Received packet '");
    // read packet
    while (LoRa.available()) {
      Serial.print((char)LoRa.read());
    }
    // print RSSI of packet
    Serial.print("' with RSSI ");
    Serial.println(LoRa.packetRssi());
  }
  static unsigned int timer1 = millis();
  if (millis() - timer1 > 1000) {
    timer1 = millis();
    SDloop();  // call SD functions
  }
}

file ESP32_SD_VSPI.cpp (in same directory as above .ino file)

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-microsd-card-arduino/
  
  This sketch can be found at: Examples > SD(esp32) > SD_Test
*/

// ESP32 connections
// ESP32 SCK pin GPIO18  to SD card  SCK
// ESP32 MISO pin GPIO19  to SD card  MISO
// ESP32 MOSI pin GPIO23  to SD card  MOSI
// ESP32 SS  pin GPIO 5   to SD card SS
// ESP32 5V to SD VCC and GND to GND

#include "FS.h"
#include "SD.h"
#include "SPI.h"

void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
  Serial.printf("Listing directory: %s\n", dirname);

  File root = fs.open(dirname);
  if(!root){
    Serial.println("Failed to open directory");
    return;
  }
  if(!root.isDirectory()){
    Serial.println("Not a directory");
    return;
  }

  File file = root.openNextFile();
  while(file){
    if(file.isDirectory()){
      Serial.print("  DIR : ");
      Serial.println(file.name());
      if(levels){
        listDir(fs, file.name(), levels -1);
      }
    } else {
      Serial.print("  FILE: ");
      Serial.print(file.name());
      Serial.print("  SIZE: ");
      Serial.println(file.size());
    }
    file = root.openNextFile();
  }
}

void createDir(fs::FS &fs, const char * path){
  Serial.printf("Creating Dir: %s\n", path);
  if(fs.mkdir(path)){
    Serial.println("Dir created");
  } else {
    Serial.println("mkdir failed");
  }
}

void removeDir(fs::FS &fs, const char * path){
  Serial.printf("Removing Dir: %s\n", path);
  if(fs.rmdir(path)){
    Serial.println("Dir removed");
  } else {
    Serial.println("rmdir failed");
  }
}

void readFile(fs::FS &fs, const char * path){
  Serial.printf("Reading file: %s\n", path);

  File file = fs.open(path);
  if(!file){
    Serial.println("Failed to open file for reading");
    return;
  }

  Serial.print("Read from file: ");
  while(file.available()){
    Serial.write(file.read());
  }
  file.close();
}

void writeFile(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Writing file: %s\n", path);

  File file = fs.open(path, FILE_WRITE);
  if(!file){
    Serial.println("Failed to open file for writing");
    return;
  }
  if(file.print(message)){
    Serial.println("File written");
  } else {
    Serial.println("Write failed");
  }
  file.close();
}

void appendFile(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Appending to file: %s\n", path);

  File file = fs.open(path, FILE_APPEND);
  if(!file){
    Serial.println("Failed to open file for appending");
    return;
  }
  if(file.print(message)){
      Serial.println("Message appended");
  } else {
    Serial.println("Append failed");
  }
  file.close();
}

void renameFile(fs::FS &fs, const char * path1, const char * path2){
  Serial.printf("Renaming file %s to %s\n", path1, path2);
  if (fs.rename(path1, path2)) {
    Serial.println("File renamed");
  } else {
    Serial.println("Rename failed");
  }
}

void deleteFile(fs::FS &fs, const char * path){
  Serial.printf("Deleting file: %s\n", path);
  if(fs.remove(path)){
    Serial.println("File deleted");
  } else {
    Serial.println("Delete failed");
  }
}

void testFileIO(fs::FS &fs, const char * path){
  File file = fs.open(path);
  static uint8_t buf[512];
  size_t len = 0;
  uint32_t start = millis();
  uint32_t end = start;
  if(file){
    len = file.size();
    size_t flen = len;
    start = millis();
    while(len){
      size_t toRead = len;
      if(toRead > 512){
        toRead = 512;
      }
      file.read(buf, toRead);
      len -= toRead;
    }
    end = millis() - start;
    Serial.printf("%u bytes read for %u ms\n", flen, end);
    file.close();
  } else {
    Serial.println("Failed to open file for reading");
  }


  file = fs.open(path, FILE_WRITE);
  if(!file){
    Serial.println("Failed to open file for writing");
    return;
  }

  size_t i;
  start = millis();
  for(i=0; i<2048; i++){
    file.write(buf, 512);
  }
  end = millis() - start;
  Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end);
  file.close();
}

void SDsetup(){
  Serial.begin(115200);
  if(!SD.begin(5)){
    Serial.println("Card Mount Failed");
    return;
  }
  uint8_t cardType = SD.cardType();

  if(cardType == CARD_NONE){
    Serial.println("No SD card attached");
    return;
  }

  Serial.print("SD Card Type: ");
  if(cardType == CARD_MMC){
    Serial.println("MMC");
  } else if(cardType == CARD_SD){
    Serial.println("SDSC");
  } else if(cardType == CARD_SDHC){
    Serial.println("SDHC");
  } else {
    Serial.println("UNKNOWN");
  }

  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.printf("SD Card Size: %lluMB\n", cardSize);

  listDir(SD, "/", 0);
  createDir(SD, "/mydir");
  listDir(SD, "/", 0);
  removeDir(SD, "/mydir");
  listDir(SD, "/", 2);
  writeFile(SD, "/hello.txt", "Hello ");
  appendFile(SD, "/hello.txt", "World!\n");
  readFile(SD, "/hello.txt");
  deleteFile(SD, "/foo.txt");
  renameFile(SD, "/hello.txt", "/foo.txt");
  readFile(SD, "/foo.txt");
  testFileIO(SD, "/test.txt");
  Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
  Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
}

void SDloop(){
 Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
  Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
}


serial monitor output

SD Card Type: SDSC
SD Card Size: 121MB
Listing directory: /
  DIR : System Volume Information
  FILE: data.txt  SIZE: 486
  FILE: test.txt  SIZE: 77824
  FILE: foo.txt  SIZE: 13
  FILE: TestNumber.txt  SIZE: 2
  FILE: test1.dat  SIZE: 7
  FILE: test2.dat  SIZE: 12
  FILE: test3.dat  SIZE: 68
  FILE: test5.dat  SIZE: 7
  FILE: test6.dat  SIZE: 7
  FILE: test7.dat  SIZE: 7
  FILE: test8.dat  SIZE: 7
  FILE: test9.dat  SIZE: 7
  FILE: test10.dat  SIZE: 14
  FILE: test11.dat  SIZE: 7
  FILE: test12.dat  SIZE: 7
  FILE: test13.dat  SIZE: 5
  FILE: test14.dat  SIZE: 354
  FILE: test15.dat  SIZE: 35
  FILE: test16.dat  SIZE: 10682
  FILE: test17.dat  SIZE: 259
  FILE: test18.dat  SIZE: 343
  FILE: test19.dat  SIZE: 42
  FILE: test20.dat  SIZE: 300
  FILE: test21.dat  SIZE: 55
Creating Dir: /mydir
Dir created
Listing directory: /
  DIR : System Volume Information
  FILE: data.txt  SIZE: 486
  FILE: test.txt  SIZE: 77824
  FILE: foo.txt  SIZE: 13
  FILE: TestNumber.txt  SIZE: 2
  FILE: test1.dat  SIZE: 7
  FILE: test2.dat  SIZE: 12
  FILE: test3.dat  SIZE: 68
  FILE: test5.dat  SIZE: 7
  FILE: test6.dat  SIZE: 7
  FILE: test7.dat  SIZE: 7
  FILE: test8.dat  SIZE: 7
  FILE: test9.dat  SIZE: 7
  FILE: test10.dat  SIZE: 14
  FILE: test11.dat  SIZE: 7
  FILE: test12.dat  SIZE: 7
  FILE: test13.dat  SIZE: 5
  FILE: test14.dat  SIZE: 354
  FILE: test15.dat  SIZE: 35
  FILE: test16.dat  SIZE: 10682
  FILE: test17.dat  SIZE: 259
  FILE: test18.dat  SIZE: 343
  FILE: test19.dat  SIZE: 42
  FILE: test20.dat  SIZE: 300
  FILE: test21.dat  SIZE: 55
  DIR : mydir
Removing Dir: /mydir
Dir removed
Listing directory: /
  DIR : System Volume Information
Listing directory: System Volume Information
Failed to open directory
  FILE: data.txt  SIZE: 486
  FILE: test.txt  SIZE: 77824
  FILE: foo.txt  SIZE: 13
  FILE: TestNumber.txt  SIZE: 2
  FILE: test1.dat  SIZE: 7
  FILE: test2.dat  SIZE: 12
  FILE: test3.dat  SIZE: 68
  FILE: test5.dat  SIZE: 7
  FILE: test6.dat  SIZE: 7
  FILE: test7.dat  SIZE: 7
  FILE: test8.dat  SIZE: 7
  FILE: test9.dat  SIZE: 7
  FILE: test10.dat  SIZE: 14
  FILE: test11.dat  SIZE: 7
  FILE: test12.dat  SIZE: 7
  FILE: test13.dat  SIZE: 5
  FILE: test14.dat  SIZE: 354
  FILE: test15.dat  SIZE: 35
  FILE: test16.dat  SIZE: 10682
  FILE: test17.dat  SIZE: 259
  FILE: test18.dat  SIZE: 343
  FILE: test19.dat  SIZE: 42
  FILE: test20.dat  SIZE: 300
  FILE: test21.dat  SIZE: 55
Writing file: /hello.txt
File written
Appending to file: /hello.txt
Message appended
Reading file: /hello.txt
Read from file: Hello World!
Deleting file: /foo.txt
File deleted
Renaming file /hello.txt to /foo.txt
File renamed
Reading file: /foo.txt
Read from file: Hello World!
77824 bytes read for 188 ms
1048576 bytes written for 256 ms
Total space: 120MB
Used space: 0MB


ESP32 LoRa RFM95W Receiver
Starting LoRa OK!
Received packet 'hello 2282' with RSSI -51
Received packet 'hello 2283' with RSSI -50
Total space: 120MB
Used space: 0MB
Received packet 'hello 2284' with RSSI -48
Received packet 'hello 2285' with RSSI -51
Total space: 120MB
Used space: 0MB
Received packet 'hello 2286' with RSSI -51
Received packet 'hello 2287' with RSSI -51
Total space: 120MB
Used space: 0MB
Received packet 'hello 2288' with RSSI -51
Received packet 'hello 2289' with RSSI -51
Total space: 120MB
Used space: 0MB
Received packet 'hello 2290' with RSSI -51
Total space: 120MB
Used space: 0MB
Received packet 'hello 2291' with RSSI -51
Received packet 'hello 2292' with RSSI -51
Total space: 120MB
Used space: 0MB
Received packet 'hello 2293' with RSSI -49
Received packet 'hello 2294' with RSSI -50
Total space: 120MB
Used space: 0MB
Received packet 'hello 2295' with RSSI -51
Received packet 'hello 2296' with RSSI -51
Total space: 120MB
Used space: 0MB
Received packet 'hello 2297' with RSSI -51

photo

1 Like

I finally solved it, it works now :slight_smile:

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

// Definizione dei pin SPI per SD (HSPI) e LoRa (VSPI)
#define SD_MISO  12
#define SD_MOSI  13
#define SD_SCLK  14
#define SD_SS    15

#define LORA_MISO  19
#define LORA_MOSI  23
#define LORA_SCLK  18
#define LORA_SS    5
#define LORA_RST   26  // Reset LoRa
#define LORA_DIO0  27   // Interrupt LoRa

// Frequenza LoRa (da adattare al tuo paese)
#define LORA_FREQUENCY  868E6  

byte destination_address = 0x3E;
// Inizializzazione dei bus SPI
SPIClass *hspi = NULL;  // HSPI per la SD
SPIClass *vspi = NULL;  // VSPI per LoRa

void setup() {
  Serial.begin(115200);
  delay(1000); // Piccola pausa per stabilizzazione

  // Creazione delle istanze SPI
  hspi = new SPIClass(HSPI);
  vspi = new SPIClass(VSPI);

  // Inizializzazione HSPI per la scheda SD
  hspi->begin(SD_SCLK, SD_MISO, SD_MOSI, SD_SS);
  pinMode(SD_SS, OUTPUT);

  if (!SD.begin(SD_SS, *hspi)) {
    Serial.println("⚠️ Errore nell'inizializzazione della SD!");
    return;
  }
  Serial.println("✅ SD inizializzata con successo!");

  // Inizializzazione VSPI per LoRa
  vspi->begin(LORA_SCLK, LORA_MISO, LORA_MOSI, LORA_SS);
  LoRa.setSPI(*vspi);
  LoRa.setPins(LORA_SS, LORA_RST, LORA_DIO0);

  if (!LoRa.begin(LORA_FREQUENCY)) {
    Serial.println("⚠️ Errore nell'inizializzazione di LoRa!");
    return;
  }

    LoRa.setTxPower(17);   //17 dB (defoult)(max power)
  LoRa.setSpreadingFactor(7);  //7 defoult (can be set from 6 to 12)
  LoRa.setSignalBandwidth(62.5E3); //125E3 defoult (7.8 10.4 15.6 20.8 31.25 41.7 62.5 125 250 500) 
  LoRa.setCodingRate4(8); //5 defoult (can be from 5 to 8 that correspond to 4/5 and 4/8)


  Serial.println("✅ LoRa inizializzato con successo!");
}

void loop() {
  unsigned long timestamp = millis();
  salvaSuSD(timestamp);
  inviaLoRa(timestamp);
  delay(5000);  // Aspetta 5 secondi prima del prossimo ciclo
}

// Funzione per salvare il timestamp sulla scheda SD
void salvaSuSD(unsigned long timestamp) {
  File file = SD.open("/log.txt", FILE_APPEND);
  if (file) {
    file.println(timestamp);
    file.close();
    Serial.print("📂 Salvato su SD: ");
    Serial.println(timestamp);
  } else {
    Serial.println("❌ Errore nell'apertura del file sulla SD!");
  }
}

// Funzione per inviare il timestamp via LoRa
void inviaLoRa(unsigned long timestamp) {
  LoRa.beginPacket();
  LoRa.write(destination_address);
  LoRa.print(timestamp);
  LoRa.endPacket();
  Serial.print("📡 Inviato via LoRa: ");
  Serial.println(timestamp);
}

Serial:

✅ SD inizializzata con successo!
✅ LoRa inizializzato con successo!
📂 Salvato su SD: 1055
📡 Inviato via LoRa: 1055
📂 Salvato su SD: 6160
📡 Inviato via LoRa: 6160
📂 Salvato su SD: 11243
📡 Inviato via LoRa: 11243
📂 Salvato su SD: 16325
📡 Inviato via LoRa: 16325

And the problem with your original code was ?

Initialization and instances were bad. Now everything works fine without any hitches

A post was split to a new topic: Heltec ESP32 Lora Project