Big ESP32 + SX127x topic part 3

Well you can re-map the pins and it does work.

However, which LoRa driver are you using ?

The first diagram with the SD card mapped to its native interface was posted by yourself, bluejedi, “TTGO LoRa32 V2.1 rev1.6 versus TTGO LoRa32 V2.0 pin layouts”, back in October 2019. And very useful it is too. It corrects TTGO’s mistake in mapping IO0 and IO4 to power and ground for starters! Thanks.

Good catch.

The TTGO LoRa32 V2.1.6 pinout has now been corrected, enhanced and the file has been renamed.

Maybe this helps:

Below microSD connection details come from the TTGO LoRa32 V2.1.6 schematic diagram:

TTGO V2.1.6 microSD schematic diagram

A post was split to a new topic: Programming ESP32 + SX1276 in Rust

Hello,

I have an issue with my TTGO SD LoRa 32 V2.0
I would like to use SD card and LMIC in the same sketch, but theese 2 libraries do not work together in my sketch.
Are there some samples using SD card and LoRaWAN together ?

NB :

  • Sketch using SD card alone works
  • Shetch using LMIC alone works well with TTN

I have not used LMIC on the ESP32, but in what way does the SD card and LoRa device ‘not work together’ ?

IIRC some modules in the market can ‘share’ pinouts & functions e.g. GPIO, SPI bus(es) etc. Is this one where say a SD Card connection conflicts with the LoRa radio function? :thinking: perhaps SPI i/f?

Update, just scrolled up and saw discussion earlier this year on just such connections - perhaps @bluejedi can advise


Step away from the soldering iron 


SPI can share the In, Out & Clock but each item connected needs it’s own Select. Arduino broke it all by assuming only one SPI device when they made pin 10 as Select. So almost all libraries use that assumption. Sometimes you can override the SS / NSS / CS pin in the library - you can in LMIC but the Select will be hardwired on the PCB. Whereas the Select for the SD Card can be moved to a different pin which would then need specifying in the code - either when initialised or by hacking the library, depending on what library you are using.

The general advice (I don’t have a source) is to not share the SPI bus where the LoRa radio is connected to with other peripherals, when using (Arduino) LMIC. This is because LMIC uses its own proprietary scheduler and timing and was not designed with sharing the SPI bus in mind.

Fortunately the ESP32 supports two different SPI busses. To use the second SPI bus you will have to create a second SPI object instance and map specific ESP32 pins for the second SPI.
In addition you will need to use libraries that can handle more than only the standard default SPI instance (allow you to specify a specific SPI object instance for your SDCard).

Above is a description for the approach, but you will have to search for the nitty gritty details for how to exactly do this in code yourself (I don’t have examples at hand).

For SD cards there afe different interfaces / modes you can use: One-bit SD mode, four-bit SD mode or SPI. Check the ESP32 documentation/datasheet for which interface requires/allows which IO ports.

Be aware that it depends on the type of ESP32 board which GPIO ports are (not) available as physical pins on the board. It is also possible that some board type already uses some of the required GPIO’s for other purposes.
Some boards are better (or worse) designed in this aspect than others so your mileage may vary.

I found when doing the file\image transfer stuff for point to point LoRa that you could share an SPI bus with LoRa and SDs on say an ATmega328P, ATmega1284P or an Arduino DUE, that was OK.

However, I would certainly concur that its not a good idea to have the SD card and LoRa device sharing the same SPI bus on an ESP32, its just not reliable, the SD card just fails to respond after a while. And for some reason an SX128X LoRa device was worse in this respect compared to an SX127X.

So for an ESP32 keep the SD card on a seperate SPI bus or use it in 1 bit MMC mode, which has the advantge of using one less IO pin than an SPI setup.

I have modified my sketch, and created another instance of SPIClass with SPI pins that are connected to SD reader.

I works well !

Thank you very much ! :slight_smile:

/*******************************************************************************
 * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
 *
 * Permission is hereby granted, free of charge, to anyone
 * obtaining a copy of this document and accompanying files,
 * to do whatever they want with them without any restriction,
 * including, but not limited to, copying, modification and redistribution.
 * NO WARRANTY OF ANY KIND IS PROVIDED.
 *
 * This example sends a valid LoRaWAN packet with payload "Hello,
 * world!", using frequency and encryption settings matching those of
 * the The Things Network.
 *
 * This uses OTAA (Over-the-air activation), where where a DevEUI and
 * application key is configured, which are used in an over-the-air
 * activation procedure where a DevAddr and session keys are
 * assigned/generated for use with all further communication.
 *
 * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in
 * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably
 * violated by this sketch when left running for longer)!

 * To use this sketch, first register your application and device with
 * the things network, to set or generate an AppEUI, DevEUI and AppKey.
 * Multiple devices can use the same AppEUI, but each device has its own
 * DevEUI and AppKey.
 *
 * Do not forget to define the radio type correctly in config.h.
 *
 *******************************************************************************/
