ESP32 + SX1276 + LoRa + SSD1306 OLED Display

Good Morning Everyone,

Project Objective:
Bare with me as this is my first post here, but I've been stumped on a problem with my code for a few days now and have resorted to finding help online. The overall objective of this project is to have a (TX) transmit incoming data from the NEO-6M GPS Module over to the (RX) using the SX1276 LoRa Module to allow the user to view the readings on an SSD1306 OLED Display. The description of component usage for the TX and RX are listed below:

(TX):

  • ESP32 Dev Module
  • SX1276 LoRa Module
  • NEO-6M GPS Module
  • SSD1306 OLED Display (Optional/ Just to See the Readings for Now)

(RX):

  • ESP32 Dev Module
  • SX1276 LoRa Module
  • SSD1306 OLED Display

Project Issue
When the code (listed below) is uploaded to the board, everything works out perfectly. The (TX) is receiving incoming data from the NEO-6M GPS and the SSD1306 OLED Display is giving off the proper readings for the viewer to see. However; if you notice, the LoRa portion of the code that's listed near the bottom of the code is grayed out for a reason. Every time I activate this portion of code into the overall code and upload it to the board, I get a few problems. The altitude, number of satellites, and HDOP readings all get bumped down to zero as if they're not receiving any data. However the latitude, longitude, and speed are all reading correctly. I want to ensure that the incoming data will be retrieved correctly before sending it over to the (RX). If anyone has any helpful tips to fix this issue, that'd be great. Thank you for your time today and have a great day!

//Libraries for LoRa
#include <SPI.h>
#include <LoRa.h>

//Libraries for Adafruit SSD1306 OLED Display
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

//Libraries for GPS
#include <TinyGPSPlus.h>
#include <SoftwareSerial.h>

//Define the Pins Used by the LoRa Transceiver Module
#define SCK 18
#define MISO 19
#define MOSI 23
#define SS 5
#define RST 14
#define DIO0 2

//Define Radio Frequency Used in North America
#define BAND 915E6

//OLED Pins
#define OLED_SDA 21
#define OLED_SCL 22

//Define Adafruit SSD1306 OLED Display Size
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

//Define RX and TX Pinout on NEO6M GPS Module
static const int RXPin = 17;
static const int TXPin = 16;

static const uint32_t GPSBaud = 9600;

//Establish NEO6M GPS Module
TinyGPSPlus gps;
SoftwareSerial gpsSerial(RXPin, TXPin);

//Establish Adafruit SSD1306 OLED Display
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

//Setup
void setup() {
  //Initialize Serial Monitor
  Serial.begin(115200);
  //Initialize GPS Serial Monitor
  gpsSerial.begin(GPSBaud);
  //Change Sync Word (0xF3) to Match the Receiver/The Sync Word assures you don't get LoRa Messages from Other LoRa Transceivers/Ranges from 0-0xFF
  LoRa.setSyncWord(0xF3);
  //SPI LoRa Pins
  SPI.begin(SCK, MISO, MOSI, SS);
  //Setup LoRa Transceiver Module
  LoRa.setPins(SS, RST, DIO0);
  //Activates Serial Connection w/ Hardware
  while (!Serial);

  //Reset OLED Display via Software
  pinMode(-1, OUTPUT);
  digitalWrite(-1, LOW);
  delay(20);
  digitalWrite(-1, HIGH);

  //Initialize OLED
  Wire.begin(OLED_SDA, OLED_SCL);
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C, false, false)) { // Address 0x3c for 128x32
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't Proceed, Loop Forever
  }
  
  //Display "LoRa Transmitter" for Booting Process
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(0,0);
  display.print("LoRa Transmitter");
  display.display();
  
  //If "LoRa Connection Failed" Display "LoRa Connection Failed"
  if (!LoRa.begin(BAND)) {
    Serial.println("LoRa Connection Failed");
    while (1);
  }
  //If "LoRa Connection Succeeded" Display "LoRa Connection Succeeded"
  Serial.println("LoRa Initializing OK!");
  display.setCursor(0,10);
  display.print("LoRa Initializing OK!");
  display.display();
  display.clearDisplay();
  delay(2000);
}

//Loop
void loop() {
  while (gpsSerial.available() > 0){
    gps.encode(gpsSerial.read());
    if (gps.location.isUpdated()){
      //Display Readings into Serial Monitor
      //Latitude
      Serial.print("Latitude: "); 
      Serial.print(gps.location.lat(), 6);
      //Longitude
      Serial.print(" Longitude: "); 
      Serial.print(gps.location.lng(), 6);
      //Altitude
      Serial.print(" Altitude: ");
      Serial.print(gps.altitude.meters(), 2);
      //Speed
      Serial.print(" Speed: ");
      Serial.print(gps.speed.mps(), 2);
      //Satellites
      Serial.print(" Satellites: ");
      Serial.print(gps.satellites.value());
      //HDOP
      Serial.print(" HDOP: ");
      Serial.print(gps.hdop.hdop(), 2);
      //Serial.print(" Date: ");
      //Serial.print(gps.date.month());
      //Serial.print("/");
      //Serial.print(gps.date.day());
      //Serial.print("/");
      //Serial.print(gps.date.year());
      //Intiate Next Line for Information
      Serial.println();

      //Display Readings into SSD1306 OLED Display
      display.setCursor(0,10);
      display.print("LAT:");
      display.println(gps.location.lat(), 6);
      display.print("LONG:");
      display.println(gps.location.lng(), 6);
      display.print("ALT:");
      display.println(gps.altitude.meters(), 2);
      display.print("SPEED:");
      display.println(gps.speed.mps(), 2);
      display.print("SATS:");
      display.println(gps.satellites.value());
      display.print("HDOP:");
      display.println(gps.hdop.hdop(), 2);
      //display.print("DATE:");  //Optional
      //display.println(gps.date.month());  //Optional
      //display.println("/");  //Optional
      //display.println(gps.date.day());  //Optional
      //display.println("/");  //Optional
      //display.println(gps.date.year());  //Optional
      display.display();
      display.clearDisplay();

      //LoRa.beginPacket();
      //LoRa.print("LAT:");
      //LoRa.println(gps.location.lat(), 6);
      //LoRa.print("LONG:");
      //LoRa.println(gps.location.lng(), 6);
      //LoRa.print("ALT:");
      //LoRa.println(gps.altitude.meters(), 2);
      //LoRa.print("SPEED:");
      //LoRa.println(gps.speed.mps(), 2);
      //LoRa.print("SATS:");
      //LoRa.println(gps.satellites.value());
      //LoRa.print("HDOP:");
      //LoRa.println(gps.hdop.hdop(), 2);
      //LoRa.endPacket();

      //delay(5000);
    }
  }
}

Here are pictures of the before and after from uploading the code. The latitude and longitude readings are blacked out for obvious reasons.

Before (Code w/ the LoRa Part Grayed Out):


After (Code w/ LoRa Part):

Software serial can cause problems when your using interfaces such as SPI for the LoRa device. Hardware serial ports are much more reliable.

Might not be you problem, but there is no reason at all I can see to risk using Software serial when the ESP32 has a spare hardware serial port.

I'll definitely have to do more research about this, but do you know what HardwareSerial enables a user to do? What's the benefit to it and should it resolve this error I'm facing? Thanks for your help!

SoftwareSerial was implemented to enable serial IO on microcontrollers which did not have hardware serial ports
if a device has hardware serial ports use them - SoftwareSerial can cause problems
e.g. using hardware port Serial2 on ESP32 pins 16 and 17

// ESP32 serial2 hardware loop back test - jumper GPIO16 (Rx) and GPIO17 (Tx)

// see https://circuits4you.com/2018/12/31/esp32-hardware-serial2-example/
/* There are three serial ports on the ESP known as U0UXD, U1UXD and U2UXD.
 * 
 * U0UXD is used to communicate with the ESP32 for programming and during reset/boot.
 * U1UXD is unused and can be used for your projects. Some boards use this port for SPI Flash access though
 * U2UXD is unused and can be used for your projects.
*/

#define RXD2 16
#define TXD2 17

void setup() {
  // Note the format for setting a serial port is as follows: Serial2.begin(baud-rate, protocol, RX pin, TX pin);
  Serial.begin(115200);
  Serial2.begin(115200, SERIAL_8N1, RXD2, TXD2);
  Serial.println("ESP32 hardware serial test on Serial2");
  Serial.println("Serial2 Txd is on pin: "+String(TXD2));
  Serial.println("Serial2 Rxd is on pin: "+String(RXD2));
}

void loop() { //Choose Serial1 or Serial2 as required
  while (Serial2.available()) {
    Serial.print(char(Serial2.read()));
  }
  while (Serial.available()) {
    Serial2.print(char(Serial.read()));
  }
}

jumper pins 16 and 17 and text entered on the serial monitor keyboard are echoed back to the display

also have a look at TTGO ESP32 LoRa - ESP32 + LoRa + OLED display all on one PCB - saves all the interconnecting wires which can give poor connections and intermittent problems

How would you go about in fixing this code? I hate to ask, but I'm stumped at the moment. Thanks for your help in trying to solve this issue!

for a start use hardware serial ports
e.g. remove the statements

#include <SoftwareSerial>
......
SoftwareSerial gpsSerial(RXPin, TXPin);

change the Serial2 pins definition to and rewire your PCB to suit

#define RXD2 16
#define TXD2 17

do a globaal find for gpsSerial replacing it with Serial2

recompile and run - does it now work as expected?
if not report what it should be doing and what it is dong with associated serial monitor output (as text not screen image - add extra Serial.printf statements at key points to check program flow and variable values

I followed the advice you gave on switching to HardwareSerial along with fixing the commands for the ESP32 and it worked! The final test was to enable the LoRa.print commands near the bottom of the code and still be able to view the right readings without them reading "0.00" consistently on the (TX) SSD1306 OLED Display. I proceeded to test the (RX) and the data is being transmitted through successfully. Thank you so much for the advice as I really appreciate it!

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