[LORA] Improvements, change frequency, webUI, Oled support, add boards (#1742)

* [LORA] Capability to change LORA frequency and Add OLED

Merge of the 2 environments for TTGO V1

* Merge messages keys with upper level json

* Id as a sub topic

* Add display and state measures

* Add frequency configuration through WebUI

* Add parameters

* Add auto discovery

* Add node example for ttgo lora32 v1

* Add environments and fix scope

* Support for sync word as a byte

* Fix TX Power setting

* Add LoRa gateway description

* Add onlyknown filter
This commit is contained in:
Florian authored and GitHub committed 2023-09-27 11:12:55 -05:00
1 parent 3d20995b3a
commit 246423a8a8
18 files changed
+990 -121

No files matched your search

+3 -4
View File
@@ -44,11 +44,10 @@ jobs:
- "lilygo-ble"
- "esp32dev-multi_receiver"
- "tinypico-ble"
- "ttgo-lora32-v1-868"
- "ttgo-lora32-v1-915"
- "ttgo-lora32-v1"
- "ttgo-lora32-v21"
- "ttgo-t-beam"
- "heltec-wifi-lora-32-868"
- "heltec-wifi-lora-32-915"
- "heltec-wifi-lora-32"
- "shelly-plus1"
- "nodemcuv2-all-test"
- "nodemcuv2-fastled-test"
Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

+45 -24
View File
@@ -1,11 +1,39 @@
---
title: LORA MQTT Gateway
description: Explore the LORA MQTT gateway, designed for integration with devices utilizing LORA technology, including the MakerFab soil and moisture sensor. Unlock long range communication with the power of LORA..
description: Explore the LORA MQTT gateway, designed for integration with devices utilizing LORA technology, including the MakerFab soil and moisture sensor, devices from PricelessTookit. Unlock long range communication with the power of LORA..
---
# LORA gateway
Tutorial on how to leverage LORA for a mailbox sensor from [PricelessToolkit](https://www.youtube.com/channel/UCz75N6inuLHXnRC5tqagNLw):
<iframe width="560" height="315" src="https://www.youtube.com/embed/6DftaHxDawM" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
## What is a LORA gateway
A LoRa (Long Range) gateway is a device that facilitates communication between LoRa nodes and networks, enabling the transmission and reception of data over long distances using the LoRa modulation technique. It's designed to work with devices that utilize LoRa technology, such as the MakerFab soil and moisture sensor, devices from PricelessTookit and DIY sensors.
The primary distinction between a LoRa gateway and a LoRaWAN gateway lies in the protocol and network architecture:
LoRa Gateway: Focuses solely on the physical layer, utilizing the LoRa modulation for communication. It's responsible for receiving and transmitting raw LoRa signals without concerning itself with network protocols or data handling at higher layers. Being focused solely on the physical layer, a LoRa Gateway offers greater flexibility for customization and experimentation. The OpenMQTTGateway LoRa gateway receives raw LoRa signals, processes them, and publishes the data to an MQTT topic. Conversely, it can subscribe to MQTT topics and send commands to LoRa devices. This gateway is particularly useful for DIY projects, home automation enthusiasts, and scenarios where direct integration of LoRa devices with MQTT is desired.
LoRaWAN Gateway: Operates at a higher layer and is part of the LoRaWAN network architecture. LoRaWAN is a protocol specification built on top of the LoRa technology, providing features like adaptive data rate, encryption, and multi-channel/multi-modulation. A LoRaWAN gateway handles the data from multiple LoRa nodes, forwards it to a centralized network server, which then manages the data and communicates back to the nodes.
In essence, while both gateways utilize LoRa technology for communication, a LoRaWAN gateway is more sophisticated, offering advanced features and integration with the LoRaWAN network infrastructure. The LoRa gateway, with its simpler architecture, is ideal for small networks of nodes, offering easier setup and configuration, making it an interesting choice for users keen on experimenting with LoRa technology.
## Configuring the LORA gateway
The LORA gateway can be configured by MQTT commands or by using the WebUI, here are the parameters available, they can be combined with the key "save" or "erase":
* txpower: 0 to 14
* spreadingfactor: 7 to 12
* frequency: 433000000, 868000000, 915000000
* signalbandwidth: 7800, 10400, 15500, 20800, 31250, 41700, 62500, 125000, and 250000
* codingrate: 5 to 8
* preamblelength: 6 to 65535
* syncword: byte
* enablecrc: boolean
* invertiq: boolean
* onlyknown: boolean
With the WebUI:
![LORA configuration page](../img/OpenMQTTGateway_LORA_Configuration.png)
With MQTT commands:
`mosquitto_pub -t home/OpenMQTTGateway/commands/MQTTtoLORA/config -m '{"frequency":"433000000","save":true}'`
## Receiving data from LORA signal
@@ -13,22 +41,24 @@ Subscribe to all the messages with mosquitto or open your MQTT client software:
` sudo mosquitto_sub -t +/# -v`
Generate your LORA signals by using another TTGO LORA module, you can flash the sender program from [this repository](https://github.com/LilyGO/TTGO-LORA32-V2.0) and the SSD1306 library [there](https://github.com/ThingPulse/esp8266-oled-ssd1306)
Generate your LORA signals by using another LORA module, you can flash the sender program from [this example](../../examples/LoraTemperature/) to an ESP32 LORA board, this sample node will generate a LORa signal containing the ESP32 internal temperature.
![TTGO Lora sending packets](../img/OpenMQTTGateway_TTGO32_LORA_Send.jpg)
Once one board flashed with OMG and the other with the sender program you should receive regular packets into `home/OpenMQTTGateway_ESP32_LORA_TEST/LORAtoMQTT` like below:
Once one board flashed with OMG and the other with the sender program you should receive regular packets into `home/OpenMQTTGateway_ESP32_LORA_TEST/LORAtoMQTT/AABBCCDDEEFF` like below:
```json
{"rssi":-16,"snr":9.25,"pferror":-3598,"packetSize":9,"message":"hello 35"}
{"rssi":-26,"snr":9,"pferror":-3598,"packetSize":9,"message":"hello 36"}
{"rssi":-16,"snr":9.5,"pferror":-3581,"packetSize":9,"message":"hello 37"}
{"id":"AA:BB:CC:DD:EE:FF","rssi":-16,"snr":9.25,"pferror":-3598,"packetSize":9,"tempc":"55.3"}
{"id":"AA:BB:CC:DD:EE:FF","rssi":-26,"snr":9,"pferror":-3598,"packetSize":9,"tempc":"55.4"}
{"id":"AA:BB:CC:DD:EE:FF","rssi":-16,"snr":9.5,"pferror":-3581,"packetSize":9,"tempc":"57"}
```
![LORA board receiving data](../img/OpenMQTTgateway_ESP32_LORA_MSG.png)
Messages that contain non-printable characters will be converted to hexadecimal and look like this:
```json
{"rssi":-121,"snr":-11.75,"pferror":-29116,"packetSize":3,"hex":"C0FFEE"}
```
They can be filtered by setting the "onlyknown" command to `true` or by an activation into the WebUI or Home Assistant.
And from a supported device (in this case, a WiPhone), looks like this:
```json
{"rssi":-50,"snr":9.25,"pferror":20728,"packetSize":30,"from":"123ABC","to":"000000","message":"Hi from WiPhone","type":"WiPhone"}
@@ -44,23 +74,14 @@ If you want to test that your sending works you can use another TTGO LORA module
## Send data by MQTT with advanced LORA parameters
LORA sending support the following parameters that should be specified in the json message;
* txpower: 2 to 20
* spreadingfactor: 6 to 12. If a spreading factor of 6 is set, implicit header mode must be used to transmit and receive * * packets.
* frequency: 433E6, 866E6, 915E6
* signalbandwidth: 7.8E3, 10.4E3, 15.6E3, 20.8E3, 31.25E3, 41.7E3, 62.5E3, 125E3, and 250E3
* codingrate: 5 to 8
* preamblelength: 6 to 65535
* syncword: byte
* enablecrc: boolean
More info on where the LORA library is born [@sandeepmistry](https://github.com/sandeepmistry/arduino-LoRa/blob/master/API.md#radio-parameters)
Examples:
* Plain text message: `mosquitto_pub -t home/OpenMQTTGateway/commands/MQTTtoLORA -m '{"message":"test8","txpower":17}'`\
will make LORA use the a txpower of 17 when sending the message "test8"
* Binary message: `mosquitto_pub -t "home/OpenMQTTGateway/commands/MQTTtoLORA" -m '{"hex":"01C0FFEE"}'`\
will send binary 0x01C0FFEE
* WiPhone message: `mosquitto_pub -t "home/OpenMQTTGateway/commands/MQTTtoLORA" -m '{"message":"test","type":"WiPhone","to":"123ABC","from":"FFFFFF"}'`\
will send "test" to a WiPhone with chip ID 123ABC
More info on where the LORA library is born [@sandeepmistry](https://github.com/sandeepmistry/arduino-LoRa/blob/master/API.md#radio-parameters)
Tutorial on how to leverage LORA for a mailbox sensor from [PricelessToolkit](https://www.youtube.com/channel/UCz75N6inuLHXnRC5tqagNLw):
<iframe width="560" height="315" src="https://www.youtube.com/embed/6DftaHxDawM" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
+27 -37
View File
@@ -869,33 +869,50 @@ board_build.flash_mode = dio
custom_description = BLE gateway
custom_hardware = ESP32 TinyPICO
[env:ttgo-lora32-v1-868]
[env:ttgo-lora32-v1]
platform = ${com.esp32_platform}
board = ttgo-lora32-v1
lib_deps =
${com-esp32.lib_deps}
${libraries.ssd1306}
${libraries.lora}
build_flags =
${com-esp32.build_flags}
'-DZgatewayLORA="LORA"'
'-DLORA_BAND=868E6'
'-DZdisplaySSD1306="LilyGo_SSD1306"'
'-DSDA_OLED=4'
'-DSCL_OLED=15'
'-DRST_OLED=16'
'-DWIFI_LoRa_32=true'
'-DGateway_Name="OMG_ESP32_LORA"'
custom_description = LORA communication 868Mhz using arduino-LoRA
custom_description = LORA communication using arduino-LoRA configurable frequency
custom_hardware= ESP32 TTGO LORA V1
[env:ttgo-lora32-v1-915]
[env:ttgo-lora32-v21]
platform = ${com.esp32_platform}
board = ttgo-lora32-v1
board = ttgo-lora32-v21
lib_deps =
${com-esp32.lib_deps}
${libraries.ssd1306}
${libraries.lora}
build_flags =
${com-esp32.build_flags}
'-DZgatewayLORA="LORA"'
'-DLORA_BAND=915E6'
; *** OpenMQTTGateway Config ***
;'-UZmqttDiscovery' ; disables MQTT Discovery
'-DGateway_Name="OMG_ESP32_LORA"'
custom_description = LORA communication 915Mhz using arduino-LoRA
custom_hardware= ESP32 TTGO LORA V1
; *** OpenMQTTGateway Modules ***
'-DZgatewayLORA="LORA"'
; *** ssd1306 Display Options ***
'-DZdisplaySSD1306="LilyGo_SSD1306"'
; '-DLOG_TO_OLED=true' ; Enable log to OLED
; '-DJSON_TO_OLED=true'
; '-DLOG_LEVEL_OLED=LOG_LEVEL_NOTICE'
; '-DDISPLAY_IDLE_LOGO=false'
; '-DDISPLAY_BRIGHTNESS=80'
; '-DDISPLAY_METRIC=false'
custom_description = For ESP32, Gateway using LORA
custom_hardware = ESP32 LILYGO LoRa32 V2.1
[env:ttgo-t-beam]
# See version pinout differences here
@@ -928,7 +945,7 @@ build_flags =
custom_description = BLE gateway with battery holder
custom_hardware = TTGO T BEAM
[env:heltec-wifi-lora-32-868] ; Heltec ESP32 Board with SSD1306 display
[env:heltec-wifi-lora-32] ; Heltec ESP32 Board with SSD1306 display
platform = ${com.esp32_platform}
board = heltec_wifi_lora_32
lib_deps =
@@ -952,34 +969,7 @@ build_flags =
; '-DDISPLAY_IDLE_LOGO=false'
; '-DDISPLAY_BRIGHTNESS=80'
; '-DDISPLAY_METRIC=false'
custom_description = LORA communication 868Mhz using arduino-LoRA
custom_hardware = ESP32 HELTEC LORA32
[env:heltec-wifi-lora-32-915] ; Heltec ESP32 Board with SSD1306 display
platform = ${com.esp32_platform}
board = heltec_wifi_lora_32
lib_deps =
${com-esp32.lib_deps}
${libraries.lora}
${libraries.ssd1306}
build_flags =
${com-esp32.build_flags}
; *** OpenMQTTGateway Modules ***
'-DZgatewayLORA="LORA"'
'-DLORA_BAND=915E6'
'-DGateway_Name="OMG_ESP32_LORA"'
'-DLED_SEND_RECEIVE=25'
'-DTimeLedON=0.1'
'-DLED_SEND_RECEIVE_ON=1'
; *** ssd1306 Display Options ***
'-DZdisplaySSD1306="HELTEC_SSD1306"'
; '-DLOG_TO_OLED=true' ; Enable log to OLED
; '-DJSON_TO_OLED=true'
; '-DLOG_LEVEL_OLED=LOG_LEVEL_NOTICE'
; '-DDISPLAY_IDLE_LOGO=false'
; '-DDISPLAY_BRIGHTNESS=80'
; '-DDISPLAY_METRIC=false'
custom_description = LORA communication 915Mhz using arduino-LoRA
custom_description = LORA communication using arduino-LoRA
custom_hardware = ESP32 HELTEC LORA32
; For testing only, not for production use
+5
View File
@@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
+59
View File
@@ -0,0 +1,59 @@
# OpenMQTTGateway LoRa Node Example
This repository contains an example of a LoRa node program designed for the ESP32 platform. The program reads the internal temperature of the ESP32, packages the data into a JSON format, and sends it over LoRa.
## Features:
* Uses an SX12XX LoRa module.
* Displays packet sending status and temperature data on an SSD1306 OLED display.
* Sends the ESP32's MAC address as the node ID.
* Sends temperature data in Celsius.
## Hardware Requirements:
* ESP32 development board.
* SX12XX LoRa module.
* SSD1306 OLED display.
## Pin Configuration:
* SCK - GPIO5
* MISO - GPIO19
* MOSI - GPIO27
* SS - GPIO18
* RST - GPIO14
* DI0 - GPIO26
## Setup:
### Hardware Setup:
Connect the SX1278 LoRa module and the SSD1306 OLED display to the ESP32 according to the pin configuration.
Ensure that the OLED display is powered correctly.
### Software Setup:
* Clone this repository.
* Open the provided node program with PlatformIO
* Upload the program to your ESP32.
## Usage:
Power on the ESP32.
The OLED display will show the status of the packet being sent and the current temperature reading.
The built-in LED on the ESP32 will blink once every time a packet is sent.
Monitor the serial output (at 115200 baud rate) to see the JSON formatted data being sent.
## Data Format:
The data is sent in the following JSON format:
```json
{
"model": "ESP32TEMP",
"id": "ESP32_MAC_ADDRESS",
"tempc": "TEMPERATURE_IN_CELSIUS"
}
```
## Troubleshooting:
LoRa Initialization Failed: Ensure that the SX1278 LoRa module is connected correctly and powered on.
OLED Display Not Working: Check the connections and ensure that the display is powered correctly.
No Temperature Data: Ensure that the ESP32's internal temperature sensor is functional.
Contributing:
Feel free to contribute to this example by opening issues or submitting pull requests. Any feedback or improvements are welcome!
I hope this README helps users understand and use your program! Adjustments can be made as necessary to fit any additional details or changes.
+36
View File
@@ -0,0 +1,36 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:ttgo-lora32-v1]
platform = espressif32
board = ttgo-lora32-v1
framework = arduino
lib_deps =
https://github.com/sandeepmistry/arduino-LoRa.git#f4a1d27
https://github.com/ThingPulse/esp8266-oled-ssd1306.git#f96fd6a
monitor_speed = 115200
[env:ttgo-lora32-v21]
platform = espressif32
board = ttgo-lora32-v21
framework = arduino
lib_deps =
https://github.com/sandeepmistry/arduino-LoRa.git#f4a1d27
https://github.com/ThingPulse/esp8266-oled-ssd1306.git#f96fd6a
monitor_speed = 115200
[env:heltec-wifi-lora-32] ; Heltec ESP32 Board with SSD1306 display
platform = ${com.esp32_platform}
board = heltec_wifi_lora_32
framework = arduino
lib_deps =
https://github.com/sandeepmistry/arduino-LoRa.git#f4a1d27
https://github.com/ThingPulse/esp8266-oled-ssd1306.git#f96fd6a
monitor_speed = 115200
+104
View File
@@ -0,0 +1,104 @@
#include <LoRa.h>
#include <SPI.h>
#include <WiFi.h>
#include <Wire.h>
#include <stdio.h>
#include "SSD1306.h"
#include "rom/ets_sys.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/sens_reg.h"
#define SCK 5 // GPIO5 -- SX1278's SCK
#define MISO 19 // GPIO19 -- SX1278's MISnO
#define MOSI 27 // GPIO27 -- SX1278's MOSI
#define SS 18 // GPIO18 -- SX1278's CS
#define RST 14 // GPIO14 -- SX1278's RESET
#define DI0 26 // GPIO26 -- SX1278's IRQ(Interrupt Request)
#define BAND 915E6
unsigned int counter = 0;
SSD1306 display(0x3c, 4, 15);
String rssi = "RSSI --";
String packSize = "--";
String packet;
float intTemperatureRead() {
SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 3,
SENS_FORCE_XPD_SAR_S);
SET_PERI_REG_BITS(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_CLK_DIV, 10,
SENS_TSENS_CLK_DIV_S);
CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP);
CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_DUMP_OUT);
SET_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP_FORCE);
SET_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP);
ets_delay_us(100);
SET_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_DUMP_OUT);
ets_delay_us(5);
float temp_f = (float)GET_PERI_REG_BITS2(SENS_SAR_SLAVE_ADDR3_REG,
SENS_TSENS_OUT, SENS_TSENS_OUT_S);
float temp_c = (temp_f - 32) / 1.8;
return temp_c;
}
void setup() {
pinMode(16, OUTPUT);
pinMode(2, OUTPUT);
digitalWrite(16, LOW); // set GPIO16 low to reset OLED
delay(50);
digitalWrite(16, HIGH); // while OLED is running, must set GPIO16 in high
Serial.begin(115200);
while (!Serial)
;
Serial.println();
Serial.println("LoRa Sender Test");
SPI.begin(SCK, MISO, MOSI, SS);
LoRa.setPins(SS, RST, DI0);
if (!LoRa.begin(BAND)) {
Serial.println("Starting LoRa failed!");
while (1)
;
}
Serial.println("init ok");
display.init();
display.flipScreenVertically();
display.setFont(ArialMT_Plain_10);
delay(1500);
}
void loop() {
display.clear();
display.setTextAlignment(TEXT_ALIGN_LEFT);
display.setFont(ArialMT_Plain_10);
display.drawString(0, 0, "Sending packet: ");
display.drawString(90, 0, String(counter));
String NodeId = WiFi.macAddress();
float temp = intTemperatureRead();
// send packet
LoRa.beginPacket();
// Build json string to send
String msg = "{\"model\":\"ESP32TEMP\",\"id\":\"" + NodeId + "\",\"tempc\":" + String(temp) + "}";
// Send json string
LoRa.print(msg);
LoRa.endPacket();
Serial.println(String(msg));
display.drawString(0, 15, String(NodeId));
display.drawString(0, 30, "tempc: " + String(temp) + " C");
display.display();
counter++;
digitalWrite(2, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(2, LOW); // turn the LED off by making the voltage LOW
delay(60000); // wait for 60 seconds
}
+1 -1
View File
@@ -719,7 +719,7 @@ Adafruit_NeoPixel leds2(ANEOPIX_IND_NUM_LEDS, ANEOPIX_IND_DATA_GPIO2, ANEOPIX_IN
#endif
/*-----------PLACEHOLDERS FOR WebUI DISPLAY--------------*/
#define pubWebUI(...) // display the published message onto the OLED display
#define pubWebUI(...) // display the published message onto the WebUI display
/*-----------PLACEHOLDERS FOR OLED/LCD DISPLAY--------------*/
// The real definitions are in config_M5.h / config_SSD1306.h
+317 -41
View File
@@ -37,6 +37,141 @@
# define WIPHONE_MESSAGE_MIN_LEN sizeof(wiphone_message) - WIPHONE_MAX_MESSAGE_LEN
# define WIPHONE_MAX_MESSAGE_LEN 230
LORAConfig_s LORAConfig;
# ifdef ZmqttDiscovery
SemaphoreHandle_t semaphorecreateOrUpdateDeviceLORA;
std::vector<LORAdevice*> LORAdevices;
int newLORADevices = 0;
static LORAdevice NO_LORA_DEVICE_FOUND = {{0},
0,
false};
LORAdevice* getDeviceById(const char* id); // Declared here to avoid pre-compilation issue (misplaced auto declaration by pio)
LORAdevice* getDeviceById(const char* id) {
Log.trace(F("getDeviceById %s" CR), id);
for (std::vector<LORAdevice*>::iterator it = LORAdevices.begin(); it != LORAdevices.end(); ++it) {
if ((strcmp((*it)->uniqueId, id) == 0)) {
return *it;
}
}
return &NO_LORA_DEVICE_FOUND;
}
void dumpLORADevices() {
for (std::vector<LORAdevice*>::iterator it = LORAdevices.begin(); it != LORAdevices.end(); ++it) {
LORAdevice* p = *it;
Log.trace(F("uniqueId %s" CR), p->uniqueId);
Log.trace(F("modelName %s" CR), p->modelName);
Log.trace(F("isDisc %d" CR), p->isDisc);
}
}
void createOrUpdateDeviceLORA(const char* id, const char* model, uint8_t flags) {
if (xSemaphoreTake(semaphorecreateOrUpdateDeviceLORA, pdMS_TO_TICKS(30000)) == pdFALSE) {
Log.error(F("[LORA] semaphorecreateOrUpdateDeviceLORA Semaphore NOT taken" CR));
return;
}
LORAdevice* device = getDeviceById(id);
if (device == &NO_LORA_DEVICE_FOUND) {
Log.trace(F("add %s" CR), id);
//new device
device = new LORAdevice();
if (strlcpy(device->uniqueId, id, uniqueIdSize) > uniqueIdSize) {
Log.warning(F("[LORA] Device id %s exceeds available space" CR), id); // Remove from production release ?
};
if (strlcpy(device->modelName, model, modelNameSize) > modelNameSize) {
Log.warning(F("[LORA] Device model %s exceeds available space" CR), id); // Remove from production release ?
};
device->isDisc = flags & device_flags_isDisc;
LORAdevices.push_back(device);
newLORADevices++;
} else {
Log.trace(F("update %s" CR), id);
if (flags & device_flags_isDisc) {
device->isDisc = true;
}
}
xSemaphoreGive(semaphorecreateOrUpdateDeviceLORA);
}
// This function always should be called from the main core as it generates direct mqtt messages
// When overrideDiscovery=true, we publish discovery messages of known LORAdevices (even if no new)
void launchLORADiscovery(bool overrideDiscovery) {
if (!overrideDiscovery && newLORADevices == 0)
return;
if (xSemaphoreTake(semaphorecreateOrUpdateDeviceLORA, pdMS_TO_TICKS(1000)) == pdFALSE) {
Log.error(F("[LORA] semaphorecreateOrUpdateDeviceLORA Semaphore NOT taken" CR));
return;
}
newLORADevices = 0;
std::vector<LORAdevice*> localDevices = LORAdevices;
xSemaphoreGive(semaphorecreateOrUpdateDeviceLORA);
for (std::vector<LORAdevice*>::iterator it = localDevices.begin(); it != localDevices.end(); ++it) {
LORAdevice* pdevice = *it;
Log.trace(F("Device id %s" CR), pdevice->uniqueId);
// Do not launch discovery for the LORAdevices already discovered (unless we have overrideDiscovery) or that are not unique by their MAC Address (Ibeacon, GAEN and Microsoft Cdp)
if (overrideDiscovery || !isDiscovered(pdevice)) {
size_t numRows = sizeof(LORAparameters) / sizeof(LORAparameters[0]);
for (int i = 0; i < numRows; i++) {
if (strstr(pdevice->uniqueId, LORAparameters[i][0]) != 0) {
// Remove the key from the unique id to extract the device id
String idWoKey = pdevice->uniqueId;
idWoKey.remove(idWoKey.length() - (strlen(LORAparameters[i][0]) + 1));
Log.trace(F("idWoKey %s" CR), idWoKey.c_str());
String value_template = "{{ value_json." + String(LORAparameters[i][0]) + " | is_defined }}";
String topic = idWoKey;
topic = String(subjectLORAtoMQTT) + "/" + topic;
createDiscovery("sensor", //set Type
(char*)topic.c_str(), LORAparameters[i][1], pdevice->uniqueId, //set state_topic,name,uniqueId
"", LORAparameters[i][3], (char*)value_template.c_str(), //set availability_topic,device_class,value_template,
"", "", LORAparameters[i][2], //set,payload_on,payload_off,unit_of_meas,
0, //set off_delay
"", "", false, "", //set,payload_available,payload_not available ,is a gateway entity, command topic
(char*)idWoKey.c_str(), "", pdevice->modelName, (char*)idWoKey.c_str(), false, // device name, device manufacturer, device model, device ID, retain
stateClassMeasurement //State Class
);
pdevice->isDisc = true; // we don't need the semaphore and all the search magic via createOrUpdateDevice
dumpLORADevices();
break;
}
}
if (!pdevice->isDisc) {
Log.trace(F("Device id %s was not discovered" CR), pdevice->uniqueId); // Remove from production release ?
}
} else {
Log.trace(F("Device already discovered or that doesn't require discovery %s" CR), pdevice->uniqueId);
}
}
}
void storeLORADiscovery(JsonObject& RFLORA_ESPdata, const char* model, const char* uniqueid) {
//Sanitize model name
String modelSanitized = model;
modelSanitized.replace(" ", "_");
modelSanitized.replace("/", "_");
modelSanitized.replace(".", "_");
modelSanitized.replace("&", "");
//Sensors translation matrix for sensors that requires statistics by using stateClassMeasurement
size_t numRows = sizeof(LORAparameters) / sizeof(LORAparameters[0]);
for (int i = 0; i < numRows; i++) {
if (RFLORA_ESPdata.containsKey(LORAparameters[i][0])) {
String key_id = String(uniqueid) + "-" + String(LORAparameters[i][0]);
createOrUpdateDeviceLORA((char*)key_id.c_str(), (char*)modelSanitized.c_str(), device_flags_init);
}
}
}
# endif
typedef struct __attribute__((packed)) {
// WiPhone uses RadioHead library which has additional (unused) headers
uint8_t rh_to;
@@ -127,7 +262,109 @@ boolean _MQTTtoWiPhone(JsonObject& LORAdata) {
return true;
}
void LORAConfig_init() {
LORAConfig.frequency = LORA_BAND;
LORAConfig.txPower = LORA_TX_POWER;
LORAConfig.spreadingFactor = LORA_SPREADING_FACTOR;
LORAConfig.signalBandwidth = LORA_SIGNAL_BANDWIDTH;
LORAConfig.codingRateDenominator = LORA_CODING_RATE;
LORAConfig.preambleLength = LORA_PREAMBLE_LENGTH;
LORAConfig.syncWord = LORA_SYNC_WORD;
LORAConfig.crc = DEFAULT_CRC;
LORAConfig.invertIQ = INVERT_IQ;
LORAConfig.onlyKnown = LORA_ONLY_KNOWN;
}
void LORAConfig_load() {
StaticJsonDocument<JSON_MSG_BUFFER> jsonBuffer;
preferences.begin(Gateway_Short_Name, true);
if (preferences.isKey("LORAConfig")) {
auto error = deserializeJson(jsonBuffer, preferences.getString("LORAConfig", "{}"));
preferences.end();
Log.notice(F("LORA Config loaded" CR));
if (error) {
Log.error(F("LORA Config deserialization failed: %s, buffer capacity: %u" CR), error.c_str(), jsonBuffer.capacity());
return;
}
if (jsonBuffer.isNull()) {
Log.warning(F("LORA Config is null" CR));
return;
}
JsonObject jo = jsonBuffer.as<JsonObject>();
LORAConfig_fromJson(jo);
Log.notice(F("LORA Config loaded" CR));
} else {
preferences.end();
Log.notice(F("LORA Config not found" CR));
}
}
byte hexStringToByte(const String& hexString) {
return (byte)strtol(hexString.c_str(), NULL, 16);
}
void LORAConfig_fromJson(JsonObject& LORAdata) {
Config_update(LORAdata, "frequency", LORAConfig.frequency);
Config_update(LORAdata, "txpower", LORAConfig.txPower);
Config_update(LORAdata, "spreadingfactor", LORAConfig.spreadingFactor);
Config_update(LORAdata, "signalbandwidth", LORAConfig.signalBandwidth);
Config_update(LORAdata, "codingrate", LORAConfig.codingRateDenominator);
Config_update(LORAdata, "preamblelength", LORAConfig.preambleLength);
Config_update(LORAdata, "onlyknown", LORAConfig.onlyKnown);
// Handle syncword separately
if (LORAdata.containsKey("syncword")) {
String syncWordStr = LORAdata["syncword"].as<String>();
LORAConfig.syncWord = hexStringToByte(syncWordStr);
Log.notice(F("Config syncword changed: %d" CR), LORAConfig.syncWord);
}
Config_update(LORAdata, "enablecrc", LORAConfig.crc);
Config_update(LORAdata, "invertiq", LORAConfig.invertIQ);
LoRa.setFrequency(LORAConfig.frequency);
LoRa.setTxPower(LORAConfig.txPower);
LoRa.setSpreadingFactor(LORAConfig.spreadingFactor);
LoRa.setSignalBandwidth(LORAConfig.signalBandwidth);
LoRa.setCodingRate4(LORAConfig.codingRateDenominator);
LoRa.setPreambleLength(LORAConfig.preambleLength);
LoRa.setSyncWord(LORAConfig.syncWord);
LORAConfig.crc ? LoRa.enableCrc() : LoRa.disableCrc();
LORAConfig.invertIQ ? LoRa.enableInvertIQ() : LoRa.disableInvertIQ();
if (LORAdata.containsKey("erase") && LORAdata["erase"].as<bool>()) {
// Erase config from NVS (non-volatile storage)
preferences.begin(Gateway_Short_Name, false);
if (preferences.isKey("LORAConfig")) {
int result = preferences.remove("LORAConfig");
Log.notice(F("LORA config erase result: %d" CR), result);
preferences.end();
return; // Erase prevails on save, so skipping save
} else {
Log.notice(F("LORA config not found" CR));
preferences.end();
}
}
if (LORAdata.containsKey("save") && LORAdata["save"].as<bool>()) {
StaticJsonDocument<JSON_MSG_BUFFER> jsonBuffer;
JsonObject jo = jsonBuffer.to<JsonObject>();
jo["frequency"] = LORAConfig.frequency;
// Save config into NVS (non-volatile storage)
String conf = "";
serializeJson(jsonBuffer, conf);
preferences.begin(Gateway_Short_Name, false);
int result = preferences.putString("LORAConfig", conf);
preferences.end();
Log.notice(F("LORA Config_save: %s, result: %d" CR), conf.c_str(), result);
}
}
void setupLORA() {
LORAConfig_init();
LORAConfig_load();
# ifdef ZmqttDiscovery
semaphorecreateOrUpdateDeviceLORA = xSemaphoreCreateBinary();
xSemaphoreGive(semaphorecreateOrUpdateDeviceLORA);
# endif
Log.notice(F("LORA Frequency: %d" CR), LORAConfig.frequency);
# ifdef ESP8266
SPI.begin();
# else
@@ -136,7 +373,7 @@ void setupLORA() {
LoRa.setPins(LORA_SS, LORA_RST, LORA_DI0);
if (!LoRa.begin(LORA_BAND)) {
if (!LoRa.begin(LORAConfig.frequency)) {
Log.error(F("ZgatewayLORA setup failed!" CR));
while (1)
;
@@ -148,12 +385,6 @@ void setupLORA() {
Log.notice(F("LORA_SS: %d" CR), LORA_SS);
Log.notice(F("LORA_RST: %d" CR), LORA_RST);
Log.notice(F("LORA_DI0: %d" CR), LORA_DI0);
LoRa.setTxPower(LORA_TX_POWER);
LoRa.setSpreadingFactor(LORA_SPREADING_FACTOR);
LoRa.setSignalBandwidth(LORA_SIGNAL_BANDWIDTH);
LoRa.setCodingRate4(LORA_CODING_RATE);
LoRa.setPreambleLength(LORA_PREAMBLE_LENGTH);
LoRa.setSyncWord(LORA_SYNC_WORD);
Log.trace(F("ZgatewayLORA setup done" CR));
}
@@ -179,16 +410,14 @@ void LORAtoMQTT() {
}
// Terminate with a null character in case we have a string
packet[packetSize] = 0;
LORAdata["rssi"] = (int)LoRa.packetRssi();
LORAdata["snr"] = (float)LoRa.packetSnr();
LORAdata["pferror"] = (float)LoRa.packetFrequencyError();
LORAdata["packetSize"] = (int)packetSize;
uint8_t deviceId = _determineDevice(packet, packetSize);
if (deviceId == WIPHONE) {
_WiPhoneToMQTT(packet, LORAdata);
} else if (binary) {
if (LORAConfig.onlyKnown) {
Log.trace(F("Ignoring non identifiable packet" CR));
return;
}
// We have non-ascii data: create hex string of the data
char hex[packetSize * 2 + 1];
_rawToHex(packet, hex, packetSize);
@@ -198,9 +427,37 @@ void LORAtoMQTT() {
LORAdata["hex"] = hex;
} else {
// ascii payload
LORAdata["message"] = packet;
deserializeJson(jsonBuffer, packet, packetSize);
}
LORAdata["rssi"] = (int)LoRa.packetRssi();
LORAdata["snr"] = (float)LoRa.packetSnr();
LORAdata["pferror"] = (float)LoRa.packetFrequencyError();
LORAdata["packetSize"] = (int)packetSize;
if (LORAdata.containsKey("id")) {
// Replace ":" in topic
std::string topic = LORAdata["id"].as<std::string>();
size_t pos = topic.find(":");
while (pos != std::string::npos) {
topic.erase(pos, 1);
pos = topic.find(":", pos);
}
std::string id = topic;
std::string subjectStr(subjectLORAtoMQTT);
topic = subjectStr + "/" + topic;
# ifdef ZmqttDiscovery
if (SYSConfig.discovery) {
if (!LORAdata.containsKey("model"))
LORAdata["model"] = "LORA_NODE";
storeLORADiscovery(LORAdata, LORAdata["model"].as<char*>(), id.c_str());
}
# endif
pub(topic.c_str(), LORAdata);
} else {
pub(subjectLORAtoMQTT, LORAdata);
}
pub(subjectLORAtoMQTT, LORAdata);
if (repeatLORAwMQTT) {
Log.trace(F("Pub LORA for rpt" CR));
pub(subjectMQTTtoLORA, LORAdata);
@@ -214,33 +471,8 @@ void MQTTtoLORA(char* topicOri, JsonObject& LORAdata) { // json object decoding
Log.trace(F("MQTTtoLORA json" CR));
const char* message = LORAdata["message"];
const char* hex = LORAdata["hex"];
int txPower = LORAdata["txpower"] | LORA_TX_POWER;
int spreadingFactor = LORAdata["spreadingfactor"] | LORA_SPREADING_FACTOR;
long int frequency = LORAdata["frequency "] | LORA_BAND;
long int signalBandwidth = LORAdata["signalbandwidth"] | LORA_SIGNAL_BANDWIDTH;
int codingRateDenominator = LORAdata["codingrate"] | LORA_CODING_RATE;
int preambleLength = LORAdata["preamblelength"] | LORA_PREAMBLE_LENGTH;
byte syncWord = LORAdata["syncword"] | LORA_SYNC_WORD;
bool crc = LORAdata["enablecrc"] | DEFAULT_CRC;
bool invertIQ = LORAdata["invertiq"] | INVERT_IQ;
LORAConfig_fromJson(LORAdata);
if (message || hex) {
LoRa.setTxPower(txPower);
LoRa.setFrequency(frequency);
LoRa.setSpreadingFactor(spreadingFactor);
LoRa.setSignalBandwidth(signalBandwidth);
LoRa.setCodingRate4(codingRateDenominator);
LoRa.setPreambleLength(preambleLength);
LoRa.setSyncWord(syncWord);
if (crc)
LoRa.enableCrc();
else
LoRa.disableCrc();
if (invertIQ)
LoRa.enableInvertIQ();
else
LoRa.disableInvertIQ();
LoRa.beginPacket();
uint8_t deviceId = _determineDevice(LORAdata);
if (deviceId == WIPHONE) {
@@ -262,6 +494,26 @@ void MQTTtoLORA(char* topicOri, JsonObject& LORAdata) { // json object decoding
Log.error(F("MQTTtoLORA Fail json" CR));
}
}
if (cmpToMainTopic(topicOri, subjectMQTTtoLORAset)) {
Log.trace(F("MQTTtoLORA json set" CR));
/*
* Configuration modifications priorities:
* First `init=true` and `load=true` commands are executed (if both are present, INIT prevails on LOAD)
* Then parameters included in json are taken in account
* Finally `erase=true` and `save=true` commands are executed (if both are present, ERASE prevails on SAVE)
*/
if (LORAdata.containsKey("init") && LORAdata["init"].as<bool>()) {
// Restore the default (initial) configuration
LORAConfig_init();
} else if (LORAdata.containsKey("load") && LORAdata["load"].as<bool>()) {
// Load the saved configuration, if not initialised
LORAConfig_load();
}
// Load config from json if available
LORAConfig_fromJson(LORAdata);
stateLORAMeasures();
}
}
# endif
# if simpleReceiving
@@ -275,4 +527,28 @@ void MQTTtoLORA(char* topicOri, char* LORAdata) { // json object decoding
}
}
# endif
String stateLORAMeasures() {
//Publish LORA state
StaticJsonDocument<JSON_MSG_BUFFER> jsonBuffer;
JsonObject LORAdata = jsonBuffer.to<JsonObject>();
LORAdata["frequency"] = LORAConfig.frequency;
LORAdata["txpower"] = LORAConfig.txPower;
LORAdata["spreadingfactor"] = LORAConfig.spreadingFactor;
LORAdata["signalbandwidth"] = LORAConfig.signalBandwidth;
LORAdata["codingrate"] = LORAConfig.codingRateDenominator;
LORAdata["preamblelength"] = LORAConfig.preambleLength;
// Convert syncWord to a hexadecimal string and store it in the JSON
char syncWordHex[5]; // Enough space for 0xXX and null terminator
snprintf(syncWordHex, sizeof(syncWordHex), "0x%02X", LORAConfig.syncWord);
LORAdata["syncword"] = syncWordHex;
LORAdata["enablecrc"] = LORAConfig.crc;
LORAdata["invertiq"] = LORAConfig.invertIQ;
LORAdata["onlyknown"] = LORAConfig.onlyKnown;
pub(subjectGTWLORAtoMQTT, LORAdata);
String output;
serializeJson(LORAdata, output);
return output;
}
#endif
+33
View File
@@ -1031,6 +1031,39 @@ void pubMqttDiscovery() {
"", "", "", "", false, // device name, device manufacturer, device model, device ID, retain
stateClassNone //State Class
);
createDiscovery("switch", //set Type
subjectLORAtoMQTT, "LORA: CRC", (char*)getUniqueId("enablecrc", "").c_str(), //set state_topic,name,uniqueId
will_Topic, "", "{{ value_json.enablecrc }}", //set availability_topic,device_class,value_template,
"{\"enablecrc\":true,\"save\":true}", "{\"enablecrc\":false,\"save\":true}", "", //set,payload_on,payload_off,unit_of_meas,
0, //set off_delay
Gateway_AnnouncementMsg, will_Message, true, subjectMQTTtoLORAset, //set,payload_available,payload_not available ,is a gateway entity, command topic
"", "", "", "", false, // device name, device manufacturer, device model, device MAC, retain
stateClassNone, //State Class
"false", "true" //state_off, state_on
);
createDiscovery("switch", //set Type
subjectLORAtoMQTT, "LORA: Invert IQ", (char*)getUniqueId("invertiq", "").c_str(), //set state_topic,name,uniqueId
will_Topic, "", "{{ value_json.invertiq }}", //set availability_topic,device_class,value_template,
"{\"invertiq\":true,\"save\":true}", "{\"invertiq\":false,\"save\":true}", "", //set,payload_on,payload_off,unit_of_meas,
0, //set off_delay
Gateway_AnnouncementMsg, will_Message, true, subjectMQTTtoLORAset, //set,payload_available,payload_not available ,is a gateway entity, command topic
"", "", "", "", false, // device name, device manufacturer, device model, device MAC, retain
stateClassNone, //State Class
"false", "true" //state_off, state_on
);
createDiscovery("switch", //set Type
subjectLORAtoMQTT, "LORA: Only Known", (char*)getUniqueId("onlyknown", "").c_str(), //set state_topic,name,uniqueId
will_Topic, "", "{{ value_json.onlyknown }}", //set availability_topic,device_class,value_template,
"{\"onlyknown\":true,\"save\":true}", "{\"onlyknown\":false,\"save\":true}", "", //set,payload_on,payload_off,unit_of_meas,
0, //set off_delay
Gateway_AnnouncementMsg, will_Message, true, subjectMQTTtoLORAset, //set,payload_available,payload_not available ,is a gateway entity, command topic
"", "", "", "", false, // device name, device manufacturer, device model, device MAC, retain
stateClassNone, //State Class
"false", "true" //state_off, state_on
);
# endif
# ifdef ZgatewaySRFB
+199
View File
@@ -852,6 +852,146 @@ void handleLO() {
server.send(200, "text/html", response);
}
# ifdef ZgatewayLORA
/**
* @brief /LA - Configure LORA Page
* T: handleLA: uri: /la, args: 11, method: 1
* T: handleLA Arg: 0, lf=868100000
* T: handleLA Arg: 1, lt=14
* T: handleLA Arg: 2, ls=12
* T: handleLA Arg: 3, lb=125
* T: handleLA Arg: 4, lc=5
* T: handleLA Arg: 5, ll=8
* T: handleLA Arg: 6, lw=0
* T: handleLA Arg: 7, lr=1
* T: handleLA Arg: 8, li=0
* T: handleLA Arg: 9, ok=0
* T: handleLA Arg: 10, save=
*/
void handleLA() {
WEBUI_TRACE_LOG(F("handleLA: uri: %s, args: %d, method: %d" CR), server.uri(), server.args(), server.method());
WEBUI_SECURE
if (server.args()) {
for (uint8_t i = 0; i < server.args(); i++) {
WEBUI_TRACE_LOG(F("handleLA Arg: %d, %s=%s" CR), i, server.argName(i).c_str(), server.arg(i).c_str());
}
if (server.hasArg("save")) {
StaticJsonDocument<JSON_MSG_BUFFER> jsonBuffer;
JsonObject WEBtoLORA = jsonBuffer.to<JsonObject>();
if (server.hasArg("lf")) {
WEBtoLORA["frequency"] = server.arg("lf");
}
if (server.hasArg("lt")) {
WEBtoLORA["txpower"] = server.arg("lt");
}
if (server.hasArg("ls")) {
WEBtoLORA["spreadingfactor"] = server.arg("ls");
}
if (server.hasArg("lb")) {
WEBtoLORA["signalbandwidth"] = server.arg("lb");
}
if (server.hasArg("lc")) {
WEBtoLORA["codingrate"] = server.arg("lc");
}
if (server.hasArg("ll")) {
WEBtoLORA["preamblelength"] = server.arg("ll");
}
if (server.hasArg("lw")) {
WEBtoLORA["syncword"] = server.arg("lw");
}
if (server.hasArg("lr")) {
WEBtoLORA["enablecrc"] = server.arg("lr");
} else {
WEBtoLORA["enablecrc"] = false;
}
if (server.hasArg("li")) {
WEBtoLORA["invertiq"] = server.arg("li");
} else {
WEBtoLORA["invertiq"] = false;
}
if (server.hasArg("ok")) {
WEBtoLORA["onlyknown"] = server.arg("ok");
} else {
WEBtoLORA["onlyknown"] = false;
}
LORAConfig_fromJson(WEBtoLORA);
}
}
char jsonChar[100];
serializeJson(modules, jsonChar, measureJson(modules) + 1);
char buffer[WEB_TEMPLATE_BUFFER_MAX_SIZE];
snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, header_html, (String(gateway_name) + " - Configure LORA").c_str());
String response = String(buffer);
response += String(script);
response += String(style);
snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, config_lora_body,
jsonChar,
gateway_name,
LORAConfig.frequency == 868000000 ? "selected" : "",
LORAConfig.frequency == 915000000 ? "selected" : "",
LORAConfig.frequency == 433000000 ? "selected" : "",
LORAConfig.txPower == 0 ? "selected" : "",
LORAConfig.txPower == 1 ? "selected" : "",
LORAConfig.txPower == 2 ? "selected" : "",
LORAConfig.txPower == 3 ? "selected" : "",
LORAConfig.txPower == 4 ? "selected" : "",
LORAConfig.txPower == 5 ? "selected" : "",
LORAConfig.txPower == 6 ? "selected" : "",
LORAConfig.txPower == 7 ? "selected" : "",
LORAConfig.txPower == 8 ? "selected" : "",
LORAConfig.txPower == 9 ? "selected" : "",
LORAConfig.txPower == 10 ? "selected" : "",
LORAConfig.txPower == 11 ? "selected" : "",
LORAConfig.txPower == 12 ? "selected" : "",
LORAConfig.txPower == 13 ? "selected" : "",
LORAConfig.txPower == 14 ? "selected" : "",
LORAConfig.spreadingFactor == 7 ? "selected" : "",
LORAConfig.spreadingFactor == 8 ? "selected" : "",
LORAConfig.spreadingFactor == 9 ? "selected" : "",
LORAConfig.spreadingFactor == 10 ? "selected" : "",
LORAConfig.spreadingFactor == 11 ? "selected" : "",
LORAConfig.spreadingFactor == 12 ? "selected" : "",
LORAConfig.signalBandwidth == 7800 ? "selected" : "",
LORAConfig.signalBandwidth == 10400 ? "selected" : "",
LORAConfig.signalBandwidth == 15600 ? "selected" : "",
LORAConfig.signalBandwidth == 20800 ? "selected" : "",
LORAConfig.signalBandwidth == 31250 ? "selected" : "",
LORAConfig.signalBandwidth == 41700 ? "selected" : "",
LORAConfig.signalBandwidth == 62500 ? "selected" : "",
LORAConfig.signalBandwidth == 125000 ? "selected" : "",
LORAConfig.signalBandwidth == 250000 ? "selected" : "",
LORAConfig.signalBandwidth == 500000 ? "selected" : "",
LORAConfig.codingRateDenominator == 5 ? "selected" : "",
LORAConfig.codingRateDenominator == 6 ? "selected" : "",
LORAConfig.codingRateDenominator == 7 ? "selected" : "",
LORAConfig.codingRateDenominator == 8 ? "selected" : "",
LORAConfig.preambleLength,
LORAConfig.syncWord,
LORAConfig.crc ? "checked" : "",
LORAConfig.invertIQ ? "checked" : "",
LORAConfig.onlyKnown ? "checked" : "");
response += String(buffer);
snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, footer, OMG_VERSION);
response += String(buffer);
server.send(200, "text/html", response);
stateLORAMeasures();
Log.trace(F("[WebUI] LORAConfig end" CR));
}
# endif
/**
* @brief /RT - Reset configuration ( Erase and Restart ) from Configuration menu
*
@@ -1015,6 +1155,10 @@ void handleIN() {
# if defined(ZgatewayCloud)
informationDisplay += "1<BR>Cloud}2}1";
informationDisplay += stateCLOUDStatus();
# endif
# if defined(ZgatewayLORA)
informationDisplay += "1<BR>LORA}2}1";
informationDisplay += stateLORAMeasures();
# endif
informationDisplay += "1<BR>WebUI}2}1";
informationDisplay += stateWebUIStatus();
@@ -1276,6 +1420,9 @@ void WebUISetup() {
server.on("/wi", handleWI); // Configure Wifi
server.on("/mq", handleMQ); // Configure MQTT
server.on("/wu", handleWU); // Configure WebUI
# ifdef ZgatewayLORA
server.on("/la", handleLA); // Configure LORA
# endif
# if defined(ZgatewayCloud)
server.on("/cl", handleCL); // Configure Cloud
server.on("/tk", handleTK); // Store Device Token
@@ -2019,6 +2166,58 @@ void webUIPubPrint(const char* topicori, JsonObject& data) {
break;
}
# endif
# ifdef ZgatewayLORA
case webUIHash("LORAtoMQTT"): {
// {"tempc":25.4,"hum":0,"batt":0}
String line1 = "";
if (data.containsKey("tempc")) {
char temp[5];
float temperature_C = data["tempc"];
if (displayMetric) {
dtostrf(temperature_C, 3, 1, temp);
line1 = "temp: " + (String)temp + "°C ";
} else {
dtostrf(convertTemp_CtoF(temperature_C), 3, 1, temp);
line1 = "temp: " + (String)temp + "°F ";
}
}
line1.toCharArray(message->line1, WEBUI_TEXT_WIDTH);
// Line 2
String line2 = "";
float humidity = data["hum"];
if (data.containsKey("hum") && humidity <= 100 && humidity >= 0) {
char hum[5];
dtostrf(humidity, 3, 1, hum);
line2 += "hum: " + (String)hum + "% ";
}
line2.toCharArray(message->line2, WEBUI_TEXT_WIDTH);
// Line 3
String line3 = "";
float adc = data["adc"];
if (data.containsKey("adc") && adc <= 100 && adc >= 0) {
char cAdc[5];
dtostrf(adc, 3, 1, cAdc);
line3 += "adc: " + (String)cAdc + "µS/cm ";
}
line3.toCharArray(message->line2, WEBUI_TEXT_WIDTH);
// Queue completed message
if (xQueueSend(webUIQueue, (void*)&message, 0) != pdTRUE) {
Log.error(F("[ WebUI ] webUIQueue full, discarding signal %s" CR), message->title);
free(message);
} else {
// Log.notice(F("[ WebUI ] Queued %s" CR), message->title);
}
break;
}
# endif
default:
Log.verbose(F("[ WebUI ] unhandled topic %s" CR), message->title);
free(message);
+63 -8
View File
@@ -34,19 +34,39 @@ extern void MQTTtoLORA(char* topicOri, JsonObject& RFdata);
#define subjectLORAtoMQTT "/LORAtoMQTT"
#define subjectMQTTtoLORA "/commands/MQTTtoLORA"
#define subjectGTWLORAtoMQTT "/LORAtoMQTT"
#define subjectMQTTtoLORAset "/commands/MQTTtoLORA/config"
//Default parameters used when the parameters are not set in the json data
#ifndef LORA_BAND
# define LORA_BAND 868E6
#endif
#define LORA_SIGNAL_BANDWIDTH 125E3
#define LORA_TX_POWER 17
#define LORA_SPREADING_FACTOR 7
#define LORA_CODING_RATE 5
#define LORA_PREAMBLE_LENGTH 8
#define LORA_SYNC_WORD 0x12
#define DEFAULT_CRC true
#define INVERT_IQ false
#ifndef LORA_SIGNAL_BANDWIDTH
# define LORA_SIGNAL_BANDWIDTH 125E3
#endif
#ifndef LORA_TX_POWER
# define LORA_TX_POWER 14
#endif
#ifndef LORA_SPREADING_FACTOR
# define LORA_SPREADING_FACTOR 7
#endif
#ifndef LORA_CODING_RATE
# define LORA_CODING_RATE 5
#endif
#ifndef LORA_PREAMBLE_LENGTH
# define LORA_PREAMBLE_LENGTH 8
#endif
#ifndef LORA_SYNC_WORD
# define LORA_SYNC_WORD 0x12
#endif
#ifndef DEFAULT_CRC
# define DEFAULT_CRC true
#endif
#ifndef INVERT_IQ
# define INVERT_IQ false
#endif
#ifndef LORA_ONLY_KNOWN
# define LORA_ONLY_KNOWN false
#endif
#define repeatLORAwMQTT false // do we repeat a received signal by using MQTT with LORA gateway
@@ -78,4 +98,39 @@ extern void MQTTtoLORA(char* topicOri, JsonObject& RFdata);
# define LORA_DI0 26 // GPIO26 -- SX1278's IRQ(Interrupt Request)
#endif
struct LORAConfig_s {
long frequency;
int txPower;
int spreadingFactor;
long signalBandwidth;
int codingRateDenominator;
int preambleLength;
byte syncWord;
bool crc;
bool invertIQ;
bool onlyKnown;
};
#ifdef ZmqttDiscovery
extern void launchLORADiscovery(bool overrideDiscovery);
// This structure stores the entities of the devices and is they have been discovered or not
// The uniqueId is composed of the device id + the key
# define uniqueIdSize 30
# define modelNameSize 30
struct LORAdevice {
char uniqueId[uniqueIdSize];
char modelName[modelNameSize];
bool isDisc;
};
const char LORAparameters[3][4][12] = {
// LORA key, name, unit, device_class
{"tempc", "temperature", "°C", "temperature"},
{"hum", "humidity", "%", "humidity"},
{"moi", "moisture", "%", "humidity"}};
#endif
#endif
+87 -1
View File
@@ -50,7 +50,11 @@
#endif
#define configure_4 "<p><form action='wu' method='get'><button>Configure WebUI</button></form></p>"
#define configure_5 "<p><form action='lo' method='get'><button>Configure Logging</button></form></p>"
#define configure_6
#ifdef ZgatewayLORA
# define configure_6 "<p><form action='la' method='get'><button>Configure LORA</button></form></p>"
#else
# define configure_6
#endif
#define configure_7
#define configure_8
@@ -98,6 +102,88 @@ const char config_logging_body[] = body_header "<fieldset class=\"set1\"><legend
const char config_webui_body[] = body_header "<fieldset class=\"set1\"><legend><span><b>Configure WebUI</b></span></legend><form method='get' action='wu'><p><b>Display Metric</b><br><input id='dm' type='checkbox' %s></p><p><b>Secure WebUI</b><br><input id='sw' type='checkbox' %s></p><br><button name='save' type='submit' class='button bgrn'>Save</button></form></fieldset>" body_footer_config_menu;
const char config_lora_body[] = body_header
"<fieldset class=\"set1\">"
"<legend><span><b>Configure LORA</b></span></legend>"
"<form method='get' action='la'>"
"<p><b>Frequency</b><br>"
"<select id='lf' name='lf'>"
"<option %s value='868000000'>868MHz</option>"
"<option %s value='915000000'>915MHz</option>"
"<option %s value='433000000'>433MHz</option>"
"</select></p>"
"<p><b>TX Power</b><br>"
"<select id='lt' name='lt'>"
"<option %s value='0'>0 dBm</option>"
"<option %s value='1'>1 dBm</option>"
"<option %s value='2'>2 dBm</option>"
"<option %s value='3'>3 dBm</option>"
"<option %s value='4'>4 dBm</option>"
"<option %s value='5'>5 dBm</option>"
"<option %s value='6'>6 dBm</option>"
"<option %s value='7'>7 dBm</option>"
"<option %s value='8'>8 dBm</option>"
"<option %s value='9'>9 dBm</option>"
"<option %s value='10'>10 dBm</option>"
"<option %s value='11'>11 dBm</option>"
"<option %s value='12'>12 dBm</option>"
"<option %s value='13'>13 dBm</option>"
"<option %s value='14'>14 dBm</option>"
"</select></p>"
"<p><b>Spreading Factor</b><br>"
"<select id='ls' name='ls'>"
"<option %s value='7'>SF7</option>"
"<option %s value='8'>SF8</option>"
"<option %s value='9'>SF9</option>"
"<option %s value='10'>SF10</option>"
"<option %s value='11'>SF11</option>"
"<option %s value='12'>SF12</option>"
"</select></p>"
"<p><b>Signal Bandwidth</b><br>"
"<select id='lb' name='lb'>"
"<option %s value='7800'>7.8 kHz</option>"
"<option %s value='10400'>10.4 kHz</option>"
"<option %s value='15600'>15.6 kHz</option>"
"<option %s value='20800'>20.8 kHz</option>"
"<option %s value='31250'>31.25 kHz</option>"
"<option %s value='41700'>41.7 kHz</option>"
"<option %s value='62500'>62.5 kHz</option>"
"<option %s value='125000'>125 kHz</option>"
"<option %s value='250000'>250 kHz</option>"
"<option %s value='500000'>500 kHz</option>"
"</select></p>"
"<p><b>Coding Rate</b><br>"
"<select id='lc' name='lc'>"
"<option %s value='5'>4/5</option>"
"<option %s value='6'>4/6</option>"
"<option %s value='7'>4/7</option>"
"<option %s value='8'>4/8</option>"
"</select></p>"
"<p><b>Preamble Length</b><br>"
"<input type='number' id='ll' name='ll' value='%d'></p>"
"<p><b>Sync Word</b><br>"
"<input type='text' id='lw' name='lw' value='0x%02X'></p>"
"<p><b>CRC</b><br>"
"<input type='checkbox' id='lr' name='lr' %s></p>"
"<p><b>Invert IQ</b><br>"
"<input type='checkbox' id='li' name='li' %s></p>"
"<p><b>Only known</b><br>"
"<input type='checkbox' id='ok' name='ok' %s></p>"
"<br><button name='save' type='submit' class='button bgrn'>Save</button>"
"</form>"
"</fieldset>" body_footer_config_menu;
const char footer[] = "<div style='text-align:right;font-size:11px;'><hr/><a href='https://community.openmqttgateway.com' target='_blank' style='color:#aaa;'>%s</a></div></div></body></html>";
// Source file - https://github.com/1technophile/OpenMQTTGateway/blob/54decb4b65c7894b926ac3a89de0c6b2a3021506/docs/.vuepress/public/favicon-16x16.png
+1 -1
View File
@@ -34,7 +34,7 @@
/*------------------- Optional Compiler Directives ----------------------*/
#ifndef WEB_TEMPLATE_BUFFER_MAX_SIZE
# define WEB_TEMPLATE_BUFFER_MAX_SIZE 2000
# define WEB_TEMPLATE_BUFFER_MAX_SIZE 3000 // Max size of the template buffer
#endif
#ifndef DISPLAY_METRIC
+7
View File
@@ -1869,6 +1869,9 @@ void loop() {
# ifdef ZdisplaySSD1306
stateSSD1306Display();
# endif
# ifdef ZgatewayLORA
stateLORAMeasures();
# endif
# if defined(ZwebUI) && defined(ESP32)
stateWebUIStatus();
# endif
@@ -1944,6 +1947,10 @@ void loop() {
#endif
#ifdef ZgatewayLORA
LORAtoMQTT();
# ifdef ZmqttDiscovery
if (SYSConfig.discovery)
launchLORADiscovery(publishDiscovery);
# endif
#endif
#ifdef ZgatewayRF
RFtoMQTT();
+3 -4
View File
@@ -66,11 +66,10 @@ extra_configs =
;default_envs = lilygo-ble
;default_envs = esp32dev-multi_receiver
;default_envs = tinypico-ble
;default_envs = ttgo-lora32-v1-868
;default_envs = ttgo-lora32-v1-915
;default_envs = ttgo-lora32-v1
;default_envs = ttgo-lora32-v21
;default_envs = ttgo-t-beam
;default_envs = heltec-wifi-lora-32-868
;default_envs = heltec-wifi-lora-32-915
;default_envs = heltec-wifi-lora-32
;default_envs = shelly-plus1
;default_envs = nodemcuv2-all-test
;default_envs = nodemcuv2-fastled-test