Problems with the SD Card on ESP32

Hello! I'm having some issues with handling the SD card in a LoRa receiver on ESP32 (file code below). The intention is to ensure that the JSONs that were not sent to the API are saved on the card for subsequent resending at specific intervals, and if the resend is successful (or returns 400), the file is deleted. It is working, however, some situations are happening at random moments:

  1. Sometimes an error occurs when creating the directory (device_X) with the device ID to store the JSON, even though in other situations it is created normally. I tried to create a max attempts system to avoid this, but the problem still appears.

  2. Sometimes it fails to serialize the JSON (apparently) and creates an empty text file, thus causing an error when resending.

  3. After performing an operation on the card, it tries to reconnect and reboots with the "Guru Meditation Error: Core 1 panic'ed (StoreProhibited)"

  4. Sometimes the code stops and the receiver no longer receives data after performing an operation with the card.

Anyone knows how to fix or avoid these problems? Note that the serial monitor images are in Portuguese, but the code I translated to English. Thanks!

Receiver_2806.ino (15,4,KB)

which particular ESP32 and LoRa module are you using?
how are the SD card and LoRa module connected to the ESP32?
if the SD card is sharing a SPI bus with other devices there can be problems, see ESP32 sdspi_share

@horace Hello, friend! I'm using the Ebyte E220-900T30D and the ESP WROOM-32.

// LORA

M0 —– 25
M1 —– 26
AUX —– 34
TX —– RX2
RX —– TX2
GND —– GND
VCC —– 3.3V

// SD CARD

CS —– 2
MISO —– 19
MOSI —– 23
SCK —– 18
GND —– GND
VCC —– 5V

connections look OK
think you need to post the code using code tags, e.g. click < CODE > and paste you code where it says 'type or paste code here
also avoid posting images of text - they take a lot of space and cannot be copied from - post as text

@horace

#include "Arduino.h"
#include "LoRa_E220.h"
#include "CustomJWT.h"
#include "ArduinoJson.h"
#include <WiFi.h>
#include <HTTPClient.h>
#include <map>
#include "SD.h"
#include <NTPClient.h>

#define PIN_M0 25
#define PIN_M1 26
#define PIN_AUX 34

const int CS = 2;

const char* wifiName = "(WIFI NAME HERE)";
const char* wifiPassword = "(WIFI PASS HERE)";

const char* postUrlApi = "(API HERE)";

char key[] = "teste";
CustomJWT jwt(key, 256);

LoRa_E220 e220ttl(&Serial2, PIN_AUX, PIN_M0, PIN_M1);  // RX AUX M0 M1

int farmId = 1; 
const char* hashingKey = "(KEY HERE)";  // Key for hashing devID 

int dBmRssi = 0;
std::map<int, int> lastPacket;

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", -3 * 3600, 60000);  // UTC-3 for Brasília time

struct Interval {
  int startHour;
  int startMinute;
  int endHour;
  int endMinute;
};

Interval intervals[] = {
  {7, 31, 7, 59},      // Interval from 07:31 to 07:59
  {18, 31, 18, 59},    // Interval from 18:31 to 18:59
  {23, 1, 23, 59}      // Interval from 23:01 to 23:59
};

const int NUM_INTERVALS = sizeof(intervals) / sizeof(intervals[0]);

void setup() {
  Serial.begin(9600);
  delay(100);
  e220ttl.begin();
  setParameters();

  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  WiFi.begin(wifiName, wifiPassword);
  Serial.println(WiFi.status());
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(250);
  }
  Serial.println();
  Serial.println("Connected!");

  // SD Initialization
  if (!SD.begin(CS)) {
    Serial.println("SD initialization failed!");
    return;
  }
  Serial.println("SD initialized.");

  if (SD.exists("/devices")) {
    Serial.println("The /devices directory exists.");
  } else {
    Serial.println("The /devices directory does not exist. Trying to create...");
    if (SD.mkdir("/devices")) {
      Serial.println("Directory /devices created successfully.");
    } else {
      Serial.println("Failed to create the /devices directory.");
    }
  }
}

void loop() {
  timeClient.update();
  int currentHour = timeClient.getHours();
  int currentMinute = timeClient.getMinutes();

  if (e220ttl.available() > 1) {
    jwt.allocateJWTMemory();

    ResponseContainer rc = e220ttl.receiveMessageRSSI();
    String message = rc.data;
    dBmRssi = -(256 - rc.rssi);
    int len = message.length();

    const char* pack = message.c_str();
    int length = strlen(pack);
    message[length] = '\0';
    char* packet = unconstchar(pack);

    jwt.decodeJWT(packet);

    String payload = jwt.payload;

    JsonDocument doc;
    extractData(payload, doc);

    int field_1 = doc["deviceId"];
    int field_2 = doc["sectionFarm"];
    int field_3 = doc["packetId"];
    float field_4 = doc["voltage"];
    float field_5 = doc["humidity0to10"];
    float field_6 = doc["humidity10to20"];
    float field_7 = doc["humidity20to30"];
    float field_8 = doc["temperature"];
    float field_9 = doc["fruitSize"];
    float field_10 = doc["stemSize"];
    String field_11 = doc["dateCreate"];

    int loraDeviceId = doc["deviceId"];

    if (loraDeviceId != NULL) { 
      if (lastPacket.find(loraDeviceId) == lastPacket.end() || field_3 != lastPacket[loraDeviceId]) {
        Serial.printf("Header: %s\nHeader Length: %d\n", jwt.header, jwt.headerLength);
        Serial.printf("Payload: %s\nPayload Length: %d\n", jwt.payload, jwt.payloadLength);
        Serial.printf("Signature: %s\nSignature Length: %d\n", jwt.signature, jwt.signatureLength);
        Serial.println("-------------------------");

        Serial.print("[DEVICE ("); Serial.print(field_1); Serial.print(") - SECTION ("); Serial.print(field_2); Serial.println(")]");
        Serial.print("RSSI: "); Serial.println(dBmRssi);
        Serial.print("[1] Packet: "); Serial.println(field_3);
        Serial.print("[2] Voltage: "); Serial.println(field_4);
        Serial.print("[3] Humidity (0-10): "); Serial.println(field_5);
        Serial.print("[4] Humidity (10-20): "); Serial.println(field_6);
        Serial.print("[5] Humidity (20-30): "); Serial.println(field_7);
        Serial.print("[6] Temperature: "); Serial.println(field_8);
        Serial.print("[7] Fruit Size: "); Serial.println(field_9);
        Serial.print("[8] Stem Size: "); Serial.println(field_10);
        Serial.print("[9] Date-Time: "); Serial.println(field_11);

        int sectionId = doc["sectionFarm"];
        String devId = generateId(loraDeviceId, sectionId, farmId);

        postApi(doc, devId);

        lastPacket[loraDeviceId] = field_3;

        jwt.clear();
      }
    }
  }

  if (connectedToInternet() && e220ttl.available() <= 1 && resendInterval(currentHour, currentMinute)) {
    resendJson();
  }

  if (WiFi.status() != WL_CONNECTED) {
    for (int i = 0; i < 3; i++) {
      WiFi.begin(wifiName, wifiPassword);
      if (WiFi.status() == WL_CONNECTED) {
        Serial.println("Reconnected");
        break;
      }
      Serial.println("Trying to reconnect...");
      delay(2000);
    }
  }
}

void extractData(String message, JsonDocument& doc) {
  int indexD = message.indexOf('D');
  doc["deviceId"] = message.substring(0, indexD).toInt();

  int indexS = message.indexOf('S');
  doc["sectionFarm"] = message.substring(indexD + 1, indexS).toInt();

  int indexP = message.indexOf('P');
  doc["packetId"] = message.substring(indexS + 1, indexP).toInt();

  int indexV = message.indexOf('V');
  doc["voltage"] = message.substring(indexP + 1, indexV).toFloat();

  int indexU1 = message.indexOf('U');
  int indexU2 = message.indexOf('U', indexU1 + 1);
  int indexU3 = message.indexOf('U', indexU2 + 1);
  doc["humidity0to10"] = message.substring(indexV + 1, indexU1).toFloat();
  doc["humidity10to20"] = message.substring(indexU1 + 1, indexU2).toFloat();
  doc["humidity20to30"] = message.substring(indexU2 + 1, indexU3).toFloat();

  int indexT = message.indexOf('T');
  doc["temperature"] = message.substring(indexU3 + 1, indexT).toFloat();

  int indexC = message.indexOf('C');
  doc["stemSize"] = message.substring(indexT + 1, indexC).toFloat();

  int indexF = message.indexOf('F');
  doc["fruitSize"] = message.substring(indexC + 1, indexF).toFloat();

  doc["dateCreate"] = message.substring(indexF + 1);
}

void postApi(JsonDocument doc, String devId) {
  String json;
  JsonDocument data;

  int deviceId = doc["deviceId"];

  data["deviceId"] = deviceId;
  data["id"] = 0;
  data["devId"] = devId;
  data["humidity0to10"] = doc["humidity0to10"];
  data["humidity10to20"] = doc["humidity10to20"];
  data["humidity20to30"] = doc["humidity20to30"];
  data["fruitSize"] = doc["fruitSize"];
  data["latitude"] = "-9.363859429356404";
  data["longitude"] = "-40.53890312404104";
  data["dateCreate"] = doc["dateCreate"];
  data["temperature"] = doc["temperature"];
  data["voltage"] = doc["packetId"];
  data["rssi"] = dBmRssi;

  bool serialized = false;
  for (int i = 0; i < 5; ++i) {
    serializeJson(data, json);
    if (!json.isEmpty()) {
      serialized = true;
      break;
    }
    delay(100); 
  }

  if (!serialized) {
    Serial.println("Failed to serialize JSON.");
    return;
  }

  Serial.println(json);

  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    http.begin(postUrlApi);
    http.addHeader("Content-Type", "application/json");
    int responseCode = http.POST(json);

    if (responseCode != 200) {
      saveToSD(json, deviceId, doc["dateCreate"]);
    }
    Serial.println(responseCode);
    http.end();
  } else {
    saveToSD(json, deviceId, doc["dateCreate"]);
  }
}

void saveToSD(String json, int deviceId, String dateCreate) {
  String dirPath = "/devices/device_" + String(deviceId);
  
  int maxAttempts = 5;
  int attempts = 0;
  bool dirCreated = false;

  while (attempts < maxAttempts) {
    if (SD.exists(dirPath)) {
      Serial.println("Directory found: " + dirPath);
      dirCreated = true;
      break;
    } else {
      Serial.println("Directory not found. Trying to create: " + dirPath);
      if (SD.mkdir(dirPath)) {
        Serial.println("Directory created successfully: " + dirPath);
        dirCreated = true;
        break;
      } else {
        Serial.println("Failed to create directory: " + dirPath);
        attempts++;
        delay(100); 
      }
    }
  }

  if (!dirCreated) {
    Serial.println("Unable to create directory.");
    return;
  }

  String datePart = dateCreate.substring(0, 10);
  String timePart = dateCreate.substring(11, 19);  
  datePart.replace("-", "");
  timePart.replace(":", "");

  String fileName = "Json_" + datePart + "_" + timePart + ".txt";
  String filePath = dirPath + "/" + fileName;

  attempts = 0;
  bool fileCreated = false;

  while (attempts < maxAttempts) {
    File file = SD.open(filePath, FILE_WRITE);
    if (file) {
      file.println(json);
      file.close();
      Serial.println("File saved: " + filePath);
      fileCreated = true;
      break;
    } else {
      Serial.println("Error opening file: " + filePath);
      attempts++;
      delay(100); 
    }
  }

  if (!fileCreated) {
    Serial.println("Unable to create file.");
  }
}

void resendJson() {
  File root = SD.open("/devices");
  if (!root || !root.isDirectory()) {
    Serial.println("Error opening folder /devices");
    return;
  }

  File deviceFolder = root.openNextFile();
  while (deviceFolder) {
    if (deviceFolder.isDirectory()) {
      File jsonFile = deviceFolder.openNextFile();
      while (jsonFile) {
        if (!jsonFile.isDirectory()) {
          String filePath = String(deviceFolder.name()) + "/" + String(jsonFile.name());
          Serial.print("Sending file: ");
          Serial.println(filePath);

          String json;
          while (jsonFile.available()) {
            json += char(jsonFile.read());
          }
          jsonFile.close();

          HTTPClient http;
          http.begin(postUrlApi);
          http.addHeader("Content-Type", "application/json");
          int responseCode = http.POST(json);

          if (responseCode == 200 || responseCode == 400) {
            Serial.println("Response code " + String(responseCode) + ". Removing file.");
            if (SD.remove("/devices/" + filePath)) {
              Serial.println("File removed: " + filePath);
            } else {
              Serial.println("Failed to remove file: " + filePath);
            }
          } else {
              Serial.println("Error sending to API. Response code: " + String(responseCode));
          }
          http.end();
          delay(1000); 
        }
        jsonFile = deviceFolder.openNextFile();
      }
    }
    deviceFolder = root.openNextFile();
  }
  root.close();
}

bool connectedToInternet() {
  WiFiClient client;
  if (!client.connect("www.google.com", 80)) {
    return false;
  }
  client.stop();
  return true;
}

String generateId(int loraDeviceId, int sectionId, int farmId) {
  String hash1 = generateHash(loraDeviceId);
  String hash2 = generateHash(sectionId);
  String hash3 = generateHash(farmId);

  return hash1 + "-" + hash2 + "-" + hash3;
}

String generateHash(int id) {
  String input = String(id) + hashingKey;

  unsigned long hash = 0;
  for (int i = 0; i < input.length(); i++) {
    hash = 31 * hash + input.charAt(i);
  }

  String hashedId = String(hash, HEX);

  while (hashedId.length() < 8) {
    hashedId = "0" + hashedId;
  }

  return hashedId;
}

bool resendInterval(int hour, int minute) {
  for (int i = 0; i < NUM_INTERVALS; ++i) {
    if ((hour > intervals[i].startHour || (hour == intervals[i].startHour && minute >= intervals[i].startMinute)) &&
        (hour < intervals[i].endHour || (hour == intervals[i].endHour && minute < intervals[i].endMinute))) {
      return true; 
    }
  }
  return false; 
}

char* unconstchar(const char* s) {
  if (!s)
    return NULL;
  int i;
  char* res = NULL;
  res = (char*)malloc(strlen(s) + 1);
  if (!res) {
    fprintf(stderr, "Memory Allocation Failed! Exiting...\n");
    exit(EXIT_FAILURE);
  } else {
    for (i = 0; s[i] != '\0'; i++) {
      res[i] = s[i];
    }
    res[i] = '\0';
    return res;
  }
}

void setParameters() {
  ResponseStructContainer c;
  c = e220ttl.getConfiguration();
  Configuration configuration = *(Configuration*)c.data;

  configuration.ADDH = 0x00;
  configuration.ADDL = 0x04;
  configuration.CHAN = 0x41;

  configuration.SPED.uartBaudRate = UART_BPS_9600;
  configuration.SPED.airDataRate = AIR_DATA_RATE_010_24;
  configuration.SPED.uartParity = MODE_00_8N1;

  configuration.OPTION.subPacketSetting = SPS_200_00;
  configuration.OPTION.RSSIAmbientNoise = RSSI_AMBIENT_NOISE_DISABLED;
  configuration.OPTION.transmissionPower = POWER_30;

  configuration.TRANSMISSION_MODE.enableRSSI = RSSI_ENABLED;
  configuration.TRANSMISSION_MODE.fixedTransmission = FT_FIXED_TRANSMISSION;
  configuration.TRANSMISSION_MODE.enableLBT = LBT_DISABLED;
  configuration.TRANSMISSION_MODE.WORPeriod = WOR_2000_011;

  e220ttl.setConfiguration(configuration, WRITE_CFG_PWR_DWN_SAVE);
  printParameters(configuration);
  c.close();
}

  void printParameters(struct Configuration configuration) {
  Serial.println("----------------------------------------");
  Serial.print(F("HEAD : "));  Serial.print(configuration.COMMAND, HEX);Serial.print(" ");Serial.print(configuration.STARTING_ADDRESS, HEX);Serial.print(" ");Serial.println(configuration.LENGHT, HEX);
  Serial.println(F(" "));
  Serial.print(F("AddH : "));  Serial.println(configuration.ADDH, HEX);
  Serial.print(F("AddL : "));  Serial.println(configuration.ADDL, HEX);
  Serial.println(F(" "));
  Serial.print(F("Chan : "));  Serial.print(configuration.CHAN, DEC); Serial.print(" -> "); Serial.println(configuration.getChannelDescription());
  Serial.println(F(" "));
  Serial.print(F("SpeedParityBit     : "));  Serial.print(configuration.SPED.uartParity, BIN);Serial.print(" -> "); Serial.println(configuration.SPED.getUARTParityDescription());
  Serial.print(F("SpeedUARTDatte     : "));  Serial.print(configuration.SPED.uartBaudRate, BIN);Serial.print(" -> "); Serial.println(configuration.SPED.getUARTBaudRateDescription());
  Serial.print(F("SpeedAirDataRate   : "));  Serial.print(configuration.SPED.airDataRate, BIN);Serial.print(" -> "); Serial.println(configuration.SPED.getAirDataRateDescription());
  Serial.println(F(" "));
  Serial.print(F("OptionSubPacketSett: "));  Serial.print(configuration.OPTION.subPacketSetting, BIN);Serial.print(" -> "); Serial.println(configuration.OPTION.getSubPacketSetting());
  Serial.print(F("OptionTranPower    : "));  Serial.print(configuration.OPTION.transmissionPower, BIN);Serial.print(" -> "); Serial.println(configuration.OPTION.getTransmissionPowerDescription());
  Serial.print(F("OptionRSSIAmbientNo: "));  Serial.print(configuration.OPTION.RSSIAmbientNoise, BIN);Serial.print(" -> "); Serial.println(configuration.OPTION.getRSSIAmbientNoiseEnable());
  Serial.println(F(" "));
  Serial.print(F("TransModeWORPeriod : "));  Serial.print(configuration.TRANSMISSION_MODE.WORPeriod, BIN);Serial.print(" -> "); Serial.println(configuration.TRANSMISSION_MODE.getWORPeriodByParamsDescription());
  Serial.print(F("TransModeEnableLBT : "));  Serial.print(configuration.TRANSMISSION_MODE.enableLBT, BIN);Serial.print(" -> "); Serial.println(configuration.TRANSMISSION_MODE.getLBTEnableByteDescription());
  Serial.print(F("TransModeEnableRSSI: "));  Serial.print(configuration.TRANSMISSION_MODE.enableRSSI, BIN);Serial.print(" -> "); Serial.println(configuration.TRANSMISSION_MODE.getRSSIEnableByteDescription());
  Serial.print(F("TransModeFixedTrans: "));  Serial.print(configuration.TRANSMISSION_MODE.fixedTransmission, BIN);Serial.print(" -> "); Serial.println(configuration.TRANSMISSION_MODE.getFixedTransmissionDescription());
  Serial.println("----------------------------------------");
}

Now I can't establish any communication between the transmitter and the receiver. This has been happening since that reboot at 6 AM shown in the image. I used the ESP Exception Decoder and it returned this:

Decoding stack results
0x400d71c3: H at C:\Users\Automa��o\Documents\Arduino\libraries\CustomJWT\src\hmac_sha256.c line 82
0x400d72d2: custom_jwt_hmac_sha256 at C:\Users\Automa��o\Documents\Arduino\libraries\CustomJWT\src\hmac_sha256.c line 64
0x400d2c22: CustomJWT::decodeJWT(char*) at C:\Users\Automa��o\Documents\Arduino\libraries\CustomJWT\src/CustomJWT.h line 211
0x400d664d: loop() at C:\Users\Automação\Documents\Arduino\Receptor_NewSD_STOPWORK/Receptor_NewSD_STOPWORK.ino line 105
0x400e1031: loopTask(void*) at C:\Users\Automa��o\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.11\cores\esp32\main.cpp line 50

Does anyone know how to solve this?

I dont find the ESP32 SD card drivers reliable when there is another device on the same SPI bus.

If you want to mix SD cards and LoRa I would run each on a seperate SPI instance, which is easy enough on an ESP32.

1 Like

@srnet Hello friend! How can I do this?

I added an SD card to an ESP32 T-Beam that had an SPI LoRa device fitted so wanting the SD card on a seperate SPI bus I found some spare pins and wrote this test code, you will need to change the pins used by the SD card to your setup;

