mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-24 03:06:33 +00:00
Add Support for LoRaWan Rx1 and Rx2 profiles (#23394)
This commit is contained in:
parent
08e8f0b64d
commit
7fb8654c6c
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user