mirror of https://github.com/arendst/Tasmota.git
Refactor lorawan
This commit is contained in:
parent
ae7bed5acf
commit
894e46cb5a
|
@ -100,8 +100,6 @@ void DrvDemoSettingsLoad(bool erase) {
|
|||
// Called from FUNC_RESET_SETTINGS (erase = 1) after command reset 4, 5, or 6
|
||||
|
||||
// *** Start init default values in case key is not found ***
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("DRV: " D_USE_DEFAULTS));
|
||||
|
||||
memset(&DrvDemoSettings, 0x00, sizeof(DrvDemoSettings));
|
||||
// Init any other parameter in struct DrvDemoSettings
|
||||
snprintf_P(DrvDemoSettings.drv_text[0], sizeof(DrvDemoSettings.drv_text[0]), PSTR("Azalea"));
|
||||
|
@ -120,7 +118,7 @@ void DrvDemoSettingsLoad(bool erase) {
|
|||
}
|
||||
else {
|
||||
// File system not ready: No flash space reserved for file system
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("CFG: Demo use defaults as file system not ready or key not found"));
|
||||
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("CFG: Demo use defaults as file system not ready or key not found"));
|
||||
}
|
||||
#endif // USE_UFILESYS
|
||||
}
|
||||
|
@ -137,7 +135,7 @@ void DrvDemoSettingsSave(void) {
|
|||
AddLog(LOG_LEVEL_DEBUG, PSTR("CFG: Demo saved to file"));
|
||||
} else {
|
||||
// File system not ready: No flash space reserved for file system
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("CFG: ERROR Demo file system not ready or unable to save file"));
|
||||
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("CFG: ERROR Demo file system not ready or unable to save file"));
|
||||
}
|
||||
}
|
||||
#endif // USE_UFILESYS
|
||||
|
|
|
@ -94,15 +94,67 @@
|
|||
#define TAS_LORAWAN_ENDNODES 4 // Max number od supported endnodes
|
||||
#define TAS_LORAWAN_AES128_KEY_SIZE 16 // Size in bytes
|
||||
|
||||
enum TasLoraFlags { TAS_LORAWAN_BRIDGE_ENABLED,
|
||||
TAS_LORAWAN_JOIN_ENABLED,
|
||||
TAS_LORAWAN_DECODE_ENABLED
|
||||
};
|
||||
enum TasLoraFlags {
|
||||
TAS_LORA_FLAG_BRIDGE_ENABLED,
|
||||
TAS_LORA_FLAG_JOIN_ENABLED,
|
||||
TAS_LORA_FLAG_DECODE_ENABLED
|
||||
};
|
||||
|
||||
enum TasLoraWanFlags { TAS_LORAWAN_LINK_ADR_REQ
|
||||
};
|
||||
enum TasLoraWanFlags {
|
||||
TAS_LORAWAN_FLAG_LINK_ADR_REQ
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
enum TasLoraWanMTypes {
|
||||
TAS_LORAWAN_MTYPE_JOIN_REQUEST,
|
||||
TAS_LORAWAN_MTYPE_JOIN_ACCEPT,
|
||||
TAS_LORAWAN_MTYPE_UNCONFIRMED_DATA_UPLINK,
|
||||
TAS_LORAWAN_MTYPE_UNCONFIRMED_DATA_DOWNLINK,
|
||||
TAS_LORAWAN_MTYPE_CONFIRMED_DATA_UPLINK,
|
||||
TAS_LORAWAN_MTYPE_CONFIRMED_DATA_DOWNLINK,
|
||||
TAS_LORAWAN_MTYPE_RFU,
|
||||
TAS_LORAWAN_MTYPE_PROPRIETARY
|
||||
};
|
||||
|
||||
enum TasLoraWanCIDNtwk {
|
||||
TAS_LORAWAN_CID_LINK_CHECK_ANS = 0x02,
|
||||
TAS_LORAWAN_CID_LINK_ADR_REQ,
|
||||
TAS_LORAWAN_CID_DUTY_CYCLE_REQ,
|
||||
TAS_LORAWAN_CID_RX_PARAM_SETUP_REQ,
|
||||
TAS_LORAWAN_CID_DEV_STATUS_REQ,
|
||||
TAS_LORAWAN_CID_NEW_CHANNEL_REQ,
|
||||
TAS_LORAWAN_CID_RX_TIMING_SETUP_REQ,
|
||||
TAS_LORAWAN_CID_TX_PARAM_SETUP_REQ,
|
||||
TAS_LORAWAN_CID_DL_CHANNEL_REQ,
|
||||
TAS_LORAWAN_CID_RFU1_REQ,
|
||||
TAS_LORAWAN_CID_RFU2_REQ,
|
||||
TAS_LORAWAN_CID_DEVICE_TIME_ANS
|
||||
};
|
||||
|
||||
enum TasLoraWanCIDNode {
|
||||
TAS_LORAWAN_CID_LINK_CHECK_REQ = 0x02,
|
||||
TAS_LORAWAN_CID_LINK_ADR_ANS,
|
||||
TAS_LORAWAN_CID_DUTY_CYCLE_ANS,
|
||||
TAS_LORAWAN_CID_RX_PARAM_SETUP_ANS,
|
||||
TAS_LORAWAN_CID_DEV_STATUS_ANS,
|
||||
TAS_LORAWAN_CID_NEW_CHANNEL_ANS,
|
||||
TAS_LORAWAN_CID_RX_TIMING_SETUP_ANS,
|
||||
TAS_LORAWAN_CID_TX_PARAM_SETUP_ANS,
|
||||
TAS_LORAWAN_CID_DL_CHANNEL_ANS,
|
||||
TAS_LORAWAN_CID_RFU1_ANS,
|
||||
TAS_LORAWAN_CID_RFU2_ANS,
|
||||
TAS_LORAWAN_CID_DEVICE_TIME_REQ
|
||||
};
|
||||
|
||||
typedef struct LoraNodeData_t {
|
||||
float rssi;
|
||||
float snr;
|
||||
uint8_t* payload;
|
||||
uint8_t payload_len;
|
||||
uint8_t node;
|
||||
uint8_t FPort;
|
||||
} LoraNodeData_t;
|
||||
|
||||
typedef struct LoraEndNode_t {
|
||||
uint32_t DevEUIh;
|
||||
uint32_t DevEUIl;
|
||||
uint32_t FCntUp;
|
||||
|
@ -111,7 +163,7 @@ typedef struct {
|
|||
uint16_t DevNonce;
|
||||
uint16_t flags;
|
||||
uint8_t AppKey[TAS_LORAWAN_AES128_KEY_SIZE];
|
||||
} tEndNode;
|
||||
} LoraEndNode_t;
|
||||
|
||||
// Global structure containing driver saved variables
|
||||
struct {
|
||||
|
@ -128,7 +180,7 @@ struct {
|
|||
uint8_t crc_bytes; // 2 bytes
|
||||
uint8_t flags;
|
||||
#ifdef USE_LORAWAN_BRIDGE
|
||||
tEndNode end_node[TAS_LORAWAN_ENDNODES]; // End node parameters
|
||||
LoraEndNode_t end_node[TAS_LORAWAN_ENDNODES]; // End node parameters
|
||||
#endif // USE_LORAWAN_BRIDGE
|
||||
} LoraSettings;
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* - LilyGo T3S3 LoRa32 868MHz ESP32S3 (uses SX1262)
|
||||
* - LilyGo TTGO T-Weigh ESP32 LoRa 868MHz HX711 (uses SX1262)
|
||||
* - Heltec (CubeCell) (uses SX1262)
|
||||
* - Waveshare
|
||||
* - Waveshare SX1262 Lora Node (HF) and (LF)
|
||||
*
|
||||
* Used GPIO's:
|
||||
* - SPI_CLK
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
xdrv_73_6_lorawan_decode.ino - LoRaWan node decode support for Tasmota
|
||||
|
||||
SPDX-FileCopyrightText: 2024 Theo Arends
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
#ifdef USE_SPI_LORA
|
||||
#ifdef USE_LORAWAN_BRIDGE
|
||||
/*********************************************************************************************\
|
||||
* LoRaWan node decode and presentation
|
||||
\*********************************************************************************************/
|
||||
|
||||
void LoraWanPublishHeader(uint32_t node) {
|
||||
ResponseClear(); // clear string
|
||||
|
||||
// Do we prefix with `LwReceived`?
|
||||
if (!Settings->flag4.remove_zbreceived && // SetOption100 - (Zigbee) Remove LwReceived form JSON message (1)
|
||||
!Settings->flag5.zb_received_as_subtopic) { // SetOption118 - (Zigbee) Move LwReceived from JSON message and into the subtopic replacing "SENSOR" default
|
||||
if (Settings->flag5.zigbee_include_time && // SetOption144 - (Zigbee) Include time in `LwReceived` messages like other sensors
|
||||
(Rtc.utc_time >= START_VALID_TIME)) {
|
||||
// Add time if needed (and if time is valid)
|
||||
ResponseAppendTimeFormat(Settings->flag2.time_format); // CMND_TIME
|
||||
ResponseAppend_P(PSTR(",\"LwReceived\":"));
|
||||
} else {
|
||||
ResponseAppend_P(PSTR("{\"LwReceived\":"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!Settings->flag5.zb_omit_json_addr) { // SetOption119 - (Zigbee) Remove the device addr from json payload, can be used with zb_topic_fname where the addr is already known from the topic
|
||||
ResponseAppend_P(PSTR("{\"%s\":"), EscapeJSONString(LoraSettings.end_node[node].name.c_str()).c_str());
|
||||
}
|
||||
ResponseAppend_P(PSTR("{\"Node\":%d,\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""), node +1, LoraSettings.end_node[node].DevEUIl & 0x0000FFFF);
|
||||
if (!LoraSettings.end_node[node].name.startsWith(F("0x"))) {
|
||||
ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""), EscapeJSONString(LoraSettings.end_node[node].name.c_str()).c_str());
|
||||
}
|
||||
ResponseAppend_P(PSTR(",\"RSSI\":%1_f,\"SNR\":%1_f"), &Lora.rssi, &Lora.snr);
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
void LoraWanPublishFooter(uint32_t node) {
|
||||
if (!Settings->flag5.zb_omit_json_addr) { // SetOption119 - (Zigbee) Remove the device addr from json payload, can be used with zb_topic_fname where the addr is already known from the topic
|
||||
ResponseAppend_P(PSTR("}"));
|
||||
}
|
||||
if (!Settings->flag4.remove_zbreceived && // SetOption100 - (Zigbee) Remove LwReceived form JSON message (1)
|
||||
!Settings->flag5.zb_received_as_subtopic) { // SetOption118 - (Zigbee) Move LwReceived from JSON message and into the subtopic replacing "SENSOR" default
|
||||
ResponseAppend_P(PSTR("}"));
|
||||
}
|
||||
|
||||
#ifdef USE_INFLUXDB
|
||||
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, LoraSettings.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 {
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings->flag.mqtt_sensor_retain);
|
||||
}
|
||||
XdrvRulesProcess(0); // Apply rules
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
void LoraWanDecode(struct LoraNodeData_t* node_data) {
|
||||
/* Enter with LoraNodeData_t
|
||||
float rssi;
|
||||
float snr;
|
||||
uint8_t* payload;
|
||||
uint8_t payload_len;
|
||||
uint8_t node;
|
||||
uint8_t FPort;
|
||||
*/
|
||||
if (bitRead(LoraSettings.flags, TAS_LORA_FLAG_DECODE_ENABLED)) { // LoraOption3 1
|
||||
if (0x00161600 == LoraSettings.end_node[node_data->node].DevEUIh) { // MerryIoT
|
||||
if (120 == node_data->FPort) { // MerryIoT door/window Sensor (DW10)
|
||||
if (9 == node_data->payload_len) { // MerryIoT Sensor state
|
||||
// 1 2 3 4 5 6 7 8 9
|
||||
// 03 0F 19 2C 8A00 040000 - button
|
||||
// 00 0F 19 2C 0000 050000 - door
|
||||
uint8_t status = node_data->payload[0];
|
||||
float battery_volt = (float)(21 + node_data->payload[1]) / 10.0;
|
||||
int temperature = node_data->payload[2];
|
||||
int humidity = node_data->payload[3];
|
||||
uint32_t elapsed_time = node_data->payload[4] | (node_data->payload[5] << 8);
|
||||
uint32_t events = node_data->payload[6] | (node_data->payload[7] << 8) | (node_data->payload[8] << 16);
|
||||
#ifdef USE_LORA_DEBUG
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("LOR: Node %d, DevEUI %08X%08X, Events %d, LastEvent %d min, DoorOpen %d, Button %d, Tamper %d, Tilt %d, Battery %1_fV, Temp %d, Hum %d"),
|
||||
node_data->node +1, LoraSettings.end_node[node_data->node].DevEUIh, LoraSettings.end_node[node_data->node].DevEUIl,
|
||||
events, elapsed_time,
|
||||
bitRead(status, 0), bitRead(status, 1), bitRead(status, 2), bitRead(status, 3),
|
||||
&battery_volt,
|
||||
temperature, humidity);
|
||||
#endif // USE_LORA_DEBUG
|
||||
LoraWanPublishHeader(node_data->node);
|
||||
ResponseAppend_P(PSTR(",\"Events\":%d,\"LastEvent\":%d,\"DoorOpen\":%d,\"Button\":%d,\"Tamper\":%d,\"Tilt\":%d"
|
||||
",\"Battery\":%1_f,"),
|
||||
events, elapsed_time,
|
||||
bitRead(status, 0), bitRead(status, 1), bitRead(status, 2), bitRead(status, 3),
|
||||
&battery_volt);
|
||||
ResponseAppendTHD(temperature, humidity);
|
||||
ResponseAppend_P(PSTR("}"));
|
||||
LoraWanPublishFooter(node_data->node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (0xA840410E == LoraSettings.end_node[node_data->node].DevEUIh) { // Dragino
|
||||
if (10 == node_data->FPort) { // Dragino LDS02
|
||||
// 8CD2 01 000010 000000 00 - Door Open, 3.282V
|
||||
// 0CD2 01 000011 000000 00 - Door Closed
|
||||
uint8_t status = node_data->payload[0];
|
||||
float battery_volt = (float)((node_data->payload[1] | (node_data->payload[0] << 8)) &0x3FFF) / 1000;
|
||||
uint8_t MOD = node_data->payload[2]; // Always 0x01
|
||||
uint32_t events = node_data->payload[5] | (node_data->payload[4] << 8) | (node_data->payload[3] << 16);
|
||||
uint32_t open_duration = node_data->payload[8] | (node_data->payload[7] << 8) | (node_data->payload[6] << 16);
|
||||
uint8_t alarm = node_data->payload[9];
|
||||
#ifdef USE_LORA_DEBUG
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("LOR: Node %d, DevEUI %08X%08X, Events %d, LastEvent %d min, DoorOpen %d, Battery %3_fV, Alarm %d"),
|
||||
node_data->node +1, LoraSettings.end_node[node_data->node].DevEUIh, LoraSettings.end_node[node_data->node].DevEUIl,
|
||||
events, open_duration,
|
||||
bitRead(status, 7),
|
||||
&battery_volt,
|
||||
bitRead(alarm, 0));
|
||||
#endif // USE_LORA_DEBUG
|
||||
LoraWanPublishHeader(node_data->node);
|
||||
ResponseAppend_P(PSTR(",\"Events\":%d,\"LastEvent\":%d,\"DoorOpen\":%d,\"Alarm\":%d,\"Battery\":%3_f}"),
|
||||
events, open_duration, bitRead(status, 7), bitRead(alarm, 0), &battery_volt);
|
||||
LoraWanPublishFooter(node_data->node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Joined device without decoding
|
||||
LoraWanPublishHeader(node_data->node);
|
||||
ResponseAppend_P(PSTR(",\"DevEUIh\":\"%08X\",\"DevEUIl\":\"%08X\",\"FPort\":%d,\"Payload\":["),
|
||||
LoraSettings.end_node[node_data->node].DevEUIh, LoraSettings.end_node[node_data->node].DevEUIl, node_data->FPort);
|
||||
for (uint32_t i = 0; i < node_data->payload_len; i++) {
|
||||
ResponseAppend_P(PSTR("%s%d"), (0==i)?"":",", node_data->payload[i]);
|
||||
}
|
||||
ResponseAppend_P(PSTR("]}"));
|
||||
LoraWanPublishFooter(node_data->node);
|
||||
}
|
||||
|
||||
#endif // USE_LORAWAN_BRIDGE
|
||||
#endif // USE_SPI_LORA
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
xdrv_73_8_lorawan.ino - LoRaWan EU868 support for Tasmota
|
||||
xdrv_73_8_lorawan_bridge.ino - LoRaWan EU868 support for Tasmota
|
||||
|
||||
SPDX-FileCopyrightText: 2024 Theo Arends
|
||||
|
||||
|
@ -203,90 +203,26 @@ uint32_t LoraWanFrequencyToChannel(void) {
|
|||
|
||||
/*********************************************************************************************/
|
||||
|
||||
void LoraWanPublishHeader(uint32_t node) {
|
||||
ResponseClear(); // clear string
|
||||
|
||||
// Do we prefix with `LwReceived`?
|
||||
if (!Settings->flag4.remove_zbreceived && // SetOption100 - (Zigbee) Remove LwReceived form JSON message (1)
|
||||
!Settings->flag5.zb_received_as_subtopic) { // SetOption118 - (Zigbee) Move LwReceived from JSON message and into the subtopic replacing "SENSOR" default
|
||||
if (Settings->flag5.zigbee_include_time && // SetOption144 - (Zigbee) Include time in `LwReceived` messages like other sensors
|
||||
(Rtc.utc_time >= START_VALID_TIME)) {
|
||||
// Add time if needed (and if time is valid)
|
||||
ResponseAppendTimeFormat(Settings->flag2.time_format); // CMND_TIME
|
||||
ResponseAppend_P(PSTR(",\"LwReceived\":"));
|
||||
} else {
|
||||
ResponseAppend_P(PSTR("{\"LwReceived\":"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!Settings->flag5.zb_omit_json_addr) { // SetOption119 - (Zigbee) Remove the device addr from json payload, can be used with zb_topic_fname where the addr is already known from the topic
|
||||
ResponseAppend_P(PSTR("{\"%s\":"), EscapeJSONString(LoraSettings.end_node[node].name.c_str()).c_str());
|
||||
}
|
||||
ResponseAppend_P(PSTR("{\"Node\":%d,\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""), node +1, LoraSettings.end_node[node].DevEUIl & 0x0000FFFF);
|
||||
if (!LoraSettings.end_node[node].name.startsWith(F("0x"))) {
|
||||
ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""), EscapeJSONString(LoraSettings.end_node[node].name.c_str()).c_str());
|
||||
}
|
||||
ResponseAppend_P(PSTR(",\"RSSI\":%1_f,\"SNR\":%1_f"), &Lora.rssi, &Lora.snr);
|
||||
}
|
||||
|
||||
void LoraWanPublishFooter(uint32_t node) {
|
||||
if (!Settings->flag5.zb_omit_json_addr) { // SetOption119 - (Zigbee) Remove the device addr from json payload, can be used with zb_topic_fname where the addr is already known from the topic
|
||||
ResponseAppend_P(PSTR("}"));
|
||||
}
|
||||
if (!Settings->flag4.remove_zbreceived && // SetOption100 - (Zigbee) Remove LwReceived form JSON message (1)
|
||||
!Settings->flag5.zb_received_as_subtopic) { // SetOption118 - (Zigbee) Move LwReceived from JSON message and into the subtopic replacing "SENSOR" default
|
||||
ResponseAppend_P(PSTR("}"));
|
||||
}
|
||||
|
||||
#ifdef USE_INFLUXDB
|
||||
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, LoraSettings.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 {
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings->flag.mqtt_sensor_retain);
|
||||
}
|
||||
XdrvRulesProcess(0); // apply rules
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
void LoraWanSendLinkADRReq(uint32_t node) {
|
||||
uint32_t DevAddr = Lorawan.device_address +node;
|
||||
uint16_t FCnt = LoraSettings.end_node[node].FCntDown++;
|
||||
uint8_t NwkSKey[TAS_LORAWAN_AES128_KEY_SIZE];
|
||||
LoraWanDeriveLegacyNwkSKey(node, NwkSKey);
|
||||
|
||||
uint8_t data[32];
|
||||
data[0] = 0xA0; // Confirmed data downlink
|
||||
uint8_t data[17];
|
||||
data[0] = TAS_LORAWAN_MTYPE_CONFIRMED_DATA_DOWNLINK << 5;
|
||||
data[1] = DevAddr;
|
||||
data[2] = DevAddr >> 8;
|
||||
data[3] = DevAddr >> 16;
|
||||
data[4] = DevAddr >> 24;
|
||||
data[5] = 0x05; // FCtrl with 5 FOpts
|
||||
data[5] = 0x05; // FCtrl with 5 FOpts
|
||||
data[6] = FCnt;
|
||||
data[7] = FCnt >> 8;
|
||||
data[8] = 0x03; // CId LinkADRReq to single channel LoraFrequency and DR LoraSpreadingFactor
|
||||
data[8] = TAS_LORAWAN_CID_LINK_ADR_REQ;
|
||||
data[9] = LoraWanSpreadingFactorToDataRate() << 4 | 0x0F; // DataRate 3 and unchanged TXPower
|
||||
data[10] = 0x01 << LoraWanFrequencyToChannel();
|
||||
data[10] = 0x01 << LoraWanFrequencyToChannel(); // Single channel
|
||||
data[11] = 0x00;
|
||||
data[12] = 0x00; // ChMaskCntl applies to Channels0..15, NbTrans is default (1)
|
||||
data[12] = 0x00; // ChMaskCntl applies to Channels0..15, NbTrans is default (1)
|
||||
|
||||
uint32_t MIC = LoraWanComputeLegacyDownlinkMIC(NwkSKey, DevAddr, FCnt, data, 13);
|
||||
data[13] = MIC;
|
||||
|
@ -295,14 +231,14 @@ void LoraWanSendLinkADRReq(uint32_t node) {
|
|||
data[16] = MIC >> 24;
|
||||
|
||||
// A0 F3F51700 05 0000 033F010000 0B2C1B8B
|
||||
LoraWanSendResponse(data, 17, TAS_LORAWAN_RECEIVE_DELAY1);
|
||||
LoraWanSendResponse(data, sizeof(data), TAS_LORAWAN_RECEIVE_DELAY1);
|
||||
}
|
||||
|
||||
bool LoraWanInput(uint8_t* data, uint32_t packet_size) {
|
||||
bool result = false;
|
||||
uint32_t MType = data[0] >> 5; // Upper three bits (used to be called FType)
|
||||
|
||||
if (0 == MType) { // Join request
|
||||
if (TAS_LORAWAN_MTYPE_JOIN_REQUEST == MType) {
|
||||
// 0007010000004140A8D64A89710E4140A82893A8AD137F - Dragino
|
||||
// 000600000000161600B51F000000161600FDA5D8127912 - MerryIoT
|
||||
uint64_t JoinEUI = (uint64_t)data[ 1] | ((uint64_t)data[ 2] << 8) |
|
||||
|
@ -329,7 +265,7 @@ bool LoraWanInput(uint8_t* data, uint32_t packet_size) {
|
|||
LoraSettings.end_node[node].DevNonce = DevNonce;
|
||||
LoraSettings.end_node[node].FCntUp = 0;
|
||||
LoraSettings.end_node[node].FCntDown = 0;
|
||||
bitClear(LoraSettings.end_node[node].flags, TAS_LORAWAN_LINK_ADR_REQ);
|
||||
bitClear(LoraSettings.end_node[node].flags, TAS_LORAWAN_FLAG_LINK_ADR_REQ);
|
||||
if (LoraSettings.end_node[node].name.equals(F("0x0000"))) {
|
||||
char name[10];
|
||||
ext_snprintf_P(name, sizeof(name), PSTR("0x%04X"), LoraSettings.end_node[node].DevEUIl & 0x0000FFFF);
|
||||
|
@ -339,8 +275,8 @@ bool LoraWanInput(uint8_t* data, uint32_t packet_size) {
|
|||
uint32_t JoinNonce = TAS_LORAWAN_JOINNONCE +node;
|
||||
uint32_t DevAddr = Lorawan.device_address +node;
|
||||
uint32_t NetID = TAS_LORAWAN_NETID;
|
||||
uint8_t join_data[33] = { 0 };
|
||||
join_data[0] = 0x20; // Join Accept
|
||||
uint8_t join_data[17] = { 0 };
|
||||
join_data[0] = TAS_LORAWAN_MTYPE_JOIN_ACCEPT << 5;
|
||||
join_data[1] = JoinNonce;
|
||||
join_data[2] = JoinNonce >> 8;
|
||||
join_data[3] = JoinNonce >> 16;
|
||||
|
@ -351,32 +287,32 @@ bool LoraWanInput(uint8_t* data, uint32_t packet_size) {
|
|||
join_data[8] = DevAddr >> 8;
|
||||
join_data[9] = DevAddr >> 16;
|
||||
join_data[10] = DevAddr >> 24;
|
||||
join_data[11] = 0x03; // DLSettings
|
||||
join_data[11] = LoraWanSpreadingFactorToDataRate(); // DLSettings
|
||||
join_data[12] = 1; // RXDelay;
|
||||
|
||||
uint32_t NewMIC = LoraWanGenerateMIC(join_data, 13, LoraSettings.end_node[node].AppKey);
|
||||
uint32_t NewMIC = LoraWanGenerateMIC(join_data, sizeof(data) -4, LoraSettings.end_node[node].AppKey);
|
||||
join_data[13] = NewMIC;
|
||||
join_data[14] = NewMIC >> 8;
|
||||
join_data[15] = NewMIC >> 16;
|
||||
join_data[16] = NewMIC >> 24;
|
||||
uint8_t EncData[33];
|
||||
uint8_t EncData[17];
|
||||
EncData[0] = join_data[0];
|
||||
RadioLibAES128Instance.init(LoraSettings.end_node[node].AppKey);
|
||||
RadioLibAES128Instance.decryptECB(&join_data[1], 16, &EncData[1]);
|
||||
RadioLibAES128Instance.decryptECB(&join_data[1], sizeof(EncData) -1, &EncData[1]);
|
||||
|
||||
// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: Join %17_H"), join_data);
|
||||
|
||||
// 203106E5000000412E010003017CB31DD4 - Dragino
|
||||
// 203206E5000000422E010003016A210EEA - MerryIoT
|
||||
LoraWanSendResponse(EncData, 17, TAS_LORAWAN_JOIN_ACCEPT_DELAY1);
|
||||
LoraWanSendResponse(EncData, sizeof(EncData), TAS_LORAWAN_JOIN_ACCEPT_DELAY1);
|
||||
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((2 == MType) || // Unconfirmed data uplink
|
||||
(4 == MType)) { // Confirmed data uplink
|
||||
else if ((TAS_LORAWAN_MTYPE_UNCONFIRMED_DATA_UPLINK == MType) ||
|
||||
(TAS_LORAWAN_MTYPE_CONFIRMED_DATA_UPLINK == MType)) {
|
||||
// 0 1 2 3 4 5 6 7 8 9 8 9101112131415... packet_size -4
|
||||
// PHYPayload --------------------------------------------------------------
|
||||
// MHDR MACPayload ---------------------------------------------- MIC ----
|
||||
|
@ -456,7 +392,7 @@ bool LoraWanInput(uint8_t* data, uint32_t packet_size) {
|
|||
uint32_t FCnt_missed = FCnt - LoraSettings.end_node[node].FCntUp;
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("LOR: Missed frames %d"), FCnt_missed);
|
||||
if (FCnt_missed > 1) { // Missed two or more frames
|
||||
bitClear(LoraSettings.end_node[node].flags, TAS_LORAWAN_LINK_ADR_REQ); // Resend LinkADRReq
|
||||
bitClear(LoraSettings.end_node[node].flags, TAS_LORAWAN_FLAG_LINK_ADR_REQ); // Resend LinkADRReq
|
||||
}
|
||||
}
|
||||
LoraSettings.end_node[node].FCntUp = FCnt;
|
||||
|
@ -464,37 +400,37 @@ bool LoraWanInput(uint8_t* data, uint32_t packet_size) {
|
|||
if (FOptsLen) {
|
||||
uint32_t i = 0;
|
||||
while (i < FOptsLen) {
|
||||
if (0x02 == FOpts[i]) { // Response from LinkCheckReq (LinkCheckAns)
|
||||
if (TAS_LORAWAN_CID_LINK_CHECK_REQ == FOpts[i]) {
|
||||
// Used by end-device to validate it's connectivity to a network
|
||||
// Need to send Margin/GWCnt
|
||||
}
|
||||
else if (0x03 == FOpts[i]) { // Response from LinkADRReq (LinkADRAns)
|
||||
else if (TAS_LORAWAN_CID_LINK_ADR_ANS == FOpts[i]) {
|
||||
i++;
|
||||
uint8_t status = FOpts[i];
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("LOR: MAC LinkADRAns PowerACK %d, DataRateACK %d, ChannelMaskACK %d"),
|
||||
bitRead(status, 2), bitRead(status, 1), bitRead(status, 0));
|
||||
bitSet(LoraSettings.end_node[node].flags, TAS_LORAWAN_LINK_ADR_REQ);
|
||||
bitSet(LoraSettings.end_node[node].flags, TAS_LORAWAN_FLAG_LINK_ADR_REQ);
|
||||
}
|
||||
else if (0x04 == FOpts[i]) { // Response from DutyCycleReq (DutyCycleAns)
|
||||
else if (TAS_LORAWAN_CID_DUTY_CYCLE_ANS == FOpts[i]) {
|
||||
i++;
|
||||
}
|
||||
else if (0x05 == FOpts[i]) { // Response from RXParamSetupReq (RXParamSetupAns)
|
||||
else if (TAS_LORAWAN_CID_RX_PARAM_SETUP_ANS == FOpts[i]) {
|
||||
i++;
|
||||
}
|
||||
else if (0x06 == FOpts[i]) { // Response from DevStatusReq (DevStatusAns)
|
||||
else if (TAS_LORAWAN_CID_DEV_STATUS_ANS == FOpts[i]) {
|
||||
i++;
|
||||
i++;
|
||||
}
|
||||
else if (0x07 == FOpts[i]) { // Response from NewChannelReq (NewChannelAns)
|
||||
else if (TAS_LORAWAN_CID_NEW_CHANNEL_ANS == FOpts[i]) {
|
||||
i++;
|
||||
}
|
||||
else if (0x08 == FOpts[i]) { // Response from RXTimingSetupReq (RXTimingSetupAns)
|
||||
else if (TAS_LORAWAN_CID_RX_TIMING_SETUP_ANS == FOpts[i]) {
|
||||
}
|
||||
else if (0x09 == FOpts[i]) { // Response from TXParamSetupReq (TXParamSetupAns)
|
||||
else if (TAS_LORAWAN_CID_TX_PARAM_SETUP_ANS == FOpts[i]) {
|
||||
}
|
||||
else if (0x0A == FOpts[i]) { // Response from DIChannelReq (DIChannelAns)
|
||||
else if (TAS_LORAWAN_CID_DL_CHANNEL_ANS == FOpts[i]) {
|
||||
}
|
||||
else if (0x0D == FOpts[i]) { // Response from DeviceTimeReq (DeviceTimeAns)
|
||||
else if (TAS_LORAWAN_CID_DEVICE_TIME_REQ == FOpts[i]) {
|
||||
// Used by the end-device to request the current GPS time
|
||||
// Need to send epoch/fractional second
|
||||
}
|
||||
|
@ -506,92 +442,27 @@ bool LoraWanInput(uint8_t* data, uint32_t packet_size) {
|
|||
}
|
||||
|
||||
if (payload_len) {
|
||||
// Unique parameters:
|
||||
// node
|
||||
// LoraSettings.end_node[node].DevEUIh
|
||||
// LoraSettings.end_node[node].DevEUIl
|
||||
// FPort
|
||||
// payload_len
|
||||
// payload_decrypted[]
|
||||
if (bitRead(LoraSettings.flags, TAS_LORAWAN_DECODE_ENABLED) &&
|
||||
(0x00161600 == LoraSettings.end_node[node].DevEUIh)) { // MerryIoT
|
||||
if (120 == FPort) { // MerryIoT door/window Sensor (DW10)
|
||||
if (9 == payload_len) { // MerryIoT Sensor state
|
||||
// 1 2 3 4 5 6 7 8 9
|
||||
// 03 0F 19 2C 8A00 040000 - button
|
||||
// 00 0F 19 2C 0000 050000 - door
|
||||
uint8_t status = payload_decrypted[0];
|
||||
float battery_volt = (float)(21 + payload_decrypted[1]) / 10.0;
|
||||
int temperature = payload_decrypted[2];
|
||||
int humidity = payload_decrypted[3];
|
||||
uint32_t elapsed_time = payload_decrypted[4] | (payload_decrypted[5] << 8);
|
||||
uint32_t events = payload_decrypted[6] | (payload_decrypted[7] << 8) | (payload_decrypted[8] << 16);
|
||||
#ifdef USE_LORA_DEBUG
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("LOR: Node %d, DevEUI %08X%08X, Events %d, LastEvent %d min, DoorOpen %d, Button %d, Tamper %d, Tilt %d, Battery %1_fV, Temp %d, Hum %d"),
|
||||
node +1, LoraSettings.end_node[node].DevEUIh, LoraSettings.end_node[node].DevEUIl,
|
||||
events, elapsed_time,
|
||||
bitRead(status, 0), bitRead(status, 1), bitRead(status, 2), bitRead(status, 3),
|
||||
&battery_volt,
|
||||
temperature, humidity);
|
||||
#endif // USE_LORA_DEBUG
|
||||
LoraWanPublishHeader(node);
|
||||
ResponseAppend_P(PSTR(",\"Events\":%d,\"LastEvent\":%d,\"DoorOpen\":%d,\"Button\":%d,\"Tamper\":%d,\"Tilt\":%d"
|
||||
",\"Battery\":%1_f,"),
|
||||
events, elapsed_time,
|
||||
bitRead(status, 0), bitRead(status, 1), bitRead(status, 2), bitRead(status, 3),
|
||||
&battery_volt);
|
||||
ResponseAppendTHD(temperature, humidity);
|
||||
ResponseAppend_P(PSTR("}"));
|
||||
LoraWanPublishFooter(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bitRead(LoraSettings.flags, TAS_LORAWAN_DECODE_ENABLED) &&
|
||||
(0xA840410E == LoraSettings.end_node[node].DevEUIh)) { // Dragino
|
||||
LoraNodeData_t node_data;
|
||||
node_data.rssi = Lora.rssi;
|
||||
node_data.snr = Lora.snr;
|
||||
node_data.payload = payload_decrypted;
|
||||
node_data.payload_len = payload_len;
|
||||
node_data.node = node;
|
||||
node_data.FPort = FPort;
|
||||
LoraWanDecode(&node_data);
|
||||
|
||||
if (0xA840410E == LoraSettings.end_node[node].DevEUIh) { // Dragino
|
||||
// Dragino v1.7 fails to set DR with ADR so set it using serial interface:
|
||||
// Password 123456
|
||||
// AT+CHS=868100000
|
||||
// Start join using reset button
|
||||
// AT+CADR=0
|
||||
// AT+CDATARATE=3
|
||||
bitSet(LoraSettings.end_node[node].flags, TAS_LORAWAN_LINK_ADR_REQ);
|
||||
if (10 == FPort) { // Dragino LDS02
|
||||
// 8CD2 01 000010 000000 00 - Door Open, 3.282V
|
||||
// 0CD2 01 000011 000000 00 - Door Closed
|
||||
uint8_t status = payload_decrypted[0];
|
||||
float battery_volt = (float)((payload_decrypted[1] | (payload_decrypted[0] << 8)) &0x3FFF) / 1000;
|
||||
uint8_t MOD = payload_decrypted[2]; // Always 0x01
|
||||
uint32_t events = payload_decrypted[5] | (payload_decrypted[4] << 8) | (payload_decrypted[3] << 16);
|
||||
uint32_t open_duration = payload_decrypted[8] | (payload_decrypted[7] << 8) | (payload_decrypted[6] << 16);
|
||||
uint8_t alarm = payload_decrypted[9];
|
||||
#ifdef USE_LORA_DEBUG
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("LOR: Node %d, DevEUI %08X%08X, Events %d, LastEvent %d min, DoorOpen %d, Battery %3_fV, Alarm %d"),
|
||||
node +1, LoraSettings.end_node[node].DevEUIh, LoraSettings.end_node[node].DevEUIl,
|
||||
events, open_duration,
|
||||
bitRead(status, 7),
|
||||
&battery_volt,
|
||||
bitRead(alarm, 0));
|
||||
#endif // USE_LORA_DEBUG
|
||||
LoraWanPublishHeader(node);
|
||||
ResponseAppend_P(PSTR(",\"Events\":%d,\"LastEvent\":%d,\"DoorOpen\":%d,\"Alarm\":%d,\"Battery\":%3_f}"),
|
||||
events, open_duration, bitRead(status, 7), bitRead(alarm, 0), &battery_volt);
|
||||
LoraWanPublishFooter(node);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Joined device without decoding
|
||||
LoraWanPublishHeader(node);
|
||||
ResponseAppend_P(PSTR(",\"DevEUIh\":\"%08X\",\"DevEUIl\":\"%08X\",\"FPort\":%d,\"Payload\":["),
|
||||
LoraSettings.end_node[node].DevEUIh, LoraSettings.end_node[node].DevEUIl, FPort);
|
||||
for (uint32_t i = 0; i < payload_len; i++) {
|
||||
ResponseAppend_P(PSTR("%s%d"), (0==i)?"":",", payload_decrypted[i]);
|
||||
}
|
||||
ResponseAppend_P(PSTR("]}"));
|
||||
LoraWanPublishFooter(node);
|
||||
bitSet(LoraSettings.end_node[node].flags, TAS_LORAWAN_FLAG_LINK_ADR_REQ);
|
||||
}
|
||||
|
||||
if (4 == MType) { // Confirmed data uplink
|
||||
data[0] = 0x60; // Unconfirmed data downlink
|
||||
if (TAS_LORAWAN_MTYPE_CONFIRMED_DATA_UPLINK == MType) {
|
||||
data[0] = TAS_LORAWAN_MTYPE_UNCONFIRMED_DATA_DOWNLINK << 5;
|
||||
data[5] |= 0x20; // FCtrl Set ACK bit
|
||||
uint16_t FCnt = LoraSettings.end_node[node].FCntDown++;
|
||||
data[6] = FCnt;
|
||||
|
@ -604,8 +475,8 @@ bool LoraWanInput(uint8_t* data, uint32_t packet_size) {
|
|||
LoraWanSendResponse(data, packet_size, TAS_LORAWAN_RECEIVE_DELAY1);
|
||||
}
|
||||
}
|
||||
if (2 == MType) { // Unconfirmed data uplink
|
||||
if (!bitRead(LoraSettings.end_node[node].flags, TAS_LORAWAN_LINK_ADR_REQ) &&
|
||||
if (TAS_LORAWAN_MTYPE_UNCONFIRMED_DATA_UPLINK == MType) {
|
||||
if (!bitRead(LoraSettings.end_node[node].flags, TAS_LORAWAN_FLAG_LINK_ADR_REQ) &&
|
||||
FCtrl_ADR && !FCtrl_ACK) {
|
||||
// Try to fix single channel and datarate
|
||||
LoraWanSendLinkADRReq(node); // Resend LinkADRReq
|
||||
|
@ -662,7 +533,7 @@ void CmndLoraWanAppKey(void) {
|
|||
}
|
||||
}
|
||||
else if (0 == XdrvMailbox.payload) {
|
||||
memset(&LoraSettings.end_node[node], 0, sizeof(tEndNode));
|
||||
memset(&LoraSettings.end_node[node], 0, sizeof(LoraEndNode_t));
|
||||
}
|
||||
char appkey[33];
|
||||
ext_snprintf_P(appkey, sizeof(appkey), PSTR("%16_H"), LoraSettings.end_node[node].AppKey);
|
||||
|
|
|
@ -134,8 +134,6 @@ void LoraSettingsLoad(bool erase) {
|
|||
// Called from FUNC_RESET_SETTINGS (erase = 1) after command reset 4, 5, or 6
|
||||
|
||||
// *** Start init default values in case key is not found ***
|
||||
// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("CFG: Lora use defaults"));
|
||||
|
||||
memset(&LoraSettings, 0x00, sizeof(LoraSettings));
|
||||
// Init any other parameter in struct LoraSettings
|
||||
LoraDefaults();
|
||||
|
@ -196,7 +194,7 @@ void LoraInput(void) {
|
|||
Lora.receive_time, packet_size, data, &Lora.rssi, &Lora.snr);
|
||||
|
||||
#ifdef USE_LORAWAN_BRIDGE
|
||||
if (bitRead(LoraSettings.flags, TAS_LORAWAN_BRIDGE_ENABLED)) {
|
||||
if (bitRead(LoraSettings.flags, TAS_LORA_FLAG_BRIDGE_ENABLED)) {
|
||||
if (LoraWanInput((uint8_t*)data, packet_size)) {
|
||||
return;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue