How to get SD card on TTGO LoRa32 working

hi there, im just trying start simple but i cant figure how add and sd card logger, i use sd,mmc and somtimes writes some times not, i using ttgo lora esp32 paxcuounter i use standar librarys but i not find how start thanks for advice

Why would you want a SD card loger? If the purposes of LoRaWAN is to send your data to your backend / database and then to your applications?

Not clear to us, which ā€˜standardā€™ libraries you are using however.

So can you say exactly which libraries you are using ?

1 Like

Assuming that you are using the board for LoRaWAN, it is not clear if you are using the LMIC-node software or something else (e.g. Paxcounter).

Anything else than LMIC-node (e.g. Paxcounter) is off-topic here (LMIC-node topic).
The LMIC-node topic is for discussions about the bare LMIC-node example program in general. Your question is about SD card support for a specific board. I will therefore move your post to a separate topic [done].

ā€˜TTGO ESP32-Paxcounter LoRa32ā€™ is a confusing marketing name used by LilyGO for the normal ā€˜TTGO LoRa32ā€™ board (but comes preloaded with Paxcounter firmware).
From your post it is not clear which exact version of the board you have. The version is essential because different versions of those boards use (very) different hardware configurations. See Big ESP32 + SX127x topic part 3

1 Like

Hi there, i finnally make working SD logger with ttn, i want save data be cause we are goin install the sensors and the loragateway in my country Chile near Atacama desert we are not sure about gsm conectivity gona work we dont have lan conection. be cause of this we beed backup data.
the default SD and test sd_mmc library too are the arduino ones for esp32, i use arduno ide and mcci lorawan otta demo file, i ask here be cause it supouse the started point
the working SD library GitHub - nhatuan84/esp32-micro-sdcard and the pinuout are for lora esp32 V2 1.6 paxcunter and lora esp32 v2 standard

  pinout especial del paxcounter tiene monitoreo de bateria y usa otros pines para conectarse

  TTGO LoRa32 V2.1.6:
  ESP32          LoRa (SPI)  SDCARD(SPI)    Display (I2C)  LED           BAT
  -----------    ----------  -----------    -------------  ------------------
  GPIO5  SCK     SCK
  GPIO27 MOSI    MOSI
  GPIO19 MISO    MISO
  GPIO18 SS      NSS
  GPIO23         RST
  GPIO26         DIO0
  GPIO33         DIO1
  GPIO32         DIO2
  GPIO21 SDA                     SDA
  GPIO22 SCL                     SCL
  GPIO25                                        LED
  GPIO35                                                 LECTURA 100K BAT      
  GPIO14                 SD_CLK     
  GPIO2                  SD_MISO    
  GPIO15                 SD_MOSI    
  GPIO13                 SD_CS
  GPIO12                 DAT2
  GPIO4                  DAT1

i can share the whole code, but are very crude and is still WIP

Well in one part of your post you mention the SD_MMC library and then point to a library that uses the SPI SD library, and the pinout diagram also suggests the SPI SD library.

In general I have not found the SPI SD library on ESP32 very reliable, especially when sharing the same SPI bus as the LoRa module. The reliability can vary between different cards too. Why that is the case is really down to Expressif who produce the libraries.

The MMC library seems to be a lot more stable, uses one less IO pin too. For the unused pins on MMC, CS, DAT1 and DAT2 use pullup resistors.

The same issues with SD cards arrises on other microcontroller platforms too, SDs can be a bit un-reliable.

1 Like

IIRC on some TTGO LoRa32 version(s) only SPI is supported on the onboard SD card slot.
Itā€™s been a few years back that I observed this though, I donā€™t remember the exact version but I think it was in one of the v2.x versions, or possibly even the T-Beam board.

1 Like

Depends how the wired the SD card, the MMC pins are fixed and cannot be re-directed.

However the SPI pins can be re-directed, so if the SD card was connected to the fixed MMC pins, 2, 14,15, then you would have the choice of MMC or SPI.

1 Like

Exactly, ā€œonly SPIā€ means MMC interface was not wired on the SD card slot.

1 Like

@arebolledo posted a pinout that I thought was for the "
TTGO LoRa32 V2.1.6:", it has that title.

On that list of pins the SPI pinouts for the SD appear to support MMC.

1 Like

Yes, the examples work alone but the ota and sd mmc together didnt work anyway i can make work but im sure its not the most efficient its works anyway Code

//  SPI port #2:  SD Card Adapter
#define  SD_CLK     14
#define  SD_MISO    02
#define  SD_MOSI    15
#define  SD_CS      13
#define  LoRa_CS    18
#define  Select    LOW   //  Low CS means that SPI device Selected
#define  DeSelect  HIGH  //  High CS means that SPI device Deselected

//#include "FS.h"
#include <mySD.h>
#include <SPI.h>
#include <lmic.h>
#include <hal/hal.h>
//#include <SPI.h>

#include <U8x8lib.h>

#define DISABLE_PING 0
#define DISABLE_BEACONS 0

#include <Wire.h>
#include <Adafruit_INA219.h>

Adafruit_INA219 ina219;

const int numReadings  = 20;
int indexD             = 0; // El indice de la lectura actual
float readings[numReadings]; // Lecturas de la entrada analogica
float total           = 0.0; // Total
float average         = 0.0; // Promedio
float voltageLevel;
float current_mA = 0;
 String mensaje ;
static const u1_t PROGMEM APPEUI[8] = {0x79, 0x88, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x79}; // este se invierte
void os_getArtEui(u1_t * buf) {
  memcpy_P(buf, APPEUI, 8);
}

static const u1_t PROGMEM DEVEUI[8] = {0x79, 0xFE, 0x04, 0xD0, 0x7E, 0xD5, 0xB3, 0x70}; // 0x70,0xB3,0xD5,0x7E,0xD0,0x04,0xFE,0x79 //
void os_getDevEui(u1_t * buf) {
  memcpy_P(buf, DEVEUI, 8);
}                                           //2F27DF318F19674A6EF7C81ACF435A88
                                                    //2F 27 DF 31 8F 19 67 4A 6E F7 C8 1A CF 43 5A 88
static const u1_t PROGMEM APPKEY[16] = {0x2F, 0x27, 0xDF, 0x31, 0x8F, 0x19, 0x67, 0x4A, 0x6E, 0xF7, 0xC8, 0x1A, 0xCF, 0x43, 0x5A, 0x88}; //0x67,0xA3,0x60,0x70,0x93,0x65,0xC7,0xE8,0x84,0x7C,0x50,0x3D,0xD8,0xE3,0xC8,0xFF
void os_getDevKey(u1_t * buf) {
  memcpy_P(buf, APPKEY, 16);
}

RTC_DATA_ATTR char filenameCSV[25]=""; //para construir el archivo csv base cada vez que se prenda y guarde

File root;
File myFile;
int led = 25;
//For TTGO LoRa32 V2.x use:
U8X8_SSD1306_128X64_NONAME_HW_I2C display_(/*rst*/ U8X8_PIN_NONE);
//static uint8_t mydata[] = "Hello, from ESP32";
//uint8_t mydata[20]="esp32Test";
// char mydata[20]="esp32Test";
static uint8_t mydata[51];
static int counter;

static osjob_t sendjob;
int runs = 0;
const unsigned TX_INTERVAL = 30;
// For TTGO LoRa32 V2.1.6
const lmic_pinmap lmic_pins = {
  .nss = LoRa_CS,
  .rxtx = LMIC_UNUSED_PIN,
  .rst = 23,
  .dio = {/*dio0*/ 26, /*dio1*/ 33, /*dio2*/ 32}
};


void printHex2(unsigned v) {
  v &= 0xff;
  if (v < 16)
    Serial.print('0');
  Serial.print(v, HEX);
}

void onEvent(ev_t ev) {
  Serial.print(os_getTime());
  Serial.print(": ");
  switch (ev) {
    case EV_JOINING:
      Serial.println(F("EV_JOINING"));
      break;

    case EV_JOINED:
      Serial.println(F("EV_JOINED")); {
        u4_t netid = 0;
        devaddr_t devaddr = 0;
        u1_t nwkKey[16];
        u1_t artKey[16];
        LMIC_getSessionKeys( & netid, & devaddr, nwkKey, artKey);
        Serial.print("netid: ");
        Serial.println(netid, DEC);
        Serial.print("devaddr: ");
        Serial.println(devaddr, HEX);
        Serial.print("AppSKey: ");
        for (size_t i = 0; i < sizeof(artKey); ++i) {
          if (i != 0)
            Serial.print(" ");
          printHex2(artKey[i]);
        }
        Serial.println("");
        Serial.print("NwkSKey: ");
        for (size_t i = 0; i < sizeof(nwkKey); ++i) {
          if (i != 0)
            Serial.print(" ");
          printHex2(nwkKey[i]);
        }
        Serial.println();
        display_.drawString(0, 5, "Network Joined");


        
      }


      initSD();
      // Disable link check validation (automatically enabled
      // during join, but because slow data rates change max TX
      // size, we don't use it in this example.
      LMIC_setLinkCheckMode(0);
      break;
    case EV_JOIN_FAILED:
      Serial.println(F("EV_JOIN_FAILED"));
      break;
    case EV_REJOIN_FAILED:
      Serial.println(F("EV_REJOIN_FAILED"));
      break;
      break;
    case EV_TXCOMPLETE:
      Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
      display_.drawString(0, 3, "Data Sent");
       WriteReadSD();
      if (LMIC.txrxFlags & TXRX_ACK)
        Serial.println(F("Received ack"));
      if (LMIC.dataLen) {
        Serial.print(F("Received "));
        Serial.print(LMIC.dataLen);
        Serial.println(F(" bytes of payload"));

        display_.drawString(0, 4, "Data received");
        Serial.println(F("Data is "));

        // Change the following codes to process incoming data !!
        for (int counter = 0; counter < LMIC.dataLen; counter++) {
          Serial.print(LMIC.frame[LMIC.dataBeg + counter], HEX);
        }
        Serial.println(F(" "));

      }
      else
        display_.drawString(0, 4, "                   ");
      // Schedule next transmission
      os_setTimedCallback( & sendjob, os_getTime() + sec2osticks(TX_INTERVAL), do_send);
      break;
    case EV_LOST_TSYNC:
      Serial.println(F("EV_LOST_TSYNC"));
      break;
    case EV_RESET:
      Serial.println(F("EV_RESET"));
      break;
    case EV_RXCOMPLETE:
      // data received in ping slot
      Serial.println(F("EV_RXCOMPLETE"));
      break;
    case EV_LINK_DEAD:
      Serial.println(F("EV_LINK_DEAD"));
      break;
    case EV_LINK_ALIVE:
      Serial.println(F("EV_LINK_ALIVE"));
      break;
    case EV_TXSTART:
      Serial.println(F("EV_TXSTART"));
      break;
    case EV_TXCANCELED:
      Serial.println(F("EV_TXCANCELED"));
      break;
    case EV_JOIN_TXCOMPLETE:
      Serial.println(F("EV_JOIN_TXCOMPLETE: no JoinAccept"));
      break;

    default:
      Serial.print(F("Unknown event: "));
      Serial.println((unsigned) ev);
      break;
  }
}

void do_send(osjob_t * j) {
  display_.clear();
  display_.drawString(0, 0, "TTGO ESP32 LoRa Test");
  // Check if there is not a current TX/RX job running
  if (LMIC.opmode & OP_TXRXPEND) {
    Serial.println(F("OP_TXRXPEND, not sending"));
  } else {

    ++counter;
    mydata[0] = 0; //este es el mensaje completo
    mensaje = "2V";
    mensaje = mensaje + String(voltageLevel) + " numero envio: " + String(counter) + " corriente: " + String(average) + "mA  '\0'";
    Serial.println(mensaje);
    int str_lenM = mensaje.length() + 1;  // esta funcion es para asignarle el valor de casillas al array
    char menC[str_lenM]; //este es el char array
    mensaje.toCharArray(menC, str_lenM);
    Serial.println(menC);
    strcat ((char*)mydata, menC); //copio el mensaje   ā€˜\0ā€™ average

    // Prepare upstream data transmission at the next possible time.
    //LMIC_setTxData2(1, mydata, sizeof(mydata) - 1, 0);
    LMIC_setTxData2(1, mydata, sizeof(mydata) + 1, 0);
    Serial.println(F("Packet queued"));
    display_.drawString(0, 2, "Packet Queued");
    digitalWrite(led, HIGH);
    delay(1000);

  }
  // Next TX is scheduled after TX_COMPLETE event.
  digitalWrite(led, LOW);
  delay(500);
}
int uno = 1;
void setup() {
  Serial.begin(115200);
  pinMode(led, OUTPUT);

  uint32_t currentFrequency;

  Serial.println("Test esp32 RM95");
 
  // Initialize the INA219.
  // By default the initialization will use the largest range (32V, 2A).  However
  // you can call a setCalibration function to change this range (see comments).
  if (! ina219.begin()) {
    Serial.println("Failed to find INA219 chip");
    while (1) {
      delay(10);
    }
  }
  //  lower 16V, 400mA range (higher precision on volts and amps):
  ina219.setCalibration_16V_400mA();

  Serial.println("Measuring voltage and current with INA219 ...");
  Serial.println(F("Starting"));
  display_.begin();
  display_.setFont(u8x8_font_pxplusibmcgathin_r);
  display_.drawString(0, 0, "TTGO ESP32 LoRa Test");
  Serial.println(F("Module Configured for CHILE BAND (AU915 MHz)"));


  // LMIC init
  os_init();
  // Reset the MAC state. Session and pending data transfers will be discarded.
  LMIC_reset();
  LMIC_setClockError(MAX_CLOCK_ERROR * 5 / 100);
  //LMIC_setClockError(MAX_CLOCK_ERROR * 10 / 100);
  // Reset the MAC state. Session and pending data transfers will be discarded.
  // LMIC_reset();

  LMIC_setLinkCheckMode(0);
  LMIC_setAdrMode(false);
  LMIC_setDrTxpow(DR_SF7, 14);
  LMIC_enableSubBand(1);
  LMIC_selectSubBand(1);


  do_send( & sendjob);
 // set output pins
  pinMode(SD_CS, OUTPUT);
  pinMode(LoRa_CS, OUTPUT);

 
}

void loop() {
  // os_runloop_once();
  float shuntvoltage = 0;
  float busvoltage = 0;

  float loadvoltage = 0;
  float power_mW = 0;
  int batR = analogRead(35);
  voltageLevel = (batR / 4095.0) * 2 * 1.1 * 3.3; // calculate voltage level
  shuntvoltage = ina219.getShuntVoltage_mV();
  busvoltage = ina219.getBusVoltage_V();
  current_mA = ina219.getCurrent_mA();
  power_mW = ina219.getPower_mW();
  loadvoltage = busvoltage + (shuntvoltage / 1000);

  // Restamos la ultima lectura:
  total = total - readings[indexD];
  // Leemos del sensor:
  readings[indexD] = current_mA;
  // AƱadimos la lectura al total:
  total = total + readings[indexD];
  // Avanzamos a la proxima posicion del array
  indexD = indexD + 1;
  // Si estamos en el final del array...
  if (indexD >= numReadings)
    indexD = 0;   // ...volvemos al inicio:

  // Calculamos el promedio:
  average = total / numReadings;
  // Lo mandamos a la PC como un valor ASCII
  //
  //  Serial.print("Bus Voltage:   "); Serial.print(busvoltage); Serial.println(" V");
  //  Serial.print("Shunt Voltage: "); Serial.print(shuntvoltage); Serial.println(" mV");
  //  Serial.print("Load Voltage:  "); Serial.print(loadvoltage); Serial.println(" V");
  //  Serial.print("Current:       "); Serial.print(current_mA); Serial.println(" mA");
  //  Serial.print("Power:         "); Serial.print(power_mW); Serial.println(" mW");
  //  Serial.println("");
  // display_.drawString(0, 4, "Current:");
  display_.setCursor(0, 4);
  display_.print(String(average) + " mA " + String(loadvoltage) + " V");
  display_.setCursor(0, 6);
  display_.print("Bat: " + String(voltageLevel) + " V");

  os_runloop_once();


}
void WriteReadSD(){
  
   static uint32_t debs;
 if (millis() - debs >= 3000){ 
  debs = millis(); 
  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.

  digitalWrite(LoRa_CS, DeSelect);
  digitalWrite(SD_CS, Select);    //  SELECT (Low) SD Card SPI
  myFile = SD.open(filenameCSV, FILE_WRITE);
  
  // if the file opened okay, write to it:
  if (myFile) {
    Serial.print("Writing to "+String(filenameCSV));
    myFile.println(mensaje);
  // close the file:
    myFile.close();
    Serial.println("done.");
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening "+String(filenameCSV));
  }
  
  // re-open the file for reading:
  myFile = SD.open(filenameCSV);
  if (myFile) {
    Serial.println(filenameCSV);
    
    // read from the file until there's nothing else in it:
    while (myFile.available()) {
      Serial.write(myFile.read());
    }
    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening "+String(filenameCSV));
  }
  digitalWrite(SD_CS, DeSelect);
  delay( 1000 );
 digitalWrite(LoRa_CS, Select);   //  SELECT (low) LoRa SPI 
 }
  
  
  }
void initSD(){
  Serial.print("Initializing SD card...");
  digitalWrite(LoRa_CS, DeSelect);
  digitalWrite(SD_CS, Select);    //  SELECT (Low) SD Card SPI
  delay(100);
/**/
  Serial.print("Initializing SD card...");
 pinMode(SD_CS, OUTPUT);    //  SELECT (Low) SD Card SPI
/**/
  if (!SD.begin( SD_CS, SD_MOSI, SD_MISO, SD_CLK )) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
int n = 0;
  snprintf(filenameCSV, sizeof(filenameCSV), "data%03d.csv", n); // includes a three-digit sequence number in the file name
  while(SD.exists(filenameCSV)) {
    n++;
    snprintf(filenameCSV, sizeof(filenameCSV), "data%03d.csv", n);
  }
  File dataFile = SD.open(filenameCSV,FILE_READ);
  Serial.println(n);
  Serial.println(filenameCSV);
  dataFile.close();

  root = SD.open("/");
  
  printDirectory(root, 0);
  
  Serial.println(" done!");

  digitalWrite(SD_CS, DeSelect);
  delay( 1000 );
 digitalWrite(LoRa_CS, Select);   //  SELECT (low) LoRa SPI 
  
  
  
  }

void printDirectory(File dir, int numTabs) {
  // Begin at the start of the directory
  dir.rewindDirectory();
  
  while(true) {
     File entry =  dir.openNextFile();
     if (! entry) {
       // no more files
       //Serial.println("**nomorefiles**");
       break;
     }
     for (uint8_t i=0; i<numTabs; i++) {
       Serial.print('\t');   // we'll have a nice indentation
     }
     // Print the 8.3 name
     Serial.print(entry.name());
     // Recurse for directories, otherwise print the file size
     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();
   }
}

If anyone think can be more officient iĀ“m glad to hear coments

1 Like

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