#include <Arduino.h>
#include <Wire.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// --- Network & ThingsBoard Credentials ---
const char* WIFI_SSID = "YOUR_WIFI_SSID";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";
const char* TB_SERVER = "your thingsboard server";
const int TB_PORT = 1883;
const char* TB_ACCESS_TOKEN = "YOUR_THINGSBOARD_TOKEN";
// --- Hardware Pins ---
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
#define OLED_SDA_PIN 8
#define OLED_SCL_PIN 9
#define BUTTON_PIN 6
#define LORA_RX_PIN 18
#define LORA_TX_PIN 17
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
WiFiClient espClient;
PubSubClient mqttClient(espClient);
bool lastBtnState = HIGH;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50;
// Telemetry Variables
String tempVal = "--.-";
String humVal = "--.-";
String latVal = "0.000000";
String lngVal = "0.000000";
String satsVal = "0";
String fixVal = "SEARCHING";
void sendLoraLedToggle() {
String msg = "LED_TOGGLE";
String cmd = "AT+SEND=1," + String(msg.length()) + "," + msg;
Serial2.print(cmd + "\r\n");
Serial.println(">>> Transmitted LED_TOGGLE to Node 1 via LoRa!");
}
// MQTT RPC Callback - Handles Commands from ThingsBoard
void mqttCallback(char* topic, byte* payload, unsigned int length) {
String message;
for (unsigned int i = 0; i < length; i++) {
message += (char)payload[i];
}
Serial.print("RPC Received on [");
Serial.print(topic);
Serial.print("]: ");
Serial.println(message);
// Trigger LED Toggle
if (message.indexOf("toggleLed") != -1 || message.indexOf("setValue") != -1) {
sendLoraLedToggle();
}
// Send RPC Response Back to Cloud
String topicStr = String(topic);
int requestIdIndex = topicStr.lastIndexOf('/');
if (requestIdIndex != -1) {
String requestId = topicStr.substring(requestIdIndex + 1);
String responseTopic = "v1/devices/me/rpc/response/" + requestId;
String responsePayload = "{\"status\":\"success\"}";
mqttClient.publish(responseTopic.c_str(), responsePayload.c_str());
Serial.println(">>> Replied to RPC Request ID: " + requestId);
}
}
void connectWiFi() {
Serial.print("Connecting to Wi-Fi");
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWi-Fi Connected! IP: " + WiFi.localIP().toString());
}
void connectMQTT() {
while (!mqttClient.connected()) {
Serial.print("Connecting to ThingsBoard MQTT...");
if (mqttClient.connect("ESP32S3_Gateway", TB_ACCESS_TOKEN, NULL)) {
Serial.println(" Connected!");
mqttClient.subscribe("v1/devices/me/rpc/request/+");
} else {
Serial.print(" Failed, rc=");
Serial.print(mqttClient.state());
Serial.println(" Retrying in 3 seconds...");
delay(3000);
}
}
}
void sendToThingsBoard() {
if (!mqttClient.connected()) {
connectMQTT();
}
String jsonPayload = "{";
jsonPayload += "\"temperature\":" + tempVal + ",";
jsonPayload += "\"humidity\":" + humVal + ",";
jsonPayload += "\"latitude\":" + latVal + ",";
jsonPayload += "\"longitude\":" + lngVal + ",";
jsonPayload += "\"satellites\":" + satsVal + ",";
jsonPayload += "\"gps_fix\":\"" + fixVal + "\"";
jsonPayload += "}";
mqttClient.publish("v1/devices/me/telemetry", jsonPayload.c_str());
Serial.print(">>> Published to ThingsBoard: ");
Serial.println(jsonPayload);
}
void updateDisplay() {
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(12, 0);
display.print("LORA TRACKER NODE");
display.drawFastHLine(0, 10, 128, SSD1306_WHITE);
display.setCursor(0, 15);
display.print("T: "); display.print(tempVal);
display.print("C H: "); display.print(humVal); display.print("%");
display.drawFastHLine(0, 26, 128, SSD1306_WHITE);
display.setCursor(0, 31);
display.print("Lat: "); display.print(latVal);
display.setCursor(0, 42);
display.print("Lng: "); display.print(lngVal);
display.setCursor(0, 54);
display.print("Sats: "); display.print(satsVal);
display.print(" | "); display.print(fixVal);
display.display();
}
void setup() {
Serial.begin(115200);
delay(1500);
Wire.begin(OLED_SDA_PIN, OLED_SCL_PIN);
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println("OLED allocation failed!");
}
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(LORA_RX_PIN, INPUT_PULLUP);
Serial2.begin(115200, SERIAL_8N1, LORA_RX_PIN, LORA_TX_PIN);
Serial2.print("AT+RESET\r\n"); delay(500);
Serial2.print("AT+ADDRESS=2\r\n"); delay(100);
Serial2.print("AT+NETWORKID=6\r\n"); delay(100);
Serial2.print("AT+PARAMETER=9,7,1,4\r\n"); delay(100);
connectWiFi();
mqttClient.setServer(TB_SERVER, TB_PORT);
mqttClient.setCallback(mqttCallback);
connectMQTT();
updateDisplay();
Serial.println("--- Display Gateway Ready ---\n");
}
void loop() {
if (WiFi.status() != WL_CONNECTED) {
connectWiFi();
}
if (!mqttClient.connected()) {
connectMQTT();
}
mqttClient.loop();
// 1. Process Incoming LoRa Telemetry
if (Serial2.available()) {
String rxData = Serial2.readStringUntil('\n');
rxData.trim();
if (rxData.startsWith("+RCV=")) {
int c1 = rxData.indexOf(',');
int c2 = rxData.indexOf(',', c1 + 1);
int cLast = rxData.lastIndexOf(',');
int cPrev = rxData.lastIndexOf(',', cLast - 1);
if (c2 != -1 && cPrev != -1 && c2 < cPrev) {
String payload = rxData.substring(c2 + 1, cPrev);
payload.trim();
int pIdx[5];
pIdx[0] = payload.indexOf(',');
pIdx[1] = payload.indexOf(',', pIdx[0] + 1);
pIdx[2] = payload.indexOf(',', pIdx[1] + 1);
pIdx[3] = payload.indexOf(',', pIdx[2] + 1);
pIdx[4] = payload.indexOf(',', pIdx[3] + 1);
if (pIdx[4] != -1) {
tempVal = payload.substring(0, pIdx[0]);
humVal = payload.substring(pIdx[0] + 1, pIdx[1]);
latVal = payload.substring(pIdx[1] + 1, pIdx[2]);
lngVal = payload.substring(pIdx[2] + 1, pIdx[3]);
satsVal = payload.substring(pIdx[3] + 1, pIdx[4]);
fixVal = payload.substring(pIdx[4] + 1);
updateDisplay();
sendToThingsBoard();
}
}
}
}
// 2. Hardware Button Handler
bool reading = digitalRead(BUTTON_PIN);
if (reading != lastBtnState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
static bool btnState = HIGH;
if (reading != btnState) {
btnState = reading;
if (btnState == LOW) {
Serial.println(">>> Physical Button Pressed!");
sendLoraLedToggle();
}
}
}
lastBtnState = reading;
}
Comments