/*******************************************************************************************************
  Program Operation - This test program has been written to check that a connected SD card adapter, Micro
  or standard, is functional. There is also a test of a LoRa module on a seperate SPI bus. Program is for
  LilyGo T-Beam with an SD card attached to some unused pins.

  The program creates a file called LOGXXXX.TXT, where XXXX is a number that increases every time the
  program is restarted. The program opens the file and writes a line like this to the file;

  #1 Hello World!

  The file is closed and the file contents are written to the IDE serial monitor and a directory of the
  SD card printed too. The process repeats with #2 Hello World! being appended to the file next. The
  directory listing allows you to keep track of the increasing size of the logFile. Problems with reading
  or writing to the SD card should result in an ERROR message on the IDE serial monitor.


  Serial monitor baud rate is set at 115200
*******************************************************************************************************/

#define SDSCK 25
#define SDMOSI 14
#define SDMISO 13
#define SDCS 33

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

SPIClass sdSPI(HSPI);

char filename[] = "/LOG0000.TXT";               //filename used as base for creating root, 0000 replaced with numbers
uint16_t linenumber = 0;

File logFile;


void loop()
{
  linenumber++;

  Serial.print("Write to file > ");
  Serial.print("#");
  Serial.print(linenumber);
  Serial.println(" Hello World!");

  logFile = SD.open(filename, FILE_APPEND);
  logFile.print("#");
  logFile.print(linenumber);
  logFile.println(" Hello World!");
  logFile.close();
  dumpFile(filename);
  Serial.println();

  logFile = SD.open("/");
  logFile.rewindDirectory();
  printDirectory(logFile, 0);

  Serial.println();
  delay(1500);
}


void printDirectory(File dir, int numTabs)
{
  Serial.println("Card directory");

  while (true)
  {
    File entry =  dir.openNextFile();
    if (! entry)
    {
      // no more files
      break;
    }

    for (uint8_t i = 0; i < numTabs; i++)
    {
      Serial.print('\t');
    }
    Serial.print(entry.name());
    if (entry.isDirectory())
    {
      Serial.println("/");
      printDirectory(entry, numTabs + 1);
    }
    else
    {
      // files have sizes, directories do not
      Serial.print("\t\t");
      Serial.println(entry.size(), DEC);
    }
    entry.close();
  }
}


bool dumpFile(char *buff)
{
  Serial.print("Print file ");

  if (SD.exists(buff))
  {
    Serial.println(buff);
  }
  else
  {
    Serial.print("ERROR ");
    Serial.print(buff);
    Serial.println(" not found - program halted");
    while (1);
    //return false;
  }

  logFile = SD.open(buff);
  logFile.seek(0);

  if (logFile)                                    //if the file is available, read from it
  {
    while (logFile.available())
    {
      Serial.write(logFile.read());
    }
    logFile.close();
    return true;
  }
  else
  {
    Serial.print("ERROR ");
    Serial.println(" dumping file ");
    Serial.print(buff);
    Serial.println(" - program halted");
    while (1);
    //return false;
  }
}


uint8_t setupSDLOG(char *buff)
{
  //creats a new filename

  uint16_t index;

  File dir;

  for (index = 1; index <= 9999; index++)
  {
    buff[4] = index / 1000 + '0';
    buff[5] = ((index % 1000) / 100) + '0';
    buff[6] = ((index % 100) / 10) + '0';
    buff[7] = index % 10 + '0' ;

    if (! SD.exists(filename))
    {
      // only open a new file if it doesn't exist
      dir = SD.open(buff, FILE_WRITE);
      break;
    }
  }

  dir.rewindDirectory();                          //stops SD.exists() command causing issues with directory listings
  dir.close();

  if (!dir)
  {
    return 0;
  }
  return index;                                   //return number of root created
}


void setup()
{
  Serial.begin(115200);
  Serial.println();
  Serial.print(("13_SD_Card_Test_SPI starting"));
  Serial.println();

  sdSPI.begin(SDSCK, SDMISO, SDMOSI, SDCS);

  if (!SD.begin(SDCS, sdSPI))
  {
    Serial.println();
    Serial.println("ERROR Card Mount Failed - program halted");
    Serial.println();
    while (1);
  }

  Serial.println("Card Mount OK");

  logFile = SD.open("/");
  setupSDLOG(filename);
}
1 Like

have a look at post connecting-sd-card-and-bmp280-via-spi-interface where SD card uses VSPI and a MFRC522 RFID reader uses HSPI

what do you have connected to SPI? the Ebyte E220-900T30D Lora modules uses a serial interface

1 Like