Increase number of supported LoRaWan nodes from 4 to 16

This commit is contained in:
Theo Arends 2025-05-23 13:40:30 +02:00
parent b684486570
commit f6bf4351f5
6 changed files with 120 additions and 90 deletions

View File

@ -20,6 +20,7 @@ All notable changes to this project will be documented in this file.
- ESP32 Platform from 2025.04.30 to 2025.05.40, Framework (Arduino Core) from v3.1.3.250411 to v3.2.0.250504 and IDF from v5.3.2.250403 to v5.4.1.250501 (#23397)
- ESP32 Platform from 2025.05.40 to 2025.05.30, Framework (Arduino Core) from v3.2.0.250504 to v3.1.3.250504 and IDF from v5.4.1.250501 to v5.3.3.250501 (#23404)
- ESP8266 platform update from 2024.09.00 to 2025.05.00 (#23448)
- Increase number of supported LoRaWan nodes from 4 to 16
### Fixed
- Haspmota `haspmota.parse()` page parsing (#23403)

View File

@ -140,6 +140,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
- ESP32 Platform from 2025.04.30 to 2025.05.30, Framework (Arduino Core) from v3.1.3.250411 to v3.1.3.250504 and IDF from v5.3.2.250403 to v5.3.3.250501 [#23404](https://github.com/arendst/Tasmota/issues/23404)
- GPIOViewer from v1.6.2 to v1.6.3 (No functional change)
- Allow command `WebRefresh` minimum from 1000 to 400 mSec
- Increase number of supported LoRaWan nodes from 4 to 16
### Fixed
- DNS setting with `IPAddress4/5` not persisted [#23426](https://github.com/arendst/Tasmota/issues/23426)

View File

@ -166,7 +166,7 @@
#define TAS_LORAWAN_RECEIVE_DELAY2 1000 // LoRaWan Receive delay 2
#define TAS_LORAWAN_JOIN_ACCEPT_DELAY1 5000 // LoRaWan Join accept delay 1
#define TAS_LORAWAN_JOIN_ACCEPT_DELAY2 1000 // LoRaWan Join accept delay 2
#define TAS_LORAWAN_ENDNODES 4 // Max number of supported endnodes
#define TAS_LORAWAN_ENDNODES 16 // Max number of supported endnodes (every active node uses 68+ bytes)
#define TAS_LORAWAN_AES128_KEY_SIZE 16 // Size in bytes
/*********************************************************************************************/
@ -290,7 +290,7 @@ typedef struct LoraSettings_t {
uint8_t flags;
uint8_t region; // 0 = Default, 1 = AU915, ...
#ifdef USE_LORAWAN_BRIDGE
LoraEndNode_t end_node[TAS_LORAWAN_ENDNODES]; // End node parameters
LoraEndNode_t *end_node[TAS_LORAWAN_ENDNODES]; // End node parameters
#endif // USE_LORAWAN_BRIDGE
} LoraSettings_t;
@ -314,6 +314,7 @@ typedef struct Lora_t {
uint8_t* send_buffer;
uint8_t send_buffer_step;
uint8_t send_buffer_len;
uint8_t nodes;
bool rx;
bool send_request;
bool profile_changed;

View File

@ -69,7 +69,7 @@ void _LoraWanDeriveLegacyAppSKey(uint8_t* key, uint32_t jn, uint32_t nid, uint16
}
void LoraWanDeriveLegacyAppSKey(uint32_t node, uint8_t* AppSKey) {
_LoraWanDeriveLegacyAppSKey(Lora->settings.end_node[node].AppKey, TAS_LORAWAN_JOINNONCE +node, TAS_LORAWAN_NETID, Lora->settings.end_node[node].DevNonce, AppSKey);
_LoraWanDeriveLegacyAppSKey(Lora->settings.end_node[node]->AppKey, TAS_LORAWAN_JOINNONCE +node, TAS_LORAWAN_NETID, Lora->settings.end_node[node]->DevNonce, AppSKey);
}
// DeriveLegacyNwkSKey derives the LoRaWAN 1.0 Network Session Key. AppNonce is entered as JoinNonce.
@ -82,7 +82,7 @@ void _LoraWanDeriveLegacyNwkSKey(uint8_t* appKey, uint32_t jn, uint32_t nid, uin
}
void LoraWanDeriveLegacyNwkSKey(uint32_t node, uint8_t* NwkSKey) {
_LoraWanDeriveLegacyNwkSKey(Lora->settings.end_node[node].AppKey, TAS_LORAWAN_JOINNONCE +node, TAS_LORAWAN_NETID, Lora->settings.end_node[node].DevNonce, NwkSKey);
_LoraWanDeriveLegacyNwkSKey(Lora->settings.end_node[node]->AppKey, TAS_LORAWAN_JOINNONCE +node, TAS_LORAWAN_NETID, Lora->settings.end_node[node]->DevNonce, NwkSKey);
}
#ifdef USE_LORAWAN_TEST

View File

@ -29,11 +29,11 @@ void LoraWanPublishHeader(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("{\"%s\":"), EscapeJSONString(Lora->settings.end_node[node].name.c_str()).c_str());
ResponseAppend_P(PSTR("{\"%s\":"), EscapeJSONString(Lora->settings.end_node[node]->name.c_str()).c_str());
}
ResponseAppend_P(PSTR("{\"Node\":%d,\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""), node +1, Lora->settings.end_node[node].DevEUIl & 0x0000FFFF);
if (!Lora->settings.end_node[node].name.startsWith(F("0x"))) {
ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""), EscapeJSONString(Lora->settings.end_node[node].name.c_str()).c_str());
ResponseAppend_P(PSTR("{\"Node\":%d,\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""), node +1, Lora->settings.end_node[node]->DevEUIl & 0x0000FFFF);
if (!Lora->settings.end_node[node]->name.startsWith(F("0x"))) {
ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""), EscapeJSONString(Lora->settings.end_node[node]->name.c_str()).c_str());
}
ResponseAppend_P(PSTR(",\"RSSI\":%1_f,\"SNR\":%1_f"), &Lora->rssi, &Lora->snr);
}
@ -58,7 +58,7 @@ void LoraWanPublishFooter(uint32_t node) {
char subtopic[TOPSZ];
// Clean special characters
char stemp[TOPSZ];
strlcpy(stemp, Lora->settings.end_node[node].name.c_str(), sizeof(stemp));
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);
@ -90,7 +90,7 @@ void LoraWanDecode(struct LoraNodeData_t* node_data) {
uint8_t FPort;
*/
if (bitRead(Lora->settings.flags, TAS_LORA_FLAG_DECODE_ENABLED)) { // LoraOption3 1
uint32_t manufacturer = Lora->settings.end_node[node_data->node].DevEUIh >> 8;
uint32_t manufacturer = Lora->settings.end_node[node_data->node]->DevEUIh >> 8;
if (0x001616 == manufacturer) { // MerryIoT
if (120 == node_data->FPort) { // MerryIoT door/window Sensor (DW10)
if (9 == node_data->payload_len) { // MerryIoT Sensor state
@ -105,7 +105,7 @@ void LoraWanDecode(struct LoraNodeData_t* node_data) {
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, Lora->settings.end_node[node_data->node].DevEUIh, Lora->settings.end_node[node_data->node].DevEUIl,
node_data->node +1, Lora->settings.end_node[node_data->node]->DevEUIh, Lora->settings.end_node[node_data->node]->DevEUIl,
events, elapsed_time,
bitRead(status, 0), bitRead(status, 1), bitRead(status, 2), bitRead(status, 3),
&battery_volt,
@ -137,7 +137,7 @@ void LoraWanDecode(struct LoraNodeData_t* node_data) {
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, Lora->settings.end_node[node_data->node].DevEUIh, Lora->settings.end_node[node_data->node].DevEUIl,
node_data->node +1, Lora->settings.end_node[node_data->node]->DevEUIh, Lora->settings.end_node[node_data->node]->DevEUIl,
events, open_duration,
bitRead(status, 7),
&battery_volt,
@ -155,9 +155,9 @@ void LoraWanDecode(struct LoraNodeData_t* node_data) {
// Joined device without decoding
LoraWanPublishHeader(node_data->node);
ResponseAppend_P(PSTR(",\"Decoder\":\"%s\",\"DevEUIh\":\"%08X\",\"DevEUIl\":\"%08X\",\"FPort\":%d,\"Payload\":["),
EscapeJSONString(Lora->settings.end_node[node_data->node].decoder.c_str()).c_str(),
Lora->settings.end_node[node_data->node].DevEUIh,
Lora->settings.end_node[node_data->node].DevEUIl,
EscapeJSONString(Lora->settings.end_node[node_data->node]->decoder.c_str()).c_str(),
Lora->settings.end_node[node_data->node]->DevEUIh,
Lora->settings.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]);

View File

@ -66,6 +66,21 @@
* LoRaWanBridge 1
\*********************************************************************************************/
bool LoraWanAddNode(void) {
if (Lora->nodes < TAS_LORAWAN_ENDNODES) {
Lora->settings.end_node[Lora->nodes] = (LoraEndNode_t*)calloc(sizeof(LoraEndNode_t), 1); // Need calloc to reset registers to 0/false
if (Lora->settings.end_node[Lora->nodes]) {
Lora->nodes++;
#ifdef USE_LORA_DEBUG
AddLog(LOG_LEVEL_DEBUG, PSTR("LOR: Allocated %d bytes for node %d at %08X"),
sizeof(LoraEndNode_t), Lora->nodes, Lora->settings.end_node[Lora->nodes -1]);
#endif // USE_LORA_DEBUG
return true;
}
}
return false;
}
/*********************************************************************************************\
* Driver Settings load and save
\*********************************************************************************************/
@ -80,41 +95,44 @@
#define D_JSON_FLAGS "Flags"
bool LoraWanLoadData(void) {
char key[12]; // Max 99 nodes (drvset73_1 to drvset73_99)
char key[12]; // Max 99 nodes (drvset73_1 to drvset73_99)
for (uint32_t n = 0; n < TAS_LORAWAN_ENDNODES; n++) {
snprintf_P(key, sizeof(key), PSTR(XDRV_73_KEY "_%d"), n +1);
String json = UfsJsonSettingsRead(key);
if (json.length() == 0) { continue; } // Only load used slots
if (json.length() == 0) { continue; } // Only load used slots
// {"AppKey":"00000000000000000000000000000000","DevEUI","0000000000000000","DevNonce":0,"FCntUp":0,"FCntDown":0,"Flags":0,"NAME":""}
// {"AppKey":"00000000000000000000000000000000","DevEUIh":1234567890,"DevEUIl":1234567890,"DevNonce":0,"FCntUp":0,"FCntDown":0,"Flags":0,"NAME":"","DCDR":""}
JsonParser parser((char*)json.c_str());
JsonParserObject root = parser.getRootObject();
if (!root) { continue; } // Only load used slots
if (!root) { continue; } // Only load used slots
if (!LoraWanAddNode()) { break; } // Unable to allocate memory
const char* app_key = nullptr;
app_key = root.getStr(PSTR(D_JSON_APPKEY), nullptr);
if (app_key && (strlen(app_key))) {
HexToBytes(app_key, Lora->settings.end_node[n].AppKey, TAS_LORAWAN_AES128_KEY_SIZE);
HexToBytes(app_key, Lora->settings.end_node[n]->AppKey, TAS_LORAWAN_AES128_KEY_SIZE);
}
Lora->settings.end_node[n].DevEUIh = root.getUInt(PSTR(D_JSON_DEVEUI "h"), Lora->settings.end_node[n].DevEUIh);
Lora->settings.end_node[n].DevEUIl = root.getUInt(PSTR(D_JSON_DEVEUI "l"), Lora->settings.end_node[n].DevEUIl);
Lora->settings.end_node[n].DevNonce = root.getUInt(PSTR(D_JSON_DEVNONCE), Lora->settings.end_node[n].DevNonce);
Lora->settings.end_node[n].FCntUp = root.getUInt(PSTR(D_JSON_FCNTUP), Lora->settings.end_node[n].FCntUp);
Lora->settings.end_node[n].FCntDown = root.getUInt(PSTR(D_JSON_FCNTDOWN), Lora->settings.end_node[n].FCntDown);
Lora->settings.end_node[n].flags = root.getUInt(PSTR(D_JSON_FLAGS), Lora->settings.end_node[n].flags);
Lora->settings.end_node[n]->DevEUIh = root.getUInt(PSTR(D_JSON_DEVEUI "h"), Lora->settings.end_node[n]->DevEUIh);
Lora->settings.end_node[n]->DevEUIl = root.getUInt(PSTR(D_JSON_DEVEUI "l"), Lora->settings.end_node[n]->DevEUIl);
Lora->settings.end_node[n]->DevNonce = root.getUInt(PSTR(D_JSON_DEVNONCE), Lora->settings.end_node[n]->DevNonce);
Lora->settings.end_node[n]->FCntUp = root.getUInt(PSTR(D_JSON_FCNTUP), Lora->settings.end_node[n]->FCntUp);
Lora->settings.end_node[n]->FCntDown = root.getUInt(PSTR(D_JSON_FCNTDOWN), Lora->settings.end_node[n]->FCntDown);
Lora->settings.end_node[n]->flags = root.getUInt(PSTR(D_JSON_FLAGS), Lora->settings.end_node[n]->flags);
const char* ctemp = root.getStr(PSTR(D_JSON_NAME), nullptr);
if (ctemp) { Lora->settings.end_node[n].name = ctemp; }
if (ctemp) { Lora->settings.end_node[n]->name = ctemp; }
ctemp = root.getStr(PSTR(D_JSON_DCDR), nullptr);
if (ctemp) { Lora->settings.end_node[n].decoder = ctemp; }
if (ctemp) { Lora->settings.end_node[n]->decoder = ctemp; }
}
AddLog(LOG_LEVEL_DEBUG, PSTR("CFG: LoraWan loaded %d node(s)"), Lora->nodes);
return true;
}
bool LoraWanSaveData(void) {
bool result = true; // Return true if no Endnodes
for (uint32_t n = 0; n < TAS_LORAWAN_ENDNODES; n++) {
if (Lora->settings.end_node[n].AppKey[0] > 0) { // Only save used slots
for (uint32_t n = 0; n < Lora->nodes; n++) {
if (Lora->settings.end_node[n]->AppKey[0] > 0) { // Only save used slots
Response_P(PSTR("{\"" XDRV_73_KEY "_%d\":{\"" D_JSON_APPKEY "\":\"%16_H\""
",\"" D_JSON_DEVEUI "h\":%lu,\"" D_JSON_DEVEUI "l\":%lu"
",\"" D_JSON_DEVNONCE "\":%u"
@ -123,13 +141,13 @@ bool LoraWanSaveData(void) {
",\"" D_JSON_NAME "\":\"%s\""
",\"" D_JSON_DCDR "\":\"%s\"}}"),
n +1,
Lora->settings.end_node[n].AppKey,
Lora->settings.end_node[n].DevEUIh, Lora->settings.end_node[n].DevEUIl,
Lora->settings.end_node[n].DevNonce,
Lora->settings.end_node[n].FCntUp, Lora->settings.end_node[n].FCntDown,
Lora->settings.end_node[n].flags,
(Lora->settings.end_node[n].name) ? Lora->settings.end_node[n].name.c_str() : "",
(Lora->settings.end_node[n].decoder) ? Lora->settings.end_node[n].decoder.c_str() : "");
Lora->settings.end_node[n]->AppKey,
Lora->settings.end_node[n]->DevEUIh, Lora->settings.end_node[n]->DevEUIl,
Lora->settings.end_node[n]->DevNonce,
Lora->settings.end_node[n]->FCntUp, Lora->settings.end_node[n]->FCntDown,
Lora->settings.end_node[n]->flags,
(Lora->settings.end_node[n]->name) ? Lora->settings.end_node[n]->name.c_str() : "",
(Lora->settings.end_node[n]->decoder) ? Lora->settings.end_node[n]->decoder.c_str() : "");
result &= UfsJsonSettingsWrite(ResponseData());
}
}
@ -373,7 +391,7 @@ void LoRaWanSend(void) {
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
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 {
@ -416,7 +434,7 @@ void LoraWanSendResponse(uint8_t* buffer, size_t len, uint32_t lorawan_delay) {
Lora->send_buffer_len = len;
Lora->send_request = false;
Lora->backup_settings = Lora->settings; // Make a copy;
Lora->backup_settings = Lora->settings; // Make a copy;
Lora->send_buffer_step = 2; // Send at RX1 and RX2
uint32_t delay_rx1 = lorawan_delay - TimePassedSince(Lora->receive_time);
@ -488,15 +506,19 @@ uint32_t LoraWanSpreadingFactorToDataRate(bool downlink) {
return downlink ? 8 : 2; // AU915 must use DR8 for RX2, and we want to use DR2 for Uplinks
}
default: { // TAS_LORA_REGION_EU868
// Allow only JoinReq message datarates (125kHz bandwidth)
if (Lora->settings.spreading_factor > 12) {
Lora->settings.spreading_factor = 12;
if (downlink) {
return 0; // RX2 = 869.525 MHz, DR0 (SF12, 125kHz)
} else {
// Allow only JoinReq message datarates (125kHz bandwidth)
if (Lora->settings.spreading_factor > 12) {
Lora->settings.spreading_factor = 12;
}
if (Lora->settings.spreading_factor < 7) {
Lora->settings.spreading_factor = 7;
}
Lora->settings.bandwidth = 125;
return 12 - Lora->settings.spreading_factor;
}
if (Lora->settings.spreading_factor < 7) {
Lora->settings.spreading_factor = 7;
}
Lora->settings.bandwidth = 125;
return 12 - Lora->settings.spreading_factor;
}
}
}
@ -505,7 +527,7 @@ uint32_t LoraWanSpreadingFactorToDataRate(bool downlink) {
void LoraWanSendLinkADRReq(uint32_t node) {
uint32_t DevAddr = Lora->device_address +node;
uint16_t FCnt = Lora->settings.end_node[node].FCntDown++;
uint16_t FCnt = Lora->settings.end_node[node]->FCntDown++;
uint8_t NwkSKey[TAS_LORAWAN_AES128_KEY_SIZE];
LoraWanDeriveLegacyNwkSKey(node, NwkSKey);
@ -565,7 +587,7 @@ void LoraWanSendMacResponse(uint32_t node, uint8_t* FOpts, uint32_t FCtrl) {
if (FCtrl > 15) { return; } // FOpts = 0..15
uint32_t DevAddr = Lora->device_address +node;
uint16_t FCnt = Lora->settings.end_node[node].FCntDown++;
uint16_t FCnt = Lora->settings.end_node[node]->FCntDown++;
uint8_t NwkSKey[TAS_LORAWAN_AES128_KEY_SIZE];
LoraWanDeriveLegacyNwkSKey(node, NwkSKey);
@ -611,23 +633,23 @@ bool LoraWanInput(uint8_t* data, uint32_t packet_size) {
uint32_t MIC = (uint32_t)data[19] | ((uint32_t)data[20] << 8) |
((uint32_t)data[21] << 16) | ((uint32_t)data[22] << 24);
for (uint32_t node = 0; node < TAS_LORAWAN_ENDNODES; node++) {
uint32_t CalcMIC = LoraWanGenerateMIC(data, 19, Lora->settings.end_node[node].AppKey);
for (uint32_t node = 0; node < Lora->nodes; node++) {
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: 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;
Lora->settings.end_node[node].DevNonce = DevNonce;
Lora->settings.end_node[node].FCntUp = 0;
Lora->settings.end_node[node].FCntDown = 0;
bitClear(Lora->settings.end_node[node].flags, TAS_LORAWAN_FLAG_LINK_ADR_REQ);
if (Lora->settings.end_node[node].name.equals(F("0x0000"))) {
Lora->settings.end_node[node]->DevEUIl = DevEUIl;
Lora->settings.end_node[node]->DevEUIh = DevEUIh;
Lora->settings.end_node[node]->DevNonce = DevNonce;
Lora->settings.end_node[node]->FCntUp = 0;
Lora->settings.end_node[node]->FCntDown = 0;
bitClear(Lora->settings.end_node[node]->flags, TAS_LORAWAN_FLAG_LINK_ADR_REQ);
if (Lora->settings.end_node[node]->name.equals(F("0x0000"))) {
char name[10];
ext_snprintf_P(name, sizeof(name), PSTR("0x%04X"), Lora->settings.end_node[node].DevEUIl & 0x0000FFFF);
Lora->settings.end_node[node].name = name;
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;
@ -659,14 +681,14 @@ bool LoraWanInput(uint8_t* data, uint32_t packet_size) {
CFListSize--;
}
uint32_t NewMIC = LoraWanGenerateMIC(join_data, join_data_index, Lora->settings.end_node[node].AppKey);
uint32_t NewMIC = LoraWanGenerateMIC(join_data, join_data_index, Lora->settings.end_node[node]->AppKey);
join_data[join_data_index++] = NewMIC;
join_data[join_data_index++] = NewMIC >> 8;
join_data[join_data_index++] = NewMIC >> 16;
join_data[join_data_index++] = NewMIC >> 24; //[16] or [32]
uint8_t EncData[33];
EncData[0] = join_data[0];
LoraWanEncryptJoinAccept(Lora->settings.end_node[node].AppKey, &join_data[1], join_data_index-1, &EncData[1]);
LoraWanEncryptJoinAccept(Lora->settings.end_node[node]->AppKey, &join_data[1], join_data_index-1, &EncData[1]);
// 203106E5000000412E010003017CB31DD4 - Dragino LDS02
// 2026B4E06C390AFA1B166D465987F31EC4 - Dragino LHT52
@ -704,13 +726,13 @@ bool LoraWanInput(uint8_t* data, uint32_t packet_size) {
bool bResponseSent = false; // Make sure do not send multiple responses
uint32_t DevAddr = (uint32_t)data[1] | ((uint32_t)data[2] << 8) | ((uint32_t)data[3] << 16) | ((uint32_t)data[4] << 24);
for (uint32_t node = 0; node < TAS_LORAWAN_ENDNODES; node++) {
if (0 == Lora->settings.end_node[node].DevEUIh) { continue; } // No DevEUI so never joined
for (uint32_t node = 0; node < Lora->nodes; node++) {
if (0 == Lora->settings.end_node[node]->DevEUIh) { continue; } // No DevEUI so never joined
if ((Lora->device_address +node) != DevAddr) { continue; } // Not my device
uint32_t FCtrl = data[5];
uint32_t FOptsLen = FCtrl & 0x0F;
uint32_t FCnt = (Lora->settings.end_node[node].FCntUp & 0xFFFF0000) | data[6] | (data[7] << 8);
uint32_t FCnt = (Lora->settings.end_node[node]->FCntUp & 0xFFFF0000) | data[6] | (data[7] << 8);
uint8_t* FOpts = &data[8];
uint32_t FPort = data[8 +FOptsLen];
uint8_t* FRMPayload = &data[9 +FOptsLen];
@ -761,17 +783,17 @@ bool LoraWanInput(uint8_t* data, uint32_t packet_size) {
DevAddr, FCtrl, FOptsLen, FCnt, FOptsLen, FOpts, FPort, org_payload_len, FRMPayload, org_payload_len, payload_decrypted, MIC);
#endif // USE_LORA_DEBUG
if (Lora->settings.end_node[node].FCntUp <= FCnt) { // Skip re-transmissions
if (Lora->settings.end_node[node]->FCntUp <= FCnt) { // Skip re-transmissions
Lora->rx = false; // Skip RX2 as this is a response from RX1
Lora->settings.end_node[node].FCntUp++;
if (Lora->settings.end_node[node].FCntUp < FCnt) { // Report missed frames
uint32_t FCnt_missed = FCnt - Lora->settings.end_node[node].FCntUp;
Lora->settings.end_node[node]->FCntUp++;
if (Lora->settings.end_node[node]->FCntUp < FCnt) { // Report missed frames
uint32_t FCnt_missed = FCnt - Lora->settings.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(Lora->settings.end_node[node].flags, TAS_LORAWAN_FLAG_LINK_ADR_REQ); // Resend LinkADRReq
bitClear(Lora->settings.end_node[node]->flags, TAS_LORAWAN_FLAG_LINK_ADR_REQ); // Resend LinkADRReq
}
}
Lora->settings.end_node[node].FCntUp = FCnt;
Lora->settings.end_node[node]->FCntUp = FCnt;
if (FOptsLen) {
uint8_t mac_data[16];
@ -794,7 +816,7 @@ bool LoraWanInput(uint8_t* data, uint32_t packet_size) {
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(Lora->settings.end_node[node].flags, TAS_LORAWAN_FLAG_LINK_ADR_REQ);
bitSet(Lora->settings.end_node[node]->flags, TAS_LORAWAN_FLAG_LINK_ADR_REQ);
}
else if (TAS_LORAWAN_CID_DUTY_CYCLE_ANS == FOpts[i]) {
i++;
@ -851,20 +873,20 @@ bool LoraWanInput(uint8_t* data, uint32_t packet_size) {
node_data.FPort = FPort;
LoraWanDecode(&node_data);
if (0xA84041 == Lora->settings.end_node[node].DevEUIh >> 8) { // Dragino
if (0xA84041 == Lora->settings.end_node[node]->DevEUIh >> 8) { // 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(Lora->settings.end_node[node].flags, TAS_LORAWAN_FLAG_LINK_ADR_REQ);
bitSet(Lora->settings.end_node[node]->flags, TAS_LORAWAN_FLAG_LINK_ADR_REQ);
}
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 = Lora->settings.end_node[node].FCntDown++;
uint16_t FCnt = Lora->settings.end_node[node]->FCntDown++;
data[6] = FCnt;
data[7] = FCnt >> 8;
uint32_t MIC = LoraWanComputeLegacyDownlinkMIC(NwkSKey, DevAddr, FCnt, data, packet_size -4);
@ -877,7 +899,7 @@ bool LoraWanInput(uint8_t* data, uint32_t packet_size) {
}
}
if (TAS_LORAWAN_MTYPE_UNCONFIRMED_DATA_UPLINK == MType) {
if (!bitRead(Lora->settings.end_node[node].flags, TAS_LORAWAN_FLAG_LINK_ADR_REQ) &&
if (!bitRead(Lora->settings.end_node[node]->flags, TAS_LORAWAN_FLAG_LINK_ADR_REQ) &&
FCtrl_ADR && !FCtrl_ACK) {
// Try to fix single channel and datarate
bResponseSent = true;
@ -932,19 +954,24 @@ void CmndLoraWanBridge(void) {
void CmndLoraWanAppKey(void) {
// LoraWanAppKey
// LoraWanAppKey2 0123456789abcdef0123456789abcdef
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TAS_LORAWAN_ENDNODES)) {
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= Lora->nodes +1)) {
if (Lora->nodes < XdrvMailbox.index) {
if (!LoraWanAddNode()) {
return; // Memory allocation failed or TAS_LORAWAN_ENDNODES reached
}
}
uint32_t node = XdrvMailbox.index -1;
if (32 == XdrvMailbox.data_len) {
HexToBytes(XdrvMailbox.data, Lora->settings.end_node[node].AppKey, TAS_LORAWAN_AES128_KEY_SIZE);
if (0 == Lora->settings.end_node[node].name.length()) {
Lora->settings.end_node[node].name = F("0x0000");
HexToBytes(XdrvMailbox.data, Lora->settings.end_node[node]->AppKey, TAS_LORAWAN_AES128_KEY_SIZE);
if (0 == Lora->settings.end_node[node]->name.length()) {
Lora->settings.end_node[node]->name = F("0x0000");
}
}
else if (0 == XdrvMailbox.payload) {
memset(&Lora->settings.end_node[node], 0, sizeof(LoraEndNode_t));
memset(Lora->settings.end_node[node], 0, sizeof(LoraEndNode_t));
}
char appkey[33];
ext_snprintf_P(appkey, sizeof(appkey), PSTR("%16_H"), Lora->settings.end_node[node].AppKey);
ext_snprintf_P(appkey, sizeof(appkey), PSTR("%16_H"), Lora->settings.end_node[node]->AppKey);
ResponseCmndIdxChar(appkey);
}
}
@ -953,18 +980,18 @@ void CmndLoraWanName(void) {
// LoraWanName
// LoraWanName 1 - Set to short DevEUI (or 0x0000 if not yet joined)
// LoraWanName2 LDS02a
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TAS_LORAWAN_ENDNODES)) {
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= Lora->nodes)) {
uint32_t node = XdrvMailbox.index -1;
if (XdrvMailbox.data_len) {
if (1 == XdrvMailbox.payload) {
char name[10];
ext_snprintf_P(name, sizeof(name), PSTR("0x%04X"), Lora->settings.end_node[node].DevEUIl & 0x0000FFFF);
Lora->settings.end_node[node].name = name;
ext_snprintf_P(name, sizeof(name), PSTR("0x%04X"), Lora->settings.end_node[node]->DevEUIl & 0x0000FFFF);
Lora->settings.end_node[node]->name = name;
} else {
Lora->settings.end_node[node].name = XdrvMailbox.data;
Lora->settings.end_node[node]->name = XdrvMailbox.data;
}
}
ResponseCmndIdxChar(Lora->settings.end_node[node].name.c_str());
ResponseCmndIdxChar(Lora->settings.end_node[node]->name.c_str());
}
}
@ -972,12 +999,12 @@ void CmndLoraWanDecoder(void) {
// LoraWanDecoder
// LoraWanDecoder DraginoLDS02 - Set Dragino LDS02 message decoder for node 1
// LoraWanDecoder2 MerryIoTDW10 - Set MerryIoT DW10 message decoder for node 2
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TAS_LORAWAN_ENDNODES)) {
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= Lora->nodes)) {
uint32_t node = XdrvMailbox.index -1;
if (XdrvMailbox.data_len) {
Lora->settings.end_node[node].decoder = XdrvMailbox.data;
Lora->settings.end_node[node]->decoder = XdrvMailbox.data;
}
ResponseCmndIdxChar(Lora->settings.end_node[node].decoder.c_str());
ResponseCmndIdxChar(Lora->settings.end_node[node]->decoder.c_str());
}
}