//bq
#include "SPI.h"
#include "SD.h"
//qb
#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>

// This EUI must be in little-endian format, so least-significant-byte
// first. When copying an EUI from ttnctl output, this means to reverse
// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,
// 0x70.
static const u1_t PROGMEM APPEUI[8]={ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

void os_getArtEui(u1_t* buf) {
  memcpy_P(buf, APPEUI, 8);
}

// This should also be in little endian format, see above.
static const u1_t PROGMEM DEVEUI[8]={ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

void os_getDevEui(u1_t* buf) {
  memcpy_P(buf, DEVEUI, 8);
}

// This key should be in big endian format (or, since it is not really a
// number but a block of memory, endianness does not really apply). In
// practice, a key taken from ttnctl can be copied as-is.
// The key shown here is the semtech default key.
static const u1_t PROGMEM APPKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C };

void os_getDevKey(u1_t* buf) {
  memcpy_P(buf, APPKEY, 16);
}

static uint8_t mydata[] = "Hello, world!";

static osjob_t sendjob;

// Schedule TX every this many seconds (might become longer due to duty
// cycle limitations).
const unsigned TX_INTERVAL = 60;

/*bq
// Pin mapping
const lmic_pinmap lmic_pins = {
    .nss = 6,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 5,
    .dio = {2, 3, 4},
};
*/
// constantes definies dans C:\Users\adm\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.4\variants\ttgo-lora32-v1\pins_arduino.h
// si pas bonne valeur => 1 seul envoi
const lmic_pinmap lmic_pins = {
  .nss = LORA_CS,
  .rxtx = LMIC_UNUSED_PIN,
  .rst = LORA_RST,
  //    .dio = {26, 26, LMIC_UNUSED_PIN}, // 1 seul envoi
  .dio = { /*dio0*/ 26, /*dio1*/ 33, /*dio2*/ 32 }
};

SPIClass spiSD(HSPI);  // à faire en global pour que ce ne soit pas détruit en sortant du setuo

//qb
void onEvent(ev_t ev) {
  Serial.print(os_getTime());
  Serial.print(": ");
  switch (ev) {
    case EV_SCAN_TIMEOUT:
      Serial.println(F("EV_SCAN_TIMEOUT"));
      break;
    case EV_BEACON_FOUND:
      Serial.println(F("EV_BEACON_FOUND"));
      break;
    case EV_BEACON_MISSED:
      Serial.println(F("EV_BEACON_MISSED"));
      break;
    case EV_BEACON_TRACKED:
      Serial.println(F("EV_BEACON_TRACKED"));
      break;
    case EV_JOINING:
      Serial.println(F("EV_JOINING"));
      break;
    case EV_JOINED:
      Serial.println(F("EV_JOINED"));
      LMIC_setLinkCheckMode(0);
      break;
    case EV_RFU1:
      Serial.println(F("EV_RFU1"));
      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)"));
      if (LMIC.txrxFlags & TXRX_ACK)
        Serial.println(F("Received ack"));
      if (LMIC.dataLen) {
        Serial.println(F("Received "));
        Serial.println(LMIC.dataLen);
        Serial.println(F(" bytes of payload"));
        //bq
        File file = SD.open("/fic.txt", FILE_APPEND);
        if (!file)
          Serial.println("Failed to open file for appending");  // failed systématique
        else {
          for (int i = 0; i < LMIC.dataLen; i++) {
            file.print(LMIC.frame[LMIC.dataBeg + i], HEX);
          }
          file.println();
          file.close();
        }
        //qb
      }
      // 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;
    default:
      Serial.println(F("Unknown event"));
      break;
  }
}

void do_send(osjob_t* j) {
  // Check if there is not a current TX/RX job running
  if (LMIC.opmode & OP_TXRXPEND) {
    Serial.println(F("OP_TXRXPEND, not sending"));
  } else {
    // Prepare upstream data transmission at the next possible time.
    LMIC_setTxData2(1, mydata, sizeof(mydata) - 1, 0);
    Serial.println(F("Packet queued"));
  }
  // Next TX is scheduled after TX_COMPLETE event.
}

void setup() {
  Serial.begin(115200);
  Serial.println(F("Starting"));
  //bq
  spiSD.begin(SD_SCK, SD_MISO, SD_MOSI, SD_CS);
  if (!SD.begin(SD_CS, spiSD)) {
    Serial.println("Card Mount Failed");
    return;
  }
  //qb

#ifdef VCC_ENABLE
  // For Pinoccio Scout boards
  pinMode(VCC_ENABLE, OUTPUT);
  digitalWrite(VCC_ENABLE, HIGH);
  delay(1000);
#endif

  // LMIC init
  os_init();
  // Reset the MAC state. Session and pending data transfers will be discarded.
  LMIC_reset();

  // Start job (sending automatically starts OTAA too)
  do_send(&sendjob);
}

void loop() {
  os_runloop_once();
}
2 Likes