Add Support for LoRaWan Rx1 and Rx2 profiles (#23394)

This commit is contained in:
Theo Arends 2025-05-13 16:53:02 +02:00
parent 08e8f0b64d
commit 7fb8654c6c
9 changed files with 222 additions and 139 deletions

View File

@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
## [14.6.0.2]
### Added
- Allow temporary change of DisplayDimmer (#23406)
- Support for LoRaWan Rx1 and Rx2 profiles (#23394)
### Breaking Changed

View File

@ -119,6 +119,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
- Command `JsonPP 0..7` to enable (>0) JSON Pretty Print on user interfaces and set number of indents
- Command `JsonPP <command>|backlog <command>;...` to enable JSON PP only once
- Support for multi channel AU915-928 LoRaWanBridge by Rob Clark [#23372](https://github.com/arendst/Tasmota/issues/23372)
- Support for LoRaWan Rx1 and Rx2 profiles [#23394](https://github.com/arendst/Tasmota/issues/23394)
- Allow temporary change of DisplayDimmer [#23406](https://github.com/arendst/Tasmota/issues/23406)
- WebUI status line for MQTT and TLS, added `FUNC_WEB_STATUS_LEFT` and `FUNC_WEB_STATUS_RIGHT` event [#23354](https://github.com/arendst/Tasmota/issues/23354)
- WebUI heap status [#23356](https://github.com/arendst/Tasmota/issues/23356)

View File

@ -266,7 +266,6 @@ struct TasmotaGlobal_t {
uint32_t zc_offset; // Zero cross moment offset due to monitoring chip processing (microseconds)
uint32_t zc_code_offset; // Zero cross moment offset due to executing power code (microseconds)
uint32_t zc_interval; // Zero cross interval around 8333 (60Hz) or 10000 (50Hz) (microseconds)
uint32_t skip_sleep; // Skip sleep
GpioOptionABits gpio_optiona; // GPIO Option_A flags
void *log_buffer_mutex; // Control access to log buffer
@ -328,6 +327,7 @@ struct TasmotaGlobal_t {
uint8_t user_globals[3]; // User set global temp/hum/press
uint8_t busy_time; // Time in ms to allow executing of time critical functions
uint8_t skip_sleep; // Skip sleep loops
uint8_t init_state; // Tasmota init state
uint8_t heartbeat_inverted; // Heartbeat pulse inverted flag
uint8_t spi_enabled; // SPI configured (bus1)
@ -747,20 +747,18 @@ void BacklogLoop(void) {
}
}
bool SleepSkip(uint32_t no_sleep) {
if (0 == no_sleep) {
return !TimeReached(TasmotaGlobal.skip_sleep);
}
SetNextTimeInterval(TasmotaGlobal.skip_sleep, no_sleep); // Skip sleep for some ms
return true;
void SleepSkip(void) {
TasmotaGlobal.skip_sleep = 250; // Skip sleep for 250 loops;
}
void SleepDelay(uint32_t mseconds) {
if (SleepSkip(0)) { return; } // Temporarily skip sleep to handle imminent interrupts outside interrupt handler
if (!TasmotaGlobal.backlog_nodelay && mseconds) {
uint32_t wait = millis() + mseconds;
while (!TimeReached(wait) && !Serial.available() && !SleepSkip(0)) { // We need to service serial buffer ASAP as otherwise we get uart buffer overrun
while (!TasmotaGlobal.skip_sleep && // We need to service imminent interrupts ASAP
!TimeReached(wait) &&
!Serial.available()) { // We need to service serial buffer ASAP as otherwise we get uart buffer overrun
XdrvXsnsCall(FUNC_SLEEP_LOOP); // Main purpose is reacting ASAP on serial data availability or interrupt handling (ADE7880)
if (TasmotaGlobal.skip_sleep) { break; }
delay(1);
}
} else {
@ -846,19 +844,22 @@ void loop(void) {
uint32_t my_activity = millis() - my_sleep;
if (Settings->flag3.sleep_normal) { // SetOption60 - Enable normal sleep instead of dynamic sleep
// yield(); // yield == delay(0), delay contains yield, auto yield in loop
SleepDelay(TasmotaGlobal.sleep); // https://github.com/esp8266/Arduino/issues/2021
if (TasmotaGlobal.skip_sleep) {
TasmotaGlobal.skip_sleep--; // Temporarily skip sleep to handle imminent interrupts outside interrupt handler
} else {
if (my_activity < (uint32_t)TasmotaGlobal.sleep) {
SleepDelay((uint32_t)TasmotaGlobal.sleep - my_activity); // Provide time for background tasks like wifi
if (Settings->flag3.sleep_normal) { // SetOption60 - Enable normal sleep instead of dynamic sleep
// yield(); // yield == delay(0), delay contains yield, auto yield in loop
SleepDelay(TasmotaGlobal.sleep); // https://github.com/esp8266/Arduino/issues/2021
} else {
if (TasmotaGlobal.global_state.network_down) {
SleepDelay(my_activity /2); // If wifi down and my_activity > setoption36 then force loop delay to 1/2 of my_activity period
if (my_activity < (uint32_t)TasmotaGlobal.sleep) {
SleepDelay((uint32_t)TasmotaGlobal.sleep - my_activity); // Provide time for background tasks like wifi
} else {
if (TasmotaGlobal.global_state.network_down) {
SleepDelay(my_activity /2); // If wifi down and my_activity > setoption36 then force loop delay to 1/2 of my_activity period
}
}
}
}
if (!my_activity) { my_activity++; } // We cannot divide by 0
uint32_t loop_delay = TasmotaGlobal.sleep;
if (!loop_delay) { loop_delay++; } // We cannot divide by 0

View File

@ -106,13 +106,30 @@
// EU868 values
#ifndef TAS_LORAWAN_FREQUENCY
#define TAS_LORAWAN_FREQUENCY 868.1 // Allowed values range from 150.0 to 960.0 MHz
#define TAS_LORAWAN_FREQUENCY 868.1 // Allowed values are 868.1, 868.3 and 868.5 MHz
#endif
#ifndef TAS_LORAWAN_BANDWIDTH
#define TAS_LORAWAN_BANDWIDTH 125.0 // Allowed values are 7.8, 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125.0, 250.0 and 500.0 kHz
#define TAS_LORAWAN_BANDWIDTH 125.0 // Allowed values are 125.0 and 250.0 kHz
#endif
#ifndef TAS_LORAWAN_SPREADING_FACTOR
#define TAS_LORAWAN_SPREADING_FACTOR 9 // Allowed values range from 5 to 12
#define TAS_LORAWAN_SPREADING_FACTOR 9 // Allowed values range from 7 to 12
#endif
#ifndef TAS_LORAWAN_BANDWIDTH_RX1
#define TAS_LORAWAN_BANDWIDTH_RX1 125.0 // DR3
#endif
#ifndef TAS_LORAWAN_SPREADING_FACTOR_RX1
#define TAS_LORAWAN_SPREADING_FACTOR_RX1 9 // DR3
#endif
#ifndef TAS_LORAWAN_FREQUENCY_DN
#define TAS_LORAWAN_FREQUENCY_DN 869.525 // Class B downlink channel
#endif
#ifndef TAS_LORAWAN_BANDWIDTH_RX2
#define TAS_LORAWAN_BANDWIDTH_RX2 125.0 // DR0
#endif
#ifndef TAS_LORAWAN_SPREADING_FACTOR_RX2
#define TAS_LORAWAN_SPREADING_FACTOR_RX2 12 // DR0
#endif
// Common LoRaWan values
@ -278,7 +295,7 @@ typedef struct LoraSettings_t {
} LoraSettings_t;
typedef struct Lora_t {
bool (* Config)(void);
bool (* Config)(bool);
bool (* Available)(void);
int (* Receive)(char*);
bool (* Send)(uint8_t*, uint32_t, bool);
@ -293,10 +310,13 @@ typedef struct Lora_t {
bool raw;
#ifdef USE_LORAWAN_BRIDGE
uint32_t device_address;
LoraSettings_t backup_settings;
uint8_t* send_buffer;
uint8_t send_buffer_step;
uint8_t send_buffer_len;
bool rx;
bool send_request;
bool profile_changed;
#endif // USE_LORAWAN_BRIDGE
} Lora_t;
Lora_t* Lora = nullptr;

View File

@ -46,7 +46,8 @@ bool LoraSx126xBusy(void) {
void IRAM_ATTR LoraSx126xOnInterrupt(void);
void LoraSx126xOnInterrupt(void) {
// This is called after EVERY type of enabled interrupt so chk for valid receivedFlag in LoraAvailableSx126x()
if (!Lora->send_flag && !Lora->received_flag && !Lora->receive_time) {
// if (!Lora->send_flag && !Lora->received_flag && !Lora->receive_time) {
if (!Lora->send_flag && !Lora->received_flag) {
Lora->receive_time = millis();
}
Lora->received_flag = true; // we got a packet, set the flag
@ -119,29 +120,31 @@ bool LoraSx126xSend(uint8_t* data, uint32_t len, bool invert) {
return (RADIOLIB_ERR_NONE == state);
}
bool LoraSx126xConfig(void) {
LoRaRadio.setCodingRate(Lora->settings.coding_rate);
LoRaRadio.setSyncWord(Lora->settings.sync_word);
LoRaRadio.setPreambleLength(Lora->settings.preamble_length);
LoRaRadio.setCurrentLimit(Lora->settings.current_limit);
LoRaRadio.setCRC(Lora->settings.crc_bytes);
bool LoraSx126xConfig(bool full) {
LoRaRadio.setSpreadingFactor(Lora->settings.spreading_factor);
LoRaRadio.setBandwidth(Lora->settings.bandwidth);
LoRaRadio.setFrequency(Lora->settings.frequency);
LoRaRadio.setOutputPower(Lora->settings.output_power);
if (Lora->settings.implicit_header) {
LoRaRadio.implicitHeader(Lora->settings.implicit_header);
} else {
LoRaRadio.explicitHeader();
if (full) {
LoRaRadio.setCodingRate(Lora->settings.coding_rate);
LoRaRadio.setSyncWord(Lora->settings.sync_word);
LoRaRadio.setPreambleLength(Lora->settings.preamble_length);
LoRaRadio.setCurrentLimit(Lora->settings.current_limit);
LoRaRadio.setCRC(Lora->settings.crc_bytes);
LoRaRadio.setOutputPower(Lora->settings.output_power);
if (Lora->settings.implicit_header) {
LoRaRadio.implicitHeader(Lora->settings.implicit_header);
} else {
LoRaRadio.explicitHeader();
}
LoRaRadio.invertIQ(false);
}
LoRaRadio.invertIQ(false);
return true;
}
bool LoraSx126xInit(void) {
LoRaRadio = new Module(Pin(GPIO_LORA_CS), Pin(GPIO_LORA_DI1), Pin(GPIO_LORA_RST), Pin(GPIO_LORA_BUSY));
if (RADIOLIB_ERR_NONE == LoRaRadio.begin(Lora->settings.frequency)) {
LoraSx126xConfig();
LoraSx126xConfig(true);
LoRaRadio.setDio1Action(LoraSx126xOnInterrupt);
if (RADIOLIB_ERR_NONE == LoRaRadio.startReceive()) {
return true;

View File

@ -72,35 +72,37 @@ bool LoraSx127xSend(uint8_t* data, uint32_t len, bool invert) {
return true;
}
bool LoraSx127xConfig(void) {
bool LoraSx127xConfig(bool full) {
LoRa.setFrequency(Lora->settings.frequency * 1000 * 1000);
LoRa.setSignalBandwidth(Lora->settings.bandwidth * 1000);
LoRa.setSpreadingFactor(Lora->settings.spreading_factor);
LoRa.setCodingRate4(Lora->settings.coding_rate);
LoRa.setSyncWord(Lora->settings.sync_word);
LoRa.setTxPower(Lora->settings.output_power);
LoRa.setPreambleLength(Lora->settings.preamble_length);
LoRa.setOCP(Lora->settings.current_limit);
if (Lora->settings.crc_bytes) {
LoRa.enableCrc();
} else {
LoRa.disableCrc();
}
if (full) {
LoRa.setCodingRate4(Lora->settings.coding_rate);
LoRa.setSyncWord(Lora->settings.sync_word);
LoRa.setTxPower(Lora->settings.output_power);
LoRa.setPreambleLength(Lora->settings.preamble_length);
LoRa.setOCP(Lora->settings.current_limit);
if (Lora->settings.crc_bytes) {
LoRa.enableCrc();
} else {
LoRa.disableCrc();
}
/*
if (Lora->settings.implicit_header) {
LoRa.implicitHeaderMode();
} else {
LoRa.explicitHeaderMode();
}
if (Lora->settings.implicit_header) {
LoRa.implicitHeaderMode();
} else {
LoRa.explicitHeaderMode();
}
*/
LoRa.disableInvertIQ(); // normal mode
LoRa.disableInvertIQ(); // normal mode
}
return true;
}
bool LoraSx127xInit(void) {
LoRa.setPins(Pin(GPIO_LORA_CS), Pin(GPIO_LORA_RST), Pin(GPIO_LORA_DI0));
if (LoRa.begin(Lora->settings.frequency * 1000 * 1000)) {
LoraSx127xConfig();
LoraSx127xConfig(true);
LoRa.onReceive(LoraSx127xOnReceive);
LoRa.receive();
return true;

View File

@ -53,25 +53,27 @@ void LoraWanPublishFooter(uint32_t node) {
InfluxDbProcess(1); // Use a copy of ResponseData
#endif
if (Settings->flag4.zigbee_distinct_topics) { // SetOption89 - (MQTT, Zigbee) Distinct MQTT topics per device for Zigbee (1) (#7835)
char subtopic[TOPSZ];
// Clean special characters
char stemp[TOPSZ];
strlcpy(stemp, Lora->settings.end_node[node].name.c_str(), sizeof(stemp));
MakeValidMqtt(0, stemp);
if (Settings->flag5.zigbee_hide_bridge_topic) { // SetOption125 - (Zigbee) Hide bridge topic from zigbee topic (use with SetOption89) (1)
snprintf_P(subtopic, sizeof(subtopic), PSTR("%s"), stemp);
if (!Settings->flag6.mqtt_disable_publish) { // SetOption147 - If it is activated, Tasmota will not publish MQTT messages, but it will proccess event trigger rules
if (Settings->flag4.zigbee_distinct_topics) { // SetOption89 - (MQTT, Zigbee) Distinct MQTT topics per device for Zigbee (1) (#7835)
char subtopic[TOPSZ];
// Clean special characters
char stemp[TOPSZ];
strlcpy(stemp, Lora->settings.end_node[node].name.c_str(), sizeof(stemp));
MakeValidMqtt(0, stemp);
if (Settings->flag5.zigbee_hide_bridge_topic) { // SetOption125 - (Zigbee) Hide bridge topic from zigbee topic (use with SetOption89) (1)
snprintf_P(subtopic, sizeof(subtopic), PSTR("%s"), stemp);
} else {
snprintf_P(subtopic, sizeof(subtopic), PSTR("%s/%s"), TasmotaGlobal.mqtt_topic, stemp);
}
char stopic[TOPSZ];
if (Settings->flag5.zb_received_as_subtopic) // SetOption118 - (Zigbee) Move LwReceived from JSON message and into the subtopic replacing "SENSOR" default
GetTopic_P(stopic, TELE, subtopic, PSTR("LwReceived"));
else
GetTopic_P(stopic, TELE, subtopic, PSTR(D_RSLT_SENSOR));
MqttPublish(stopic, Settings->flag.mqtt_sensor_retain);
} else {
snprintf_P(subtopic, sizeof(subtopic), PSTR("%s/%s"), TasmotaGlobal.mqtt_topic, stemp);
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings->flag.mqtt_sensor_retain);
}
char stopic[TOPSZ];
if (Settings->flag5.zb_received_as_subtopic) // SetOption118 - (Zigbee) Move LwReceived from JSON message and into the subtopic replacing "SENSOR" default
GetTopic_P(stopic, TELE, subtopic, PSTR("LwReceived"));
else
GetTopic_P(stopic, TELE, subtopic, PSTR(D_RSLT_SENSOR));
MqttPublish(stopic, Settings->flag.mqtt_sensor_retain);
} else {
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings->flag.mqtt_sensor_retain);
}
XdrvRulesProcess(0); // Apply rules
}

View File

@ -187,7 +187,7 @@ uint32_t LoraWanFrequencyToChannel(void) {
}
if (250 == channel) {
Lora->settings.frequency = 868.1;
Lora->Config();
Lora->Config(false);
channel = 0;
}
}
@ -309,33 +309,50 @@ void LoraWanRadioInfo(uint8_t mode, void* pInfo, uint32_t uChannel = 0) {
break;
}
default: { // TAS_LORA_REGION_EU868
//Default TX/RX1/TX1 same
(*pResult).frequency = TAS_LORAWAN_FREQUENCY;
(*pResult).bandwidth = TAS_LORAWAN_BANDWIDTH;
(*pResult).spreading_factor = TAS_LORAWAN_SPREADING_FACTOR;
switch (mode) {
case TAS_LORAWAN_RADIO_UPLINK: {
(*pResult).frequency = TAS_LORAWAN_FREQUENCY;
(*pResult).bandwidth = TAS_LORAWAN_BANDWIDTH;
(*pResult).spreading_factor = TAS_LORAWAN_SPREADING_FACTOR;
break;
}
case TAS_LORAWAN_RADIO_RX1: {
// RX1 DR depends on the Uplink settings
// https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf
// Page 19
(*pResult).frequency = TAS_LORAWAN_FREQUENCY;
(*pResult).bandwidth = TAS_LORAWAN_BANDWIDTH_RX1;
(*pResult).spreading_factor = TAS_LORAWAN_SPREADING_FACTOR_RX1;
break;
}
case TAS_LORAWAN_RADIO_RX2: {
(*pResult).frequency = TAS_LORAWAN_FREQUENCY_DN;
(*pResult).bandwidth = TAS_LORAWAN_BANDWIDTH_RX2;
(*pResult).spreading_factor = TAS_LORAWAN_SPREADING_FACTOR_RX2;
break;
}
// default:
// not implemented
}
}
}
}
bool LoraWanDefaults(uint32_t region = TAS_LORA_REGION_EU868, LoRaWanRadioMode_t mode = TAS_LORAWAN_RADIO_UPLINK) {
bool multi_profile = false;
bool LoraWanProfile(uint32_t mode) {
LoRaWanRadioInfo_t RadioInfo;
LoraWanRadioInfo(mode, &RadioInfo, LoraWanFrequencyToChannel()); // Region specific
bool changed = ((Lora->settings.frequency != RadioInfo.frequency) ||
(Lora->settings.bandwidth != RadioInfo.bandwidth) ||
(Lora->settings.spreading_factor != RadioInfo.spreading_factor));
Lora->settings.frequency = RadioInfo.frequency;
Lora->settings.bandwidth = RadioInfo.bandwidth;
Lora->settings.spreading_factor = RadioInfo.spreading_factor;
return changed;
}
void LoraWanDefaults(uint32_t region) {
Lora->settings.region = region;
switch (region) {
case TAS_LORA_REGION_AU915:
// TO DO: Need 3 profiles: Uplink, RX1, RX2
// Works OK for now as RX2 always received by end device.
multi_profile = true;
LoRaWanRadioInfo_t RadioInfo;
LoraWanRadioInfo(mode, &RadioInfo, LoraWanFrequencyToChannel()); // Region specific
Lora->settings.frequency = RadioInfo.frequency;
Lora->settings.bandwidth = RadioInfo.bandwidth;
Lora->settings.spreading_factor = RadioInfo.spreading_factor;
break;
default: // TAS_LORA_REGION_EU868
Lora->settings.frequency = TAS_LORAWAN_FREQUENCY;
Lora->settings.bandwidth = TAS_LORAWAN_BANDWIDTH;
Lora->settings.spreading_factor = TAS_LORAWAN_SPREADING_FACTOR;
}
LoraWanProfile(TAS_LORAWAN_RADIO_UPLINK);
Lora->settings.coding_rate = TAS_LORAWAN_CODING_RATE;
Lora->settings.sync_word = TAS_LORAWAN_SYNC_WORD;
Lora->settings.output_power = TAS_LORAWAN_OUTPUT_POWER;
@ -343,45 +360,51 @@ bool LoraWanDefaults(uint32_t region = TAS_LORA_REGION_EU868, LoRaWanRadioMode_t
Lora->settings.current_limit = TAS_LORAWAN_CURRENT_LIMIT;
Lora->settings.implicit_header = TAS_LORAWAN_HEADER;
Lora->settings.crc_bytes = TAS_LORAWAN_CRC_BYTES;
return multi_profile;
}
/*********************************************************************************************/
#include <Ticker.h>
Ticker LoraWan_Send;
Ticker LoraWan_Send_RX1;
Ticker LoraWan_Send_RX2;
void LoraWanSend(uint8_t* data, uint32_t len, bool uplink_profile) {
LoraSettings_t RXsettings;
if (uplink_profile) {
// Different TX/RX profiles allowed. (e.g. LoRaWAN)
// For CmndLoraSend() ... do not allow changes.
RXsettings = Lora->settings; // Make a copy;
LoraWanDefaults(Lora->settings.region, TAS_LORAWAN_RADIO_RX2); // Set Downlink profile TO DO: Support different RX1 & RX2 profiles
Lora->Config();
}
LoraSend(data, len, true);
if (uplink_profile) {
Lora->settings = RXsettings; // Restore copy
Lora->Config();
void LoRaWanSend(void) {
if (!Lora->send_request) { return; }
Lora->send_request = false;
LoraSend(Lora->send_buffer, Lora->send_buffer_len, true);
if (Lora->profile_changed) {
Lora->settings = Lora->backup_settings; // Restore copy for reception
if (0 == Lora->send_buffer_step) {
Lora->Init(); // Necessary to re-init the SXxxxx chip in cases where TX/RX frequencies differ
} else {
Lora->Config(false);
}
}
}
void LoraWanTickerSend(void) {
// Need to stay here as short as possible to not initiate watchdog exception
// As SF12 can take over 1 sec do send in loop instead of here
Lora->send_buffer_step--;
if (1 == Lora->send_buffer_step) {
Lora->rx = true; // Always send during RX1
Lora->receive_time = 0; // Reset receive timer
LoraWan_Send.once_ms(TAS_LORAWAN_RECEIVE_DELAY2, LoraWanTickerSend); // Retry after 1000 ms
}
bool uplink_profile = (Lora->settings.region == TAS_LORA_REGION_AU915);
if (Lora->rx) { // If received in RX1 do not resend in RX2
LoraWanSend(Lora->send_buffer, Lora->send_buffer_len, uplink_profile);
}
if (uplink_profile && (0 == Lora->send_buffer_step)) {
Lora->Init(); // Necessary to re-init the SXxxxx chip in cases where TX/RX frequencies differ
if (1 == Lora->send_buffer_step) {
Lora->profile_changed = LoraWanProfile(TAS_LORAWAN_RADIO_RX1); // Set Downlink RX1 profile
} else {
Lora->profile_changed = LoraWanProfile(TAS_LORAWAN_RADIO_RX2); // Set Downlink RX2 profile
}
if (Lora->profile_changed) {
Lora->Config(false);
}
Lora->send_request = true; // Send in loop fixing watchdogs
#ifdef ESP8266
SleepSkip(); // Skip sleep
#else // ESP32
LoRaWanSend();
#endif // ESP8266/ESP32
}
}
@ -391,8 +414,19 @@ void LoraWanSendResponse(uint8_t* buffer, size_t len, uint32_t lorawan_delay) {
if (nullptr == Lora->send_buffer) { return; }
memcpy(Lora->send_buffer, buffer, len);
Lora->send_buffer_len = len;
Lora->send_request = false;
Lora->backup_settings = Lora->settings; // Make a copy;
Lora->send_buffer_step = 2; // Send at RX1 and RX2
LoraWan_Send.once_ms(lorawan_delay - TimePassedSince(Lora->receive_time), LoraWanTickerSend);
uint32_t delay_rx1 = lorawan_delay - TimePassedSince(Lora->receive_time);
LoraWan_Send_RX1.once_ms(delay_rx1, LoraWanTickerSend);
uint32_t delay_rx2 = delay_rx1 + TAS_LORAWAN_RECEIVE_DELAY2;
LoraWan_Send_RX2.once_ms(delay_rx2, LoraWanTickerSend); // Retry after 1000 ms
#ifdef USE_LORA_DEBUG
AddLog(LOG_LEVEL_DEBUG, PSTR("LOR: About to send '%*_H' in %d and (optional) %d ms"),
Lora->send_buffer_len, Lora->send_buffer, delay_rx1, delay_rx2);
#endif // USE_LORA_DEBUG
}
/*-------------------------------------------------------------------------------------------*/
@ -400,12 +434,12 @@ void LoraWanSendResponse(uint8_t* buffer, size_t len, uint32_t lorawan_delay) {
size_t LoraWanCFList(uint8_t * CFList, size_t uLen) {
// Populates CFList for use in JOIN-ACCEPT message to lock device to specific frequencies
// Returns: Number of bytes added
if (uLen < 16) return 0;
uint8_t idx = 0;
switch (Lora->settings.region) {
case TAS_LORA_REGION_AU915: {
if (uLen < 16) return 0;
uint8_t uChannel = LoraWanFrequencyToChannel(); // 0..71
uint8_t uMaskByte = uChannel /8; // 0..8
@ -415,17 +449,28 @@ size_t LoraWanCFList(uint8_t * CFList, size_t uLen) {
}
// Add next 6 bytes
CFList[idx++] = 0x00; //RFU
CFList[idx++] = 0x00; // RFU
CFList[idx++] = 0x00;
CFList[idx++] = 0x00; //RFU
CFList[idx++] = 0x00; // RFU
CFList[idx++] = 0x00;
CFList[idx++] = 0x00;
CFList[idx++] = 0x01; //CFListType
CFList[idx++] = 0x01; // CFListType
break;
}
default: { // TAS_LORA_REGION_EU868
/*
uint32_t frequency = (Lora->settings.frequency * 10000); // 868.1 -> 8681000
// Add first 15 bytes
for (uint32_t i = 0; i < 5; i++) {
CFList[idx++] = frequency;
CFList[idx++] = frequency >> 8;
CFList[idx++] = frequency >> 16;
}
CFList[idx++] = 0x00; // CFListType
*/
}
}
return idx;
@ -551,8 +596,9 @@ bool LoraWanInput(uint8_t* data, uint32_t packet_size) {
uint32_t MType = data[0] >> 5; // Upper three bits (used to be called FType)
if (TAS_LORAWAN_MTYPE_JOIN_REQUEST == MType) {
// 0007010000004140A8D64A89710E4140A82893A8AD137F - Dragino
// 000600000000161600B51F000000161600FDA5D8127912 - MerryIoT
// 0007010000004140A8D64A89710E4140A82893A8AD137F - Dragino LDS02
// 0000010000004140A889AD5C17544140A849E1B2CAC27B - Dragino LHT52
// 000600000000161600B51F000000161600FDA5D8127912 - MerryIoT DW10
uint64_t JoinEUI = (uint64_t)data[ 1] | ((uint64_t)data[ 2] << 8) |
((uint64_t)data[ 3] << 16) | ((uint64_t)data[ 4] << 24) |
((uint64_t)data[ 5] << 32) | ((uint64_t)data[ 6] << 40) |
@ -569,8 +615,8 @@ bool LoraWanInput(uint8_t* data, uint32_t packet_size) {
uint32_t CalcMIC = LoraWanGenerateMIC(data, 19, Lora->settings.end_node[node].AppKey);
if (MIC == CalcMIC) { // Valid MIC based on LoraWanAppKey
AddLog(LOG_LEVEL_DEBUG, PSTR("LOR: JoinEUI %8_H, DevEUIh %08X, DevEUIl %08X, DevNonce %04X, MIC %08X"),
(uint8_t*)&JoinEUI, DevEUIh, DevEUIl, DevNonce, MIC);
AddLog(LOG_LEVEL_DEBUG, PSTR("LOR: Node %d, JoinEUI %8_H, DevEUIh %08X, DevEUIl %08X, DevNonce %04X, MIC %08X"),
node +1, (uint8_t*)&JoinEUI, DevEUIh, DevEUIl, DevNonce, MIC);
Lora->settings.end_node[node].DevEUIl = DevEUIl;
Lora->settings.end_node[node].DevEUIh = DevEUIh;
@ -583,6 +629,7 @@ bool LoraWanInput(uint8_t* data, uint32_t packet_size) {
ext_snprintf_P(name, sizeof(name), PSTR("0x%04X"), Lora->settings.end_node[node].DevEUIl & 0x0000FFFF);
Lora->settings.end_node[node].name = name;
}
uint32_t JoinNonce = TAS_LORAWAN_JOINNONCE +node;
uint32_t DevAddr = Lora->device_address +node;
uint32_t NetID = TAS_LORAWAN_NETID;
@ -621,8 +668,9 @@ bool LoraWanInput(uint8_t* data, uint32_t packet_size) {
EncData[0] = join_data[0];
LoraWanEncryptJoinAccept(Lora->settings.end_node[node].AppKey, &join_data[1], join_data_index-1, &EncData[1]);
// 203106E5000000412E010003017CB31DD4 - Dragino
// 203206E5000000422E010003016A210EEA - MerryIoT
// 203106E5000000412E010003017CB31DD4 - Dragino LDS02
// 2026B4E06C390AFA1B166D465987F31EC4 - Dragino LHT52
// 203206E5000000422E010003016A210EEA - MerryIoT DW10
LoraWanSendResponse(EncData, join_data_index, TAS_LORAWAN_JOIN_ACCEPT_DELAY1);
result = true;

View File

@ -46,7 +46,7 @@ void LoraSettings2Json(bool show = false) {
ResponseAppend_P(PSTR("\"" D_JSON_REGION "\":%d"), Lora->settings.region); // enum 0 = EU868, 1 = AU915
}
#ifdef USE_LORAWAN_BRIDGE
if (show && (Lora->settings.region == TAS_LORA_REGION_AU915)) {
if (show && (bitRead(Lora->settings.flags, TAS_LORA_FLAG_BRIDGE_ENABLED))) {
LoRaWanRadioInfo_t Rx1Info;
LoraWanRadioInfo(TAS_LORAWAN_RADIO_RX1, &Rx1Info, LoraWanFrequencyToChannel()); // Get Rx1Info with values used for RX1 transmit window. (Region specific, calculated from Uplink radio settings)
LoRaWanRadioInfo_t Rx2Info;
@ -193,8 +193,8 @@ void LoraSettingsSave(void) {
bool LoraSend(uint8_t* data, uint32_t len, bool invert) {
uint32_t lora_time = millis(); // Time is important for LoRaWan RX windows
bool result = Lora->Send(data, len, invert);
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("LOR: Send (%u) '%*_H', Invert %d, Time %d"),
lora_time, len, data, invert, TimePassedSince(lora_time));
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("LOR: Send (%u) '%*_H', Invert %d, Freq %1_f, BW %1_f, SF %d, Time %d"),
lora_time, len, data, invert, &Lora->settings.frequency, &Lora->settings.bandwidth, Lora->settings.spreading_factor, TimePassedSince(lora_time));
return result;
}
@ -204,8 +204,8 @@ void LoraInput(void) {
char data[TAS_LORA_MAX_PACKET_LENGTH] = { 0 };
int packet_size = Lora->Receive(data);
if (!packet_size) { return; }
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("LOR: Rcvd (%u) '%*_H', RSSI %1_f, SNR %1_f"),
Lora->receive_time, packet_size, data, &Lora->rssi, &Lora->snr);
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("LOR: Rcvd (%u) '%*_H', Freq %1_f, BW %1_f, SF %d, RSSI %1_f, SNR %1_f"),
Lora->receive_time, packet_size, data, &Lora->settings.frequency, &Lora->settings.bandwidth, Lora->settings.spreading_factor, &Lora->rssi, &Lora->snr);
#ifdef USE_LORAWAN_BRIDGE
if (bitRead(Lora->settings.flags, TAS_LORA_FLAG_BRIDGE_ENABLED)) {
if (LoraWanInput((uint8_t*)data, packet_size)) {
@ -457,9 +457,9 @@ void CmndLoraConfig(void) {
LoraDefaults(region); // Default region LoRa values
break;
#ifdef USE_LORAWAN_BRIDGE
case 2:
case 2: {
switch (region) {
case TAS_LORA_REGION_AU915:
case TAS_LORA_REGION_AU915: {
uint32_t parm[2] = { 0, 0 };
ParseParameters(2, parm); // parm[1] will hold channel
Lora->settings.region = region; // Need valid region for LoraWanRadioInfo()
@ -467,21 +467,23 @@ void CmndLoraConfig(void) {
LoraWanRadioInfo(TAS_LORAWAN_RADIO_UPLINK, &UpInfo, parm[1]); // Set uplink frequency based on channel
Lora->settings.frequency = UpInfo.frequency;
break;
// default:
// not implemented
}
default: // TAS_LORA_REGION_EU868
Lora->settings.frequency = TAS_LORAWAN_FREQUENCY;
}
LoraWanDefaults(region); // Default region LoRaWan values
break;
}
#endif // USE_LORAWAN_BRIDGE
}
Lora->Config();
Lora->Config(true);
}
else {
JsonParser parser(XdrvMailbox.data);
JsonParserObject root = parser.getRootObject();
if (root) {
LoraJson2Settings(root);
Lora->Config();
Lora->Config(true);
}
}
uint8_t data[1] = { 0 };
@ -507,6 +509,9 @@ bool Xdrv73(uint32_t function) {
switch (function) {
case FUNC_LOOP:
case FUNC_SLEEP_LOOP:
#ifdef USE_LORAWAN_BRIDGE
LoRaWanSend();
#endif // USE_LORAWAN_BRIDGE
LoraInput();
break;
case FUNC_RESET_SETTINGS: