[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:
1 parent
3d20995b3a
commit
246423a8a8
18 files changed
+990
-121
No files matched your search
+1
-1
@@ -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
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in new issue
Block a user