Compare commits
No commits in common. "34154d716d57da87010c60cd57d5489b18feed1f" and "e305e5c4c1add94b8b928090967572eb9c028fb8" have entirely different histories.
34154d716d
...
e305e5c4c1
|
|
@ -15,11 +15,10 @@ framework = arduino
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
build_flags =
|
build_flags =
|
||||||
-D DEBUG
|
-D DEBUG
|
||||||
-D MOCK_LORA
|
|
||||||
lib_deps =
|
lib_deps =
|
||||||
https://github.com/tzapu/WiFiManager.git
|
https://github.com/tzapu/WiFiManager.git
|
||||||
bblanchon/ArduinoJson@^7.0.4
|
bblanchon/ArduinoJson@^7.0.4
|
||||||
peterus/esp-logger@^1.0.0
|
peterus/esp-logger@^1.0.0
|
||||||
knolleary/PubSubClient@^2.8
|
knolleary/PubSubClient@^2.8
|
||||||
jgromes/RadioLib@^6.0.0
|
sandeepmistry/LoRa@^0.8.0
|
||||||
thingpulse/ESP8266 and ESP32 OLED driver for SSD1306 displays@^4.5.0
|
thingpulse/ESP8266 and ESP32 OLED driver for SSD1306 displays@^4.5.0
|
||||||
|
|
|
||||||
|
|
@ -1,152 +1,54 @@
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include "lorahandler.hpp"
|
#include "lorahandler.hpp"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include <ArduinoJson.h>
|
|
||||||
|
|
||||||
extern logging::Logger logger;
|
extern logging::Logger logger;
|
||||||
extern Config config;
|
extern Config config;
|
||||||
|
|
||||||
LoraHandler::LoraHandler() {
|
|
||||||
// Initialize with SX1278 radio
|
|
||||||
}
|
|
||||||
|
|
||||||
void LoraHandler::setup()
|
void LoraHandler::setup()
|
||||||
{
|
{
|
||||||
#ifdef MOCK_LORA
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "LoRa", "Running in MOCK MODE - simulating LoRa reception!");
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "LoRa", "Mock data will be generated every 10 calls");
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "LoRa", "Initializing SX1278 with RadioLib!");
|
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "LoRa", "Set SPI pins!");
|
||||||
|
SPI.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS);
|
||||||
|
LoRa.setPins(LORA_CS, LORA_RST, LORA_IRQ);
|
||||||
|
|
||||||
// Create new SPI instance for LoRa
|
long freq = config.loraConfig.frequency;
|
||||||
spi = new SPIClass(VSPI);
|
if (!LoRa.begin(freq))
|
||||||
spi->begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS);
|
|
||||||
|
|
||||||
// Create SX1278 instance
|
|
||||||
radio = new SX1278(new SPIDriver(spi, LORA_CS));
|
|
||||||
|
|
||||||
// Configure RXEN/TXEN pins
|
|
||||||
pinMode(LORA_RXEN, OUTPUT);
|
|
||||||
pinMode(LORA_TXEN, OUTPUT);
|
|
||||||
digitalWrite(LORA_RXEN, LOW);
|
|
||||||
digitalWrite(LORA_TXEN, LOW);
|
|
||||||
|
|
||||||
// Initialize LoRa module
|
|
||||||
int state = radio->begin();
|
|
||||||
if (state != RADIOLIB_ERR_NONE)
|
|
||||||
{
|
{
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "LoRa", "LoRa init failed: %i", state);
|
logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "LoRa", "Starting LoRa failed!");
|
||||||
while (true) {
|
// show_display("ERROR", "Starting LoRa failed!");
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
delay(1000);
|
delay(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
LoRa.setSpreadingFactor(config.loraConfig.spreadingFactor);
|
||||||
// Configure LoRa parameters
|
LoRa.setSignalBandwidth(config.loraConfig.signalBandwidth);
|
||||||
long freq = config.loraConfig.frequency;
|
LoRa.setCodingRate4(config.loraConfig.codingRate4);
|
||||||
state = radio->setFrequency(freq);
|
LoRa.enableCrc();
|
||||||
if (state != RADIOLIB_ERR_NONE)
|
LoRa.setTxPower(config.loraConfig.power);
|
||||||
{
|
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "LoRa", "LoRa init done!");
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "LoRa", "setFrequency failed: %i", state);
|
String currentLoRainfo = "LoRa Freq: " + String(config.loraConfig.frequency) + " / SF:" + String(config.loraConfig.spreadingFactor) + " / CR: " + String(config.loraConfig.codingRate4);
|
||||||
}
|
|
||||||
|
|
||||||
state = radio->setSpreadingFactor(config.loraConfig.spreadingFactor);
|
|
||||||
if (state != RADIOLIB_ERR_NONE)
|
|
||||||
{
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "LoRa", "setSpreadingFactor failed: %i", state);
|
|
||||||
}
|
|
||||||
|
|
||||||
state = radio->setBandwidth(config.loraConfig.signalBandwidth);
|
|
||||||
if (state != RADIOLIB_ERR_NONE)
|
|
||||||
{
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "LoRa", "setBandwidth failed: %i", state);
|
|
||||||
}
|
|
||||||
|
|
||||||
state = radio->setCodingRate(config.loraConfig.codingRate4);
|
|
||||||
if (state != RADIOLIB_ERR_NONE)
|
|
||||||
{
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "LoRa", "setCodingRate failed: %i", state);
|
|
||||||
}
|
|
||||||
|
|
||||||
state = radio->setOutputPower(config.loraConfig.power);
|
|
||||||
if (state != RADIOLIB_ERR_NONE)
|
|
||||||
{
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "LoRa", "setOutputPower failed: %i", state);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable CRC
|
|
||||||
radio->setCRC(true);
|
|
||||||
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "LoRa", "SX1278 initialized successfully!");
|
|
||||||
String currentLoRainfo = "LoRa Freq: " + String(freq) + " / SF:" + String(config.loraConfig.spreadingFactor) + " / CR: " + String(config.loraConfig.codingRate4);
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "LoRa", currentLoRainfo.c_str());
|
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "LoRa", currentLoRainfo.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
ReceivedLoRaPacket LoraHandler::receivePacket()
|
ReceivedLoRaPacket LoraHandler::receivePacket()
|
||||||
{
|
{
|
||||||
ReceivedLoRaPacket receivedLoraPacket;
|
ReceivedLoRaPacket receivedLoraPacket;
|
||||||
|
String packet = "";
|
||||||
#ifdef MOCK_LORA
|
int packetSize = LoRa.parsePacket();
|
||||||
// Mock mode - simulate LoRa reception every 10 calls
|
if (packetSize)
|
||||||
mockCounter++;
|
|
||||||
if (mockCounter >= 10)
|
|
||||||
{
|
{
|
||||||
mockCounter = 0;
|
while (LoRa.available())
|
||||||
|
{
|
||||||
// Create mock sensor data from sender
|
int inChar = LoRa.read();
|
||||||
float temp = 22.5 + (random(0, 50) / 100.0); // 22.5 - 23.0°C
|
packet += (char)inChar;
|
||||||
int battery = 75 + random(0, 20); // 75-95%
|
}
|
||||||
int boot = 5;
|
receivedLoraPacket.text = packet;
|
||||||
int update = 1;
|
receivedLoraPacket.rssi = LoRa.packetRssi();
|
||||||
|
receivedLoraPacket.snr = LoRa.packetSnr();
|
||||||
// Build JSON payload like sender would send
|
receivedLoraPacket.freqError = LoRa.packetFrequencyError();
|
||||||
JsonDocument doc;
|
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "LoRa Rx", "---> %s", packet.c_str());
|
||||||
doc["temp"] = temp;
|
|
||||||
doc["battery"] = battery;
|
|
||||||
doc["boot"] = boot;
|
|
||||||
doc["update"] = update;
|
|
||||||
|
|
||||||
serializeJson(doc, receivedLoraPacket.text);
|
|
||||||
|
|
||||||
// Simulate signal parameters
|
|
||||||
receivedLoraPacket.rssi = -85 + random(0, 30); // -85 to -55 dBm
|
|
||||||
receivedLoraPacket.snr = 5.0 + (random(0, 100) / 100.0); // 5.0 to 6.0 dB
|
|
||||||
receivedLoraPacket.freqError = random(-1000, 1000);
|
|
||||||
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "LoRa Rx [MOCK]", "---> %s (RSSI: %d, SNR: %.1f)",
|
|
||||||
receivedLoraPacket.text.c_str(), receivedLoraPacket.rssi, receivedLoraPacket.snr);
|
|
||||||
}
|
}
|
||||||
return receivedLoraPacket;
|
return receivedLoraPacket;
|
||||||
#else
|
|
||||||
// Real hardware mode
|
|
||||||
// Enable RX mode
|
|
||||||
digitalWrite(LORA_RXEN, HIGH);
|
|
||||||
digitalWrite(LORA_TXEN, LOW);
|
|
||||||
|
|
||||||
int state = radio->receive(0); // 0 = non-blocking
|
|
||||||
|
|
||||||
if (state == RADIOLIB_ERR_RX_ONGOING)
|
|
||||||
{
|
|
||||||
// No packet received yet
|
|
||||||
return receivedLoraPacket;
|
|
||||||
}
|
|
||||||
else if (state == RADIOLIB_ERR_NONE)
|
|
||||||
{
|
|
||||||
// Packet received!
|
|
||||||
receivedLoraPacket.text = radio->readData();
|
|
||||||
receivedLoraPacket.rssi = radio->getRSSI();
|
|
||||||
receivedLoraPacket.snr = radio->getSNR();
|
|
||||||
receivedLoraPacket.freqError = radio->getFrequencyError();
|
|
||||||
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "LoRa Rx", "---> %s (RSSI: %d, SNR: %.1f)",
|
|
||||||
receivedLoraPacket.text.c_str(), receivedLoraPacket.rssi, receivedLoraPacket.snr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "LoRa Rx", "Receive error: %i", state);
|
|
||||||
}
|
|
||||||
|
|
||||||
return receivedLoraPacket;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
#define LORAHANDLER_H
|
#define LORAHANDLER_H
|
||||||
|
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include <RadioLib.h>
|
#include <SPI.h>
|
||||||
|
#include <LoRa.h>
|
||||||
|
|
||||||
#define LORA_SCK 5
|
#define LORA_SCK 5
|
||||||
#define LORA_MISO 19
|
#define LORA_MISO 19
|
||||||
|
|
@ -10,8 +11,6 @@
|
||||||
#define LORA_CS 18 // CS --> NSS
|
#define LORA_CS 18 // CS --> NSS
|
||||||
#define LORA_RST 14
|
#define LORA_RST 14
|
||||||
#define LORA_IRQ 26 // IRQ --> DIO0
|
#define LORA_IRQ 26 // IRQ --> DIO0
|
||||||
#define LORA_RXEN 2 // RX Enable
|
|
||||||
#define LORA_TXEN 13 // TX Enable
|
|
||||||
|
|
||||||
struct ReceivedLoRaPacket {
|
struct ReceivedLoRaPacket {
|
||||||
String text;
|
String text;
|
||||||
|
|
@ -22,13 +21,9 @@ struct ReceivedLoRaPacket {
|
||||||
|
|
||||||
class LoraHandler {
|
class LoraHandler {
|
||||||
public:
|
public:
|
||||||
LoraHandler();
|
|
||||||
void setup();
|
void setup();
|
||||||
ReceivedLoRaPacket receivePacket();
|
ReceivedLoRaPacket receivePacket();
|
||||||
private:
|
private:
|
||||||
SX1278* radio;
|
|
||||||
SPIClass* spi;
|
|
||||||
int mockCounter = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* LORAHANDLER_H */
|
#endif /* LORAHANDLER_H */
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
#include "lorahandler.hpp"
|
#include "lorahandler.hpp"
|
||||||
#include "display.hpp"
|
#include "display.hpp"
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <ArduinoJson.h>
|
|
||||||
|
|
||||||
Config config;
|
Config config;
|
||||||
WiFiServer server(80);
|
WiFiServer server(80);
|
||||||
|
|
@ -52,36 +51,10 @@ void setup()
|
||||||
void loop()
|
void loop()
|
||||||
{
|
{
|
||||||
ReceivedLoRaPacket packet = lora.receivePacket();
|
ReceivedLoRaPacket packet = lora.receivePacket();
|
||||||
|
if (packet.text.isEmpty())
|
||||||
// Process received LoRa packet
|
|
||||||
if (!packet.text.isEmpty())
|
|
||||||
{
|
{
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MAIN", "Processing LoRa packet: %s", packet.text.c_str());
|
packet.rssi = 69;
|
||||||
|
packet.snr = 3.3;
|
||||||
// Try to parse JSON payload from sender
|
|
||||||
JsonDocument doc;
|
|
||||||
DeserializationError error = deserializeJson(doc, packet.text);
|
|
||||||
|
|
||||||
if (!error) {
|
|
||||||
float temp = doc["temp"] | 0.0;
|
|
||||||
int battery = doc["battery"] | 0;
|
|
||||||
int boot = doc["boot"] | 0;
|
|
||||||
int update = doc["update"] | 0;
|
|
||||||
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MAIN", "Parsed - Temp: %.2f, Battery: %d, Boot: %d, Update: %d", temp, battery, boot, update);
|
|
||||||
|
|
||||||
// Publish to MQTT with Home Assistant format
|
|
||||||
if (WiFi.status() == WL_CONNECTED) {
|
|
||||||
mqtt.publishSensorData(temp, packet.rssi, battery);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_WARN, "MAIN", "Failed to parse JSON: %s", error.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
packet.rssi = 0;
|
|
||||||
packet.snr = 0.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WiFi.status() == WL_CONNECTED) { // Check WiFi connection
|
if (WiFi.status() == WL_CONNECTED) { // Check WiFi connection
|
||||||
|
|
@ -92,22 +65,26 @@ void loop()
|
||||||
// Optional: Display message indicating WiFi not connected
|
// Optional: Display message indicating WiFi not connected
|
||||||
display.show_message("WiFi not connected", 0);
|
display.show_message("WiFi not connected", 0);
|
||||||
}
|
}
|
||||||
|
int mqtt_pkt = 123455;
|
||||||
String line1 = "LoRa RSSI: " + String(packet.rssi) + " dBm";
|
String line1 = "Mqtt pkt: " + String(mqtt_pkt);
|
||||||
display.show_message(line1.c_str(), 1);
|
display.show_message(line1.c_str(), 1);
|
||||||
|
|
||||||
String line2 = "SNR: " + String(packet.snr, 1) + " dB";
|
String line2 = "Last lora, rssi: " + String(packet.rssi ) ;
|
||||||
display.show_message(line2.c_str(), 2);
|
display.show_message(line2.c_str(), 2);
|
||||||
|
display.show_message("Fourth", 3);
|
||||||
display.show_message("Data received OK", 3);
|
display.show_message("Fifth", 4);
|
||||||
|
|
||||||
// Keep MQTT connection alive
|
|
||||||
mqtt.mqttRun();
|
|
||||||
|
|
||||||
sleep(5);
|
sleep(5);
|
||||||
|
/*
|
||||||
|
mqtt.mqttRun();
|
||||||
|
if (portalRunning)
|
||||||
|
{
|
||||||
|
wm.process();
|
||||||
|
}
|
||||||
|
mqtt.mqttPublish("test/sensor", "123");
|
||||||
|
checkButton();
|
||||||
|
sleep(10);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void checkButton()
|
void checkButton()
|
||||||
{
|
{
|
||||||
// check for button press
|
// check for button press
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,13 @@
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include <PubSubClient.h>
|
#include <PubSubClient.h>
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include <ArduinoJson.h>
|
|
||||||
|
|
||||||
extern logging::Logger logger;
|
extern logging::Logger logger;
|
||||||
|
|
||||||
|
extern Config config;
|
||||||
extern Config config;
|
extern Config config;
|
||||||
|
|
||||||
Mqtt::Mqtt(WiFiClient& wifiClient) : mqttClient(wifiClient) {
|
Mqtt::Mqtt(WiFiClient& wifiClient) : mqttClient(wifiClient) {}
|
||||||
deviceId = "water_temp_receiver";
|
|
||||||
baseTopic = "homeassistant/sensor/water_temp";
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mqtt::mqttSetup()
|
void Mqtt::mqttSetup()
|
||||||
{
|
{
|
||||||
|
|
@ -18,6 +16,7 @@ void Mqtt::mqttSetup()
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", message.c_str());
|
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", message.c_str());
|
||||||
if (config.mqttConfig.server.length() > 0)
|
if (config.mqttConfig.server.length() > 0)
|
||||||
{
|
{
|
||||||
|
// strcpy(mqttHost, config.mqtt.server.c_str());
|
||||||
String message = "MqttServer: " + config.mqttConfig.server + ", MqttPort: " + config.mqttConfig.port;
|
String message = "MqttServer: " + config.mqttConfig.server + ", MqttPort: " + config.mqttConfig.port;
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", message.c_str());
|
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", message.c_str());
|
||||||
IPAddress mqttServerIP;
|
IPAddress mqttServerIP;
|
||||||
|
|
@ -69,15 +68,12 @@ void Mqtt::mqttReconnect()
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT CONFIG", "Server: %s", config.mqttConfig.server.c_str());
|
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT CONFIG", "Server: %s", config.mqttConfig.server.c_str());
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT CONFIG", "Port: %d", config.mqttConfig.port);
|
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT CONFIG", "Port: %d", config.mqttConfig.port);
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT CONFIG", "Username: %s", config.mqttConfig.username.c_str());
|
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT CONFIG", "Username: %s", config.mqttConfig.username.c_str());
|
||||||
|
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT CONFIG", "Password: %s", config.mqttConfig.password.c_str());
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT CONFIG", "ID: %s", config.mqttConfig.id.c_str());
|
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT CONFIG", "ID: %s", config.mqttConfig.id.c_str());
|
||||||
|
|
||||||
if (mqttClient.connect(mqttId.c_str(), config.mqttConfig.username.c_str(), config.mqttConfig.password.c_str()))
|
if (mqttClient.connect(mqttId.c_str(), "simon", "bajsa123"))
|
||||||
{
|
{
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", "connected");
|
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", "connected");
|
||||||
|
|
||||||
// Setup Home Assistant Discovery on successful connection
|
|
||||||
setupHomeAssistantDiscovery();
|
|
||||||
|
|
||||||
String topic = config.mqttConfig.topic;
|
String topic = config.mqttConfig.topic;
|
||||||
if (topic.isEmpty())
|
if (topic.isEmpty())
|
||||||
{
|
{
|
||||||
|
|
@ -101,117 +97,6 @@ void Mqtt::mqttReconnect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mqtt::setupHomeAssistantDiscovery()
|
|
||||||
{
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", "Setting up Home Assistant MQTT Discovery");
|
|
||||||
|
|
||||||
// Temperature sensor discovery
|
|
||||||
{
|
|
||||||
JsonDocument doc;
|
|
||||||
doc["name"] = "Water Temperature";
|
|
||||||
doc["unique_id"] = "water_temp_temperature";
|
|
||||||
doc["unit_of_measurement"] = "°C";
|
|
||||||
doc["device_class"] = "temperature";
|
|
||||||
doc["state_topic"] = baseTopic + "/temperature";
|
|
||||||
doc["availability_topic"] = baseTopic + "/availability";
|
|
||||||
|
|
||||||
JsonObject device = doc["device"].to<JsonObject>();
|
|
||||||
device["identifiers"][0] = deviceId;
|
|
||||||
device["name"] = "Water Temperature Sensor";
|
|
||||||
device["model"] = "Water Temp v1";
|
|
||||||
device["manufacturer"] = "DIY";
|
|
||||||
|
|
||||||
String payload;
|
|
||||||
serializeJson(doc, payload);
|
|
||||||
String topic = baseTopic + "/temperature/config";
|
|
||||||
mqttPublish(topic.c_str(), payload.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// RSSI sensor discovery
|
|
||||||
{
|
|
||||||
JsonDocument doc;
|
|
||||||
doc["name"] = "LoRa RSSI";
|
|
||||||
doc["unique_id"] = "water_temp_rssi";
|
|
||||||
doc["unit_of_measurement"] = "dBm";
|
|
||||||
doc["device_class"] = "signal_strength";
|
|
||||||
doc["state_topic"] = baseTopic + "/rssi";
|
|
||||||
doc["availability_topic"] = baseTopic + "/availability";
|
|
||||||
|
|
||||||
JsonObject device = doc["device"].to<JsonObject>();
|
|
||||||
device["identifiers"][0] = deviceId;
|
|
||||||
|
|
||||||
String payload;
|
|
||||||
serializeJson(doc, payload);
|
|
||||||
String topic = baseTopic + "/rssi/config";
|
|
||||||
mqttPublish(topic.c_str(), payload.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Battery level discovery
|
|
||||||
{
|
|
||||||
JsonDocument doc;
|
|
||||||
doc["name"] = "Sender Battery";
|
|
||||||
doc["unique_id"] = "water_temp_battery";
|
|
||||||
doc["unit_of_measurement"] = "%";
|
|
||||||
doc["device_class"] = "battery";
|
|
||||||
doc["state_topic"] = baseTopic + "/battery";
|
|
||||||
doc["availability_topic"] = baseTopic + "/availability";
|
|
||||||
|
|
||||||
JsonObject device = doc["device"].to<JsonObject>();
|
|
||||||
device["identifiers"][0] = deviceId;
|
|
||||||
|
|
||||||
String payload;
|
|
||||||
serializeJson(doc, payload);
|
|
||||||
String topic = baseTopic + "/battery/config";
|
|
||||||
mqttPublish(topic.c_str(), payload.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set availability to online
|
|
||||||
String availTopic = baseTopic + "/availability";
|
|
||||||
mqttPublish(availTopic.c_str(), "online");
|
|
||||||
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", "Home Assistant Discovery setup complete");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mqtt::publishTemperature(float temp)
|
|
||||||
{
|
|
||||||
String topic = baseTopic + "/temperature";
|
|
||||||
String payload = String(temp, 2);
|
|
||||||
mqttPublish(topic.c_str(), payload.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mqtt::publishRSSI(int rssi)
|
|
||||||
{
|
|
||||||
String topic = baseTopic + "/rssi";
|
|
||||||
String payload = String(rssi);
|
|
||||||
mqttPublish(topic.c_str(), payload.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mqtt::publishSensorData(float temp, int rssi, int battery)
|
|
||||||
{
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", "Publishing sensor data - Temp: %.2f, RSSI: %d, Battery: %d", temp, rssi, battery);
|
|
||||||
|
|
||||||
publishTemperature(temp);
|
|
||||||
publishRSSI(rssi);
|
|
||||||
|
|
||||||
String topic = baseTopic + "/battery";
|
|
||||||
String payload = String(battery);
|
|
||||||
mqttPublish(topic.c_str(), payload.c_str());
|
|
||||||
|
|
||||||
// Also publish combined JSON
|
|
||||||
{
|
|
||||||
JsonDocument doc;
|
|
||||||
doc["temperature"] = serialized(String(temp, 2));
|
|
||||||
doc["rssi"] = rssi;
|
|
||||||
doc["battery"] = battery;
|
|
||||||
doc["timestamp"] = millis();
|
|
||||||
|
|
||||||
String jsonPayload;
|
|
||||||
serializeJson(doc, jsonPayload);
|
|
||||||
String jsonTopic = baseTopic + "/state";
|
|
||||||
mqttPublish(jsonTopic.c_str(), jsonPayload.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mqtt::callback(char *topic, uint8_t *payload, unsigned int length)
|
void Mqtt::callback(char *topic, uint8_t *payload, unsigned int length)
|
||||||
// Not implemented yet. Just for debug
|
// Not implemented yet. Just for debug
|
||||||
{
|
{
|
||||||
|
|
@ -222,6 +107,8 @@ void Mqtt::callback(char *topic, uint8_t *payload, unsigned int length)
|
||||||
// Nullify last character to eliminate garbage at end
|
// Nullify last character to eliminate garbage at end
|
||||||
message[length] = '\0';
|
message[length] = '\0';
|
||||||
|
|
||||||
|
// Create correct object
|
||||||
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", "Received: %s", message);
|
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", "Received: %s", message);
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", "From: %s", topic);
|
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MQTT", "From: %s", topic);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,17 +13,10 @@ class Mqtt {
|
||||||
void mqttSetup();
|
void mqttSetup();
|
||||||
void mqttRun();
|
void mqttRun();
|
||||||
void mqttPublish(const char* topic, const char* payload);
|
void mqttPublish(const char* topic, const char* payload);
|
||||||
void publishTemperature(float temp);
|
|
||||||
void publishRSSI(int rssi);
|
|
||||||
void publishSensorData(float temp, int rssi, int battery);
|
|
||||||
void publishDiscoveryConfig();
|
|
||||||
private:
|
private:
|
||||||
void mqttReconnect();
|
void mqttReconnect();
|
||||||
void setupHomeAssistantDiscovery();
|
|
||||||
static void callback(char *topic, uint8_t *payload, unsigned int length);
|
static void callback(char *topic, uint8_t *payload, unsigned int length);
|
||||||
PubSubClient mqttClient;
|
PubSubClient mqttClient;
|
||||||
String deviceId;
|
|
||||||
String baseTopic;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* MQTT_H */
|
#endif /* MQTT_H */
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"board": {
|
"board": {
|
||||||
"active_layer": 7,
|
"active_layer": 2,
|
||||||
"active_layer_preset": "",
|
"active_layer_preset": "All Layers",
|
||||||
"auto_track_width": true,
|
"auto_track_width": true,
|
||||||
"hidden_netclasses": [],
|
"hidden_netclasses": [],
|
||||||
"hidden_nets": [],
|
"hidden_nets": [],
|
||||||
|
|
@ -89,16 +89,16 @@
|
||||||
9
|
9
|
||||||
],
|
],
|
||||||
"col_widths": [
|
"col_widths": [
|
||||||
99,
|
148,
|
||||||
156,
|
234,
|
||||||
75,
|
|
||||||
82,
|
|
||||||
119,
|
|
||||||
138,
|
|
||||||
129,
|
|
||||||
113,
|
113,
|
||||||
61,
|
123,
|
||||||
520
|
178,
|
||||||
|
207,
|
||||||
|
194,
|
||||||
|
170,
|
||||||
|
91,
|
||||||
|
780
|
||||||
],
|
],
|
||||||
"custom_group_rules": [],
|
"custom_group_rules": [],
|
||||||
"expanded_rows": [],
|
"expanded_rows": [],
|
||||||
|
|
|
||||||
|
|
@ -549,10 +549,6 @@
|
||||||
{
|
{
|
||||||
"netclass": "Power",
|
"netclass": "Power",
|
||||||
"pattern": "V"
|
"pattern": "V"
|
||||||
},
|
|
||||||
{
|
|
||||||
"netclass": "Power",
|
|
||||||
"pattern": "DRV"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -2050,29 +2050,27 @@
|
||||||
(on_board yes)
|
(on_board yes)
|
||||||
(in_pos_files yes)
|
(in_pos_files yes)
|
||||||
(duplicate_pin_numbers_are_jumpers no)
|
(duplicate_pin_numbers_are_jumpers no)
|
||||||
(property "Reference" "U2"
|
(property "Reference" "U"
|
||||||
(at 2.1433 12.7 0)
|
(at -10.16 8.89 0)
|
||||||
(show_name no)
|
(show_name no)
|
||||||
(do_not_autoplace no)
|
(do_not_autoplace no)
|
||||||
(effects
|
(effects
|
||||||
(font
|
(font
|
||||||
(size 1.27 1.27)
|
(size 1.27 1.27)
|
||||||
)
|
)
|
||||||
(justify left)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
(property "Value" "TPL5110"
|
(property "Value" "TPL5110"
|
||||||
(at 2.1433 10.16 0)
|
(at 6.35 8.89 0)
|
||||||
(show_name no)
|
(show_name no)
|
||||||
(do_not_autoplace no)
|
(do_not_autoplace no)
|
||||||
(effects
|
(effects
|
||||||
(font
|
(font
|
||||||
(size 1.27 1.27)
|
(size 1.27 1.27)
|
||||||
)
|
)
|
||||||
(justify left)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
(property "Footprint" "Connector_PinHeader_2.54mm:PinHeader_1x05_P2.54mm_Vertical"
|
(property "Footprint" "Package_TO_SOT_SMD:SOT-23-6"
|
||||||
(at 0 0 0)
|
(at 0 0 0)
|
||||||
(show_name no)
|
(show_name no)
|
||||||
(do_not_autoplace no)
|
(do_not_autoplace no)
|
||||||
|
|
@ -2195,10 +2193,10 @@
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
(pin output line
|
(pin input line
|
||||||
(at 12.7 0 180)
|
(at 12.7 -2.54 180)
|
||||||
(length 2.54)
|
(length 2.54)
|
||||||
(name "DRV"
|
(name "DONE"
|
||||||
(effects
|
(effects
|
||||||
(font
|
(font
|
||||||
(size 1.27 1.27)
|
(size 1.27 1.27)
|
||||||
|
|
@ -2213,10 +2211,10 @@
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
(pin input line
|
(pin output line
|
||||||
(at 12.7 -2.54 180)
|
(at 12.7 0 180)
|
||||||
(length 2.54)
|
(length 2.54)
|
||||||
(name "DONE"
|
(name "DRV"
|
||||||
(effects
|
(effects
|
||||||
(font
|
(font
|
||||||
(size 1.27 1.27)
|
(size 1.27 1.27)
|
||||||
|
|
@ -3005,16 +3003,6 @@
|
||||||
)
|
)
|
||||||
(uuid "a6ae61cd-e9d4-47f6-8882-fa10e2999828")
|
(uuid "a6ae61cd-e9d4-47f6-8882-fa10e2999828")
|
||||||
)
|
)
|
||||||
(wire
|
|
||||||
(pts
|
|
||||||
(xy 217.17 81.28) (xy 227.33 81.28)
|
|
||||||
)
|
|
||||||
(stroke
|
|
||||||
(width 0)
|
|
||||||
(type default)
|
|
||||||
)
|
|
||||||
(uuid "b22fa6e6-33d1-4ee5-aa6a-13a2f7290c02")
|
|
||||||
)
|
|
||||||
(wire
|
(wire
|
||||||
(pts
|
(pts
|
||||||
(xy 91.44 120.65) (xy 111.76 120.65)
|
(xy 91.44 120.65) (xy 111.76 120.65)
|
||||||
|
|
@ -3185,6 +3173,16 @@
|
||||||
)
|
)
|
||||||
(uuid "dd49213d-e5d5-450f-8a6b-3ad161d40e30")
|
(uuid "dd49213d-e5d5-450f-8a6b-3ad161d40e30")
|
||||||
)
|
)
|
||||||
|
(wire
|
||||||
|
(pts
|
||||||
|
(xy 219.71 82.55) (xy 227.33 82.55)
|
||||||
|
)
|
||||||
|
(stroke
|
||||||
|
(width 0)
|
||||||
|
(type default)
|
||||||
|
)
|
||||||
|
(uuid "e021cd98-8e13-4fe8-9877-a9093c57dd0d")
|
||||||
|
)
|
||||||
(wire
|
(wire
|
||||||
(pts
|
(pts
|
||||||
(xy 91.44 115.57) (xy 111.76 115.57)
|
(xy 91.44 115.57) (xy 111.76 115.57)
|
||||||
|
|
@ -3801,7 +3799,7 @@
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
(global_label "DRV"
|
(global_label "VBat"
|
||||||
(shape input)
|
(shape input)
|
||||||
(at 41.91 123.19 180)
|
(at 41.91 123.19 180)
|
||||||
(fields_autoplaced yes)
|
(fields_autoplaced yes)
|
||||||
|
|
@ -3813,7 +3811,7 @@
|
||||||
)
|
)
|
||||||
(uuid "8c02f690-251e-4668-a5e3-dc40144b8d1e")
|
(uuid "8c02f690-251e-4668-a5e3-dc40144b8d1e")
|
||||||
(property "Intersheetrefs" "${INTERSHEET_REFS}"
|
(property "Intersheetrefs" "${INTERSHEET_REFS}"
|
||||||
(at 35.2962 123.19 0)
|
(at 34.6915 123.19 0)
|
||||||
(hide yes)
|
(hide yes)
|
||||||
(show_name no)
|
(show_name no)
|
||||||
(do_not_autoplace no)
|
(do_not_autoplace no)
|
||||||
|
|
@ -4043,7 +4041,7 @@
|
||||||
)
|
)
|
||||||
(global_label "RST"
|
(global_label "RST"
|
||||||
(shape input)
|
(shape input)
|
||||||
(at 217.17 81.28 180)
|
(at 219.71 82.55 180)
|
||||||
(fields_autoplaced yes)
|
(fields_autoplaced yes)
|
||||||
(effects
|
(effects
|
||||||
(font
|
(font
|
||||||
|
|
@ -4053,7 +4051,7 @@
|
||||||
)
|
)
|
||||||
(uuid "ed29a99c-a631-4bf3-a4b4-2001a48e027d")
|
(uuid "ed29a99c-a631-4bf3-a4b4-2001a48e027d")
|
||||||
(property "Intersheetrefs" "${INTERSHEET_REFS}"
|
(property "Intersheetrefs" "${INTERSHEET_REFS}"
|
||||||
(at 210.7377 81.28 0)
|
(at 213.2777 82.55 0)
|
||||||
(hide yes)
|
(hide yes)
|
||||||
(show_name no)
|
(show_name no)
|
||||||
(do_not_autoplace no)
|
(do_not_autoplace no)
|
||||||
|
|
@ -4976,7 +4974,7 @@
|
||||||
(justify left)
|
(justify left)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
(property "Value" "TPL5110_breakout"
|
(property "Value" "TPL5110"
|
||||||
(at 54.2133 165.1 0)
|
(at 54.2133 165.1 0)
|
||||||
(show_name no)
|
(show_name no)
|
||||||
(do_not_autoplace no)
|
(do_not_autoplace no)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,6 @@ lib_deps =
|
||||||
zinggjm/GxEPD2@^1.5.6
|
zinggjm/GxEPD2@^1.5.6
|
||||||
bblanchon/ArduinoJson@^7.0.4
|
bblanchon/ArduinoJson@^7.0.4
|
||||||
peterus/esp-logger@^1.0.0
|
peterus/esp-logger@^1.0.0
|
||||||
jgromes/RadioLib@^6.0.0
|
sandeepmistry/LoRa@^0.8.0
|
||||||
olikraus/U8g2_for_Adafruit_GFX@^1.8.0
|
olikraus/U8g2_for_Adafruit_GFX@^1.8.0
|
||||||
milesburton/DallasTemperature@^3.11.0
|
milesburton/DallasTemperature@^3.11.0
|
||||||
|
|
|
||||||
|
|
@ -6,131 +6,50 @@
|
||||||
extern logging::Logger logger;
|
extern logging::Logger logger;
|
||||||
extern Config config;
|
extern Config config;
|
||||||
|
|
||||||
LoraHandler::LoraHandler() {
|
|
||||||
// Initialize with new SX1268 radio
|
|
||||||
}
|
|
||||||
|
|
||||||
void LoraHandler::setup()
|
void LoraHandler::setup()
|
||||||
{
|
{
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "LoRa", "Initializing SX1268 with RadioLib!");
|
|
||||||
|
|
||||||
// Create new SPI instance for LoRa
|
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "LoRa", "Set SPI pins!");
|
||||||
spi = new SPIClass(VSPI);
|
SPI.begin(RADIO_SCK, RADIO_MISO, RADIO_MOSI, RADIO_CS);
|
||||||
spi->begin(RADIO_SCK, RADIO_MISO, RADIO_MOSI, RADIO_CS);
|
LoRa.setPins(RADIO_CS, RADIO_RST, RADIO_IRQ);
|
||||||
|
|
||||||
// Create SX1268 instance
|
long freq = config.loraConfig.frequency;
|
||||||
radio = new SX1268(new SPIDriver(spi, RADIO_CS));
|
if (!LoRa.begin(freq))
|
||||||
|
|
||||||
// Configure RXEN/TXEN pins
|
|
||||||
pinMode(RADIO_RXEN, OUTPUT);
|
|
||||||
pinMode(RADIO_TXEN, OUTPUT);
|
|
||||||
digitalWrite(RADIO_RXEN, LOW);
|
|
||||||
digitalWrite(RADIO_TXEN, LOW);
|
|
||||||
|
|
||||||
// Initialize LoRa module
|
|
||||||
int state = radio->begin();
|
|
||||||
if (state != RADIOLIB_ERR_NONE)
|
|
||||||
{
|
{
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "LoRa", "LoRa init failed: %i", state);
|
logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "LoRa", "Starting LoRa failed!");
|
||||||
while (true) {
|
// show_display("ERROR", "Starting LoRa failed!");
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
delay(1000);
|
delay(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
LoRa.setSpreadingFactor(config.loraConfig.spreadingFactor);
|
||||||
// Configure LoRa parameters
|
LoRa.setSignalBandwidth(config.loraConfig.signalBandwidth);
|
||||||
long freq = config.loraConfig.frequency;
|
LoRa.setCodingRate4(config.loraConfig.codingRate4);
|
||||||
state = radio->setFrequency(freq);
|
LoRa.enableCrc();
|
||||||
if (state != RADIOLIB_ERR_NONE)
|
LoRa.setTxPower(config.loraConfig.power);
|
||||||
{
|
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "LoRa", "LoRa init done!");
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "LoRa", "setFrequency failed: %i", state);
|
String currentLoRainfo = "LoRa Freq: " + String(config.loraConfig.frequency) + " / SF:" + String(config.loraConfig.spreadingFactor) + " / CR: " + String(config.loraConfig.codingRate4);
|
||||||
}
|
|
||||||
|
|
||||||
state = radio->setSpreadingFactor(config.loraConfig.spreadingFactor);
|
|
||||||
if (state != RADIOLIB_ERR_NONE)
|
|
||||||
{
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "LoRa", "setSpreadingFactor failed: %i", state);
|
|
||||||
}
|
|
||||||
|
|
||||||
state = radio->setBandwidth(config.loraConfig.signalBandwidth);
|
|
||||||
if (state != RADIOLIB_ERR_NONE)
|
|
||||||
{
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "LoRa", "setBandwidth failed: %i", state);
|
|
||||||
}
|
|
||||||
|
|
||||||
state = radio->setCodingRate(config.loraConfig.codingRate4);
|
|
||||||
if (state != RADIOLIB_ERR_NONE)
|
|
||||||
{
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "LoRa", "setCodingRate failed: %i", state);
|
|
||||||
}
|
|
||||||
|
|
||||||
state = radio->setOutputPower(config.loraConfig.power);
|
|
||||||
if (state != RADIOLIB_ERR_NONE)
|
|
||||||
{
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "LoRa", "setOutputPower failed: %i", state);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable CRC
|
|
||||||
radio->setCRC(true);
|
|
||||||
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "LoRa", "SX1268 initialized successfully!");
|
|
||||||
String currentLoRainfo = "LoRa Freq: " + String(freq) + " / SF:" + String(config.loraConfig.spreadingFactor) + " / CR: " + String(config.loraConfig.codingRate4);
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "LoRa", currentLoRainfo.c_str());
|
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "LoRa", currentLoRainfo.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoraHandler::sendPacket(const String& data)
|
|
||||||
{
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "LoRa Tx", "Sending: %s", data.c_str());
|
|
||||||
|
|
||||||
// Enable TX mode
|
|
||||||
digitalWrite(RADIO_TXEN, HIGH);
|
|
||||||
digitalWrite(RADIO_RXEN, LOW);
|
|
||||||
delay(10);
|
|
||||||
|
|
||||||
int state = radio->transmit(data);
|
|
||||||
|
|
||||||
// Disable TX mode
|
|
||||||
digitalWrite(RADIO_TXEN, LOW);
|
|
||||||
|
|
||||||
if (state != RADIOLIB_ERR_NONE)
|
|
||||||
{
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "LoRa Tx", "Transmit failed: %i", state);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "LoRa Tx", "Packet sent!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ReceivedLoRaPacket LoraHandler::receivePacket()
|
ReceivedLoRaPacket LoraHandler::receivePacket()
|
||||||
{
|
{
|
||||||
ReceivedLoRaPacket receivedLoraPacket;
|
ReceivedLoRaPacket receivedLoraPacket;
|
||||||
|
String packet = "";
|
||||||
// Enable RX mode
|
int packetSize = LoRa.parsePacket();
|
||||||
digitalWrite(RADIO_RXEN, HIGH);
|
if (packetSize)
|
||||||
digitalWrite(RADIO_TXEN, LOW);
|
|
||||||
|
|
||||||
int state = radio->receive(0); // 0 = non-blocking
|
|
||||||
|
|
||||||
if (state == RADIOLIB_ERR_RX_ONGOING)
|
|
||||||
{
|
{
|
||||||
// No packet received yet
|
while (LoRa.available())
|
||||||
return receivedLoraPacket;
|
{
|
||||||
|
int inChar = LoRa.read();
|
||||||
|
packet += (char)inChar;
|
||||||
|
}
|
||||||
|
receivedLoraPacket.text = packet;
|
||||||
|
receivedLoraPacket.rssi = LoRa.packetRssi();
|
||||||
|
receivedLoraPacket.snr = LoRa.packetSnr();
|
||||||
|
receivedLoraPacket.freqError = LoRa.packetFrequencyError();
|
||||||
|
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "LoRa Rx", "---> %s", packet.c_str());
|
||||||
}
|
}
|
||||||
else if (state == RADIOLIB_ERR_NONE)
|
|
||||||
{
|
|
||||||
// Packet received!
|
|
||||||
receivedLoraPacket.text = radio->readData();
|
|
||||||
receivedLoraPacket.rssi = radio->getRSSI();
|
|
||||||
receivedLoraPacket.snr = radio->getSNR();
|
|
||||||
receivedLoraPacket.freqError = radio->getFrequencyError();
|
|
||||||
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "LoRa Rx", "---> %s (RSSI: %d, SNR: %.1f)",
|
|
||||||
receivedLoraPacket.text.c_str(), receivedLoraPacket.rssi, receivedLoraPacket.snr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "LoRa Rx", "Receive error: %i", state);
|
|
||||||
}
|
|
||||||
|
|
||||||
return receivedLoraPacket;
|
return receivedLoraPacket;
|
||||||
}
|
}
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
#define LORAHANDLER_H
|
#define LORAHANDLER_H
|
||||||
|
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include <RadioLib.h>
|
#include <SPI.h>
|
||||||
|
#include <LoRa.h>
|
||||||
|
|
||||||
struct ReceivedLoRaPacket {
|
struct ReceivedLoRaPacket {
|
||||||
String text;
|
String text;
|
||||||
|
|
@ -13,13 +14,9 @@ struct ReceivedLoRaPacket {
|
||||||
|
|
||||||
class LoraHandler {
|
class LoraHandler {
|
||||||
public:
|
public:
|
||||||
LoraHandler();
|
|
||||||
void setup();
|
void setup();
|
||||||
void sendPacket(const String& data);
|
|
||||||
ReceivedLoRaPacket receivePacket();
|
ReceivedLoRaPacket receivePacket();
|
||||||
private:
|
private:
|
||||||
SX1268* radio;
|
|
||||||
SPIClass* spi;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* LORAHANDLER_H */
|
#endif /* LORAHANDLER_H */
|
||||||
|
|
@ -2,16 +2,10 @@
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include "eink.hpp"
|
#include "eink.hpp"
|
||||||
#include "io.hpp"
|
#include "io.hpp"
|
||||||
#include "lorahandler.hpp"
|
|
||||||
#include "tpl5110.hpp"
|
|
||||||
#include "pins.hpp"
|
|
||||||
#include <ArduinoJson.h>
|
|
||||||
|
|
||||||
logging::Logger logger;
|
logging::Logger logger;
|
||||||
Eink eink;
|
Eink eink;
|
||||||
IO io;
|
IO io;
|
||||||
LoraHandler lora;
|
|
||||||
TPL5110 tpl5110(TPL5110_DONE);
|
|
||||||
|
|
||||||
RTC_DATA_ATTR int bootCount = 0;
|
RTC_DATA_ATTR int bootCount = 0;
|
||||||
RTC_DATA_ATTR int updateCount = 0;
|
RTC_DATA_ATTR int updateCount = 0;
|
||||||
|
|
@ -28,8 +22,6 @@ void setup()
|
||||||
#ifndef DEBUG
|
#ifndef DEBUG
|
||||||
logger.setDebugLevel(logging::LoggerLevel::LOGGER_LEVEL_INFO);
|
logger.setDebugLevel(logging::LoggerLevel::LOGGER_LEVEL_INFO);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
tpl5110.setup(); // Initialize TPL5110 timer
|
|
||||||
io.setup_io();
|
io.setup_io();
|
||||||
io.set_low_power();
|
io.set_low_power();
|
||||||
|
|
||||||
|
|
@ -65,29 +57,7 @@ void setup()
|
||||||
eink.show_count(180 - 24 - 4 - 100 - 60, 12, (float)bootCount, "c: ");
|
eink.show_count(180 - 24 - 4 - 100 - 60, 12, (float)bootCount, "c: ");
|
||||||
eink.show_count(180 - 24 - 4 - 100 - 60 - 50, 12, (float)bootCount, "u: ");
|
eink.show_count(180 - 24 - 4 - 100 - 60 - 50, 12, (float)bootCount, "u: ");
|
||||||
eink.display(true);
|
eink.display(true);
|
||||||
|
|
||||||
// Initialize LoRa and send data
|
|
||||||
lora.setup();
|
|
||||||
|
|
||||||
// Create JSON payload
|
|
||||||
JsonDocument doc;
|
|
||||||
doc["temp"] = temp;
|
|
||||||
doc["battery"] = battery;
|
|
||||||
doc["boot"] = bootCount;
|
|
||||||
doc["update"] = updateCount;
|
|
||||||
|
|
||||||
String payload;
|
|
||||||
serializeJson(doc, payload);
|
|
||||||
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "MAIN", "Sending LoRa payload: %s", payload.c_str());
|
|
||||||
lora.sendPacket(payload);
|
|
||||||
|
|
||||||
delay(1000); // Give time for transmission
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signal TPL5110 that work is done - device will sleep
|
|
||||||
tpl5110.signalDone();
|
|
||||||
delay(100);
|
|
||||||
io.set_deep_sleep();
|
io.set_deep_sleep();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,18 +11,13 @@ const int EPD_RSET = 16;
|
||||||
const int EPD_DC = 17;
|
const int EPD_DC = 17;
|
||||||
const int EPD_CS = 5;
|
const int EPD_CS = 5;
|
||||||
|
|
||||||
// Pin assignments for LORA module (SX1268 E22-400M22S)
|
// Pin assignments for LORA module
|
||||||
const int RADIO_SCK = 5;
|
const int RADIO_SCK = 5;
|
||||||
const int RADIO_MISO = 19;
|
const int RADIO_MISO = 19;
|
||||||
const int RADIO_MOSI = 27;
|
const int RADIO_MOSI = 27;
|
||||||
const int RADIO_CS = 18; // CS --> NSS
|
const int RADIO_CS = 18; // CS --> NSS
|
||||||
const int RADIO_RST = 14;
|
const int RADIO_RST = 14;
|
||||||
const int RADIO_IRQ = 26; // IRQ --> DIO0
|
const int RADIO_IRQ = 26; // IRQ --> DIO0
|
||||||
const int RADIO_RXEN = 2; // RX Enable
|
|
||||||
const int RADIO_TXEN = 13; // TX Enable
|
|
||||||
|
|
||||||
// TPL5110 timer module
|
|
||||||
const int TPL5110_DONE = 15; // DONE signal
|
|
||||||
|
|
||||||
// Battery pin
|
// Battery pin
|
||||||
const int ADC_BATTERY = 35; //builtin = 35
|
const int ADC_BATTERY = 35; //builtin = 35
|
||||||
|
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
#include "tpl5110.hpp"
|
|
||||||
#include "logger.h"
|
|
||||||
|
|
||||||
extern logging::Logger logger;
|
|
||||||
|
|
||||||
TPL5110::TPL5110(int donePin) : _donePin(donePin) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void TPL5110::setup() {
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "TPL5110", "Initializing TPL5110 timer!");
|
|
||||||
pinMode(_donePin, OUTPUT);
|
|
||||||
digitalWrite(_donePin, LOW); // DONE pin starts LOW
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "TPL5110", "TPL5110 initialized!");
|
|
||||||
}
|
|
||||||
|
|
||||||
void TPL5110::signalDone() {
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "TPL5110", "Signaling DONE to TPL5110!");
|
|
||||||
digitalWrite(_donePin, HIGH);
|
|
||||||
delay(100); // Hold HIGH for 100ms
|
|
||||||
digitalWrite(_donePin, LOW);
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "TPL5110", "DONE signal sent - device will sleep now");
|
|
||||||
}
|
|
||||||
|
|
||||||
void TPL5110::setActive() {
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "TPL5110", "TPL5110 set to ACTIVE");
|
|
||||||
digitalWrite(_donePin, LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TPL5110::setInactive() {
|
|
||||||
logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "TPL5110", "TPL5110 set to INACTIVE");
|
|
||||||
digitalWrite(_donePin, HIGH);
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
#ifndef TPL5110_H
|
|
||||||
#define TPL5110_H
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
class TPL5110 {
|
|
||||||
public:
|
|
||||||
TPL5110(int donePin);
|
|
||||||
void setup();
|
|
||||||
void signalDone();
|
|
||||||
void setActive();
|
|
||||||
void setInactive();
|
|
||||||
private:
|
|
||||||
int _donePin;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* TPL5110_H */
|
|
||||||
Loading…
Reference in New Issue