├── LICENSE ├── LICENSE.txt ├── LoraCommon.h ├── LoraGateway.cpp ├── LoraGateway.h ├── LoraNode.cpp ├── LoraNode.h ├── README.md ├── examples ├── gateway.ino └── node.ino ├── keywords.txt ├── library.json └── library.properties /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 tinytronix 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 tinytronix 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 20 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 21 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /LoraCommon.h: -------------------------------------------------------------------------------- 1 | #ifndef _LORACOMMON_H 2 | #define _LORACOMMON_H 3 | 4 | #define LORANODE_NO_ENCRYPT //Kommentar entfernen, wenn keine Verschluesselung genwuenscht ist 5 | 6 | 7 | #define LORA_SECONDS(x) ((x) * (1000/taskInterval)) 8 | #define LORA_MILLISECONDS(x) ((x) / taskInterval) 9 | #define LORA_TIMER_INACTIVE 0xFFFF 10 | 11 | 12 | #define LORA_STATUS_SUCCESS 0 13 | #define LORA_STATUS_CMD_NOTSUPPORTED 1 14 | #define LORA_STATUS_CHANNEL_NOTSUPPORTED 3 15 | #define LORA_STATUS_DEVICE_NOTSUPPORTED 4 16 | #define LORA_STATUS_DEVICE_NOTCONNECTED 5 17 | #define LORA_STATUS_BAD_DEVICEID 6 18 | #define LORA_STATUS_OUTDATED 7 19 | 20 | 21 | typedef enum _tagLORAMDID 22 | { 23 | //Requests immer ungerade, Responses immer gerade Zahl 24 | //Diese Festlegung wird benutzt, um Gateway-Funktionen und Node-Funktionen 25 | //auseinander zu halten 26 | LORA_ACTOR_REQ = 0x01, 27 | LORA_ACTOR_RESP = 0x02, 28 | LORA_SENSOR_REQ = 0x03, 29 | LORA_SENSOR_RESP = 0x04, 30 | LORA_EEPWRITE_REQ = 0x05, 31 | LORA_EEPWRITE_RESP = 0x06, 32 | LORA_EEPREAD_REQ = 0x07, 33 | LORA_EEPREAD_RESP = 0x08, 34 | LORA_PROPERTY_REQ = 0x09, 35 | LORA_PROPERTY_RESP = 0x0A 36 | }LORACMDID; 37 | 38 | //GW 39 | typedef struct _tagLORAHEAD 40 | { 41 | uint32_t radioId; //id des Empfaengers 42 | uint8_t transactionContext; //wird vom Gateway fuer jeden Request neu vergeben und muss zurueckgeschickt werden, falls eine Antwort erwartet wird 43 | uint8_t cmdId; //LORACMD, LORAACK, LORABRIDGE etc. 44 | uint8_t status; //ACK etc 45 | }LORAHEAD; 46 | 47 | 48 | typedef struct _tagACTOR_REQ 49 | { 50 | uint16_t id; 51 | uint8_t action; //0:off 1:on 2:pulseUp 3:pulseDown 52 | }ACTOR_REQ; 53 | 54 | 55 | typedef struct _tagACTOR_RESP 56 | { 57 | uint8_t status; 58 | }ACTOR_RESP; 59 | 60 | typedef struct _tagSENSOR_REQ 61 | { 62 | uint8_t startId; 63 | uint8_t nSensors; //Anzahl der abzufragenden Sensoren 64 | }SENSOR_REQ; 65 | 66 | typedef struct _tagSENSOR_ELEM 67 | { 68 | int8_t id; 69 | int16_t value; 70 | }SENSOR_ELEM; 71 | 72 | typedef struct _tagSENSOR_RESP 73 | { 74 | uint8_t nSensors; 75 | SENSOR_ELEM sensor[6]; //1..6 Sensorwerte 76 | }SENSOR_RESP; 77 | 78 | 79 | typedef struct _tagFRAME 80 | { 81 | LORAHEAD head; 82 | union 83 | { 84 | uint8_t data; 85 | ACTOR_REQ actorReq; 86 | ACTOR_RESP actorResp; 87 | SENSOR_REQ sensorReq; 88 | SENSOR_RESP sensorResp; 89 | }; 90 | }LORAFRAME; 91 | 92 | 93 | typedef struct _tagCipherKey 94 | { 95 | uint8_t key[3]; 96 | }CIPHERKEY; 97 | 98 | #endif //_LORACOMMON_H 99 | -------------------------------------------------------------------------------- /LoraGateway.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "LoraGateway.h" 3 | 4 | 5 | 6 | #define LORA_REPEAT_INTERVAL 120 //nach dieser Zeit in ms wird das Komamndo wiederholt, wenn keine Antwort kommt 7 | 8 | #define LORA_PROTOCOL_STATE_TRANSMIT 1 //Kommando ist zum Senden bereit 9 | #define LORA_PROTOCOL_STATE_WAITACK 2 //Kommando wartet auf Bestaetigung (nur wenn ACK Flag gesetzt ist) 10 | #define LORA_PROTOCOL_STATE_IDLE 3 //derzeit laeuft keine Transaktion 11 | #define LORA_PROTOCOL_STATE_ERROR 4 //Kommando nicht erfolgreich (ACK oder Response nicht emfangen) 12 | #define LORA_PROTOCOL_STATE_RXDONE 5 //Kommando wurde von der Gegenseite beantwortet 13 | 14 | LoraGateway::LoraGateway(int spiSelect, int reset, int busy, int interrupt) : SX126x(spiSelect,reset,busy,interrupt) 15 | { 16 | timer = LORA_TIMER_INACTIVE; 17 | 18 | #ifndef LORANODE_NO_ENCRYPT 19 | encryptDecryptState = 0; 20 | #endif 21 | return; 22 | } 23 | 24 | LoraGateway::~LoraGateway() 25 | { 26 | } 27 | 28 | void LoraGateway::begin(uint8_t taskIntervalInMs, uint8_t spreadingFactor, uint32_t frequencyInHz, int8_t txPowerInDbm) 29 | { 30 | taskInterval = taskIntervalInMs; 31 | currTransaction.state = LORA_PROTOCOL_STATE_IDLE; 32 | transactionCtxFactory = 0; 33 | SX126x::begin(spreadingFactor, frequencyInHz, txPowerInDbm); 34 | } 35 | 36 | void LoraGateway::LoRaConfig(uint8_t spreadingFactor, uint8_t bandwidth, uint8_t codingRate, uint16_t preambleLength, uint8_t payloadLen, bool crcOn, bool invertIrq) 37 | { 38 | SX126x::LoRaConfig(spreadingFactor, bandwidth, codingRate, preambleLength, payloadLen, crcOn, invertIrq); 39 | } 40 | 41 | void LoraGateway::ReceiveResp(void) 42 | { 43 | uint8_t rxLen = 0; 44 | 45 | if ( true == ReceiveMode() ) 46 | { 47 | rxLen = SX126x::Receive((uint8_t*)&currTransaction.loraFrameRx, sizeof(LORAFRAME)); 48 | if ( currTransaction.state < LORA_PROTOCOL_STATE_IDLE ) 49 | { 50 | if ( (rxLen > sizeof(LORAHEAD)) && (rxLen <= sizeof(LORAFRAME)) ) 51 | { 52 | currTransaction.frameLenRx = rxLen; 53 | #ifndef LORANODE_NO_ENCRYPT 54 | EncryptDecrypt((uint8_t*)&currTransaction.loraFrameRx, (uint8_t*)&currTransaction.loraFrameRx, rxLen); 55 | #endif 56 | if ( currTransaction.loraFrameRx.head.transactionContext == currTransaction.loraFrameTx.head.transactionContext ) 57 | { 58 | Serial.println("WAITACK -> RXDONE"); 59 | currTransaction.state = LORA_PROTOCOL_STATE_RXDONE; 60 | } 61 | } 62 | } 63 | } 64 | } 65 | 66 | uint8_t LoraGateway::GetResponseData(uint8_t** pRespData) 67 | { 68 | if ( currTransaction.state == LORA_PROTOCOL_STATE_RXDONE ) 69 | { 70 | *pRespData = &currTransaction.loraFrameRx.data; 71 | return (currTransaction.frameLenRx - sizeof(LORAHEAD)); 72 | } 73 | else 74 | { 75 | *pRespData = NULL; 76 | return 0; 77 | } 78 | } 79 | 80 | void LoraGateway::ProcessTransaction(void) 81 | { 82 | #ifndef LORANODE_NO_ENCRYPT 83 | LORAFRAME d; 84 | #endif 85 | switch ( currTransaction.state ) 86 | { 87 | case LORA_PROTOCOL_STATE_TRANSMIT: 88 | #ifndef LORANODE_NO_ENCRYPT 89 | EncryptDecrypt((uint8_t*)&currTransaction.loraFrameTx, (uint8_t*)&d, currTransaction.frameLenTx); 90 | if ( SX126x::Send((uint8_t*)&d, currTransaction.frameLenTx, SX126x_TXMODE_ASYNC) ) 91 | #else 92 | if ( SX126x::Send((uint8_t*)&currTransaction.loraFrameTx, currTransaction.frameLenTx, SX126x_TXMODE_ASYNC) ) 93 | #endif 94 | { 95 | if ( currTransaction.timeout > 0 ) 96 | { 97 | Serial.println("TRANSMIT -> WAITACK"); 98 | currTransaction.state = LORA_PROTOCOL_STATE_WAITACK; 99 | } 100 | else 101 | { 102 | Serial.println("TRANSMIT -> IDLE"); 103 | currTransaction.state = LORA_PROTOCOL_STATE_IDLE; 104 | } 105 | } 106 | break; 107 | 108 | case LORA_PROTOCOL_STATE_WAITACK: 109 | if ( currTransaction.timeout > 0 ) 110 | { 111 | currTransaction.timeout--; 112 | } 113 | else if ( currTransaction.nRepeat > 0 ) 114 | { 115 | currTransaction.nRepeat--; 116 | currTransaction.timeout = LORA_MILLISECONDS(LORA_REPEAT_INTERVAL); 117 | Serial.println("WAITACK -> TRANSMIT"); 118 | currTransaction.state = LORA_PROTOCOL_STATE_TRANSMIT; 119 | } 120 | else 121 | { 122 | Serial.println("WAITACK -> ERROR "); 123 | currTransaction.state = LORA_PROTOCOL_STATE_ERROR; 124 | } 125 | break; 126 | 127 | case LORA_PROTOCOL_STATE_ERROR: 128 | break; 129 | 130 | default: 131 | break; 132 | } 133 | } 134 | 135 | void LoraGateway::Timer(void) 136 | { 137 | if ( (timer != LORA_TIMER_INACTIVE) && (timer > 0) ) 138 | timer--; 139 | } 140 | 141 | #ifndef LORANODE_NO_ENCRYPT 142 | void LoraGateway::EncryptDecrypt(uint8_t* input, uint8_t* output, uint16_t len) 143 | { 144 | if ( encryptDecryptState == 0 ) 145 | { 146 | memcpy(output, input, len); 147 | return; 148 | } 149 | 150 | if ( encryptDecryptState == 1 ) 151 | { 152 | //wenn wir hier sind wuerde das bedeuten, dass in einem Taskzyklus 2x gesendet 153 | //oder 2x empfangen oder sowohl empfangen als auch gesendet wurde. 154 | //Das kann eigentlich nicht sein, denn die Statemachines im Gateway und den Endgeraeten 155 | //lassen das nicht zu. 156 | spritz_setup(&encryptDecryptCtx, (uint8_t*)&encryptDecryptKey, sizeof(encryptDecryptKey)); 157 | encryptDecryptState = 2; 158 | } 159 | 160 | encryptDecryptState = 1; 161 | spritz_crypt(&encryptDecryptCtx, input, len, output); 162 | } 163 | 164 | void LoraGateway::EncryptDecryptKey(CIPHERKEY * key) 165 | { 166 | encryptDecryptKey = *key; 167 | spritz_setup(&encryptDecryptCtx, (uint8_t*)&encryptDecryptKey, sizeof(encryptDecryptKey)); 168 | encryptDecryptState = 2; 169 | } 170 | #endif 171 | 172 | void LoraGateway::Send(uint32_t radioId, uint8_t cmdId, uint8_t *pData, uint8_t dataLen) 173 | { 174 | //pruefen, ob eine Transaktion laeuft 175 | if ( currTransaction.state > LORA_PROTOCOL_STATE_WAITACK ) 176 | { 177 | //Transaktionsszustand ist IDLE oder ERROR oder RXDONE 178 | transactionCtxFactory++; 179 | currTransaction.frameLenTx = sizeof(LORAHEAD) + dataLen; 180 | currTransaction.nRepeat = 3; 181 | currTransaction.timeout = LORA_MILLISECONDS(LORA_REPEAT_INTERVAL); 182 | currTransaction.state = LORA_PROTOCOL_STATE_TRANSMIT; 183 | 184 | currTransaction.loraFrameTx.head.radioId = radioId; 185 | currTransaction.loraFrameTx.head.transactionContext = transactionCtxFactory; 186 | currTransaction.loraFrameTx.head.cmdId = cmdId; 187 | currTransaction.loraFrameTx.head.status = LORA_STATUS_SUCCESS; 188 | 189 | memcpy(&currTransaction.loraFrameTx.data, pData, dataLen); 190 | } 191 | } 192 | 193 | void LoraGateway::Service(void) 194 | { 195 | #ifndef LORANODE_NO_ENCRYPT 196 | if ( encryptDecryptState == 1 ) 197 | { 198 | spritz_setup(&encryptDecryptCtx, (uint8_t*)&encryptDecryptKey, sizeof(encryptDecryptKey)); 199 | encryptDecryptState = 2; 200 | } 201 | #endif 202 | Timer(); 203 | ReceiveResp(); 204 | ProcessTransaction(); 205 | } 206 | -------------------------------------------------------------------------------- /LoraGateway.h: -------------------------------------------------------------------------------- 1 | #ifndef _LORAGATEWAY_H 2 | #define _LORAGATEWAY_H 3 | #include "LoraCommon.h" 4 | #include 5 | #include 6 | 7 | 8 | typedef struct _tagLORATRANSACTION 9 | { 10 | LORAFRAME loraFrameTx; 11 | LORAFRAME loraFrameRx; 12 | uint16_t timeout; 13 | uint16_t state; 14 | uint8_t nRepeat; 15 | uint8_t frameLenTx; 16 | uint8_t frameLenRx; 17 | uint8_t usrContext; 18 | }LORATRANSACTION; 19 | 20 | 21 | class LoraGateway : private SX126x 22 | { 23 | public: 24 | LoraGateway(int spiSelectPin, int resetPin, int busyPin, int interruptPin); 25 | ~LoraGateway(); 26 | 27 | void begin (uint8_t taskIntervalInMs, uint8_t spreadingFactor, uint32_t frequencyInHz, int8_t txPowerInDbm); 28 | void LoRaConfig (uint8_t spreadingFactor, uint8_t bandwidth, uint8_t codingRate, uint16_t preambleLength, uint8_t payloadLen, bool crcOn, bool invertIrq); 29 | void Service (void); 30 | void Send (uint32_t radioId, uint8_t cmdId, uint8_t *data, uint8_t len); 31 | uint8_t GetResponseData (uint8_t **pRespData); 32 | #ifndef LORANODE_NO_ENCRYPT 33 | void EncryptDecryptKey (CIPHERKEY * key); 34 | #endif 35 | 36 | private: 37 | LORATRANSACTION currTransaction; 38 | uint8_t transactionCtxFactory; 39 | 40 | uint16_t timer; 41 | uint8_t taskInterval; //intervall in dem die Funktion Service aufgerufen wird, wichtig zur Berechnugn der Timer 42 | #ifndef LORANODE_NO_ENCRYPT 43 | CIPHERKEY encryptDecryptKey; 44 | uint8_t encryptDecryptState; //0: no encryption, 1: setup required, 2: encrypt prepared 45 | spritz_ctx encryptDecryptCtx; 46 | #endif 47 | 48 | void ReceiveResp (void); 49 | void Timer (void); 50 | void ProcessTransaction (void); 51 | #ifndef LORANODE_NO_ENCRYPT 52 | void EncryptDecrypt (uint8_t* input, uint8_t* output, uint16_t len); 53 | #endif 54 | }; 55 | #endif //_LORAGATEWAY_H 56 | -------------------------------------------------------------------------------- /LoraNode.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "LoraNode.h" 3 | #include "QList.h" 4 | 5 | //Todo: Jeder Node kann immer nur einem Gateway zugeordnet sein. 6 | //Bisher ist diese Zuordnung per #define fest im Code verankert. 7 | //Spaeter koennte diese Zuordnung per Eeprom-Konfiguration festgelegt werden. 8 | #define DEVICE_LORA_GATEWAY (DEVICE_CLASS_INFRASTRUCTURE | DEVICE_TYPE_GATEWAY | 0x0001) //LoRa Gateway 9 | 10 | #define LORA_RESP_WAITTIME 40 //Zeit in ms zwischen Empfang eines Lora Command Frames und dem Versenden des ACK Frames 11 | 12 | typedef struct _tagCallbackElem 13 | { 14 | uint32_t radioId; 15 | void * cbFunction; 16 | uint8_t cmdId; 17 | }CALLBACK_ELEM; 18 | 19 | LoraNode::LoraNode(int spiSelect, int reset, int busy, int interrupt) : SX126x(spiSelect,reset,busy,interrupt) 20 | { 21 | timer = LORA_TIMER_INACTIVE; 22 | frameLen = 0; 23 | onLORA_ACTOR_REQ = NULL; 24 | onLORA_ACTOR_REQ = NULL; 25 | onLORA_SENSOR_REQ = NULL; 26 | onLORA_EEPWRITE_REQ = NULL; 27 | onLORA_EEPREAD_REQ = NULL; 28 | onLORA_PROPERTY_REQ = NULL; 29 | 30 | #ifndef LORANODE_NO_ENCRYPT 31 | encryptDecryptState = 0; 32 | #endif 33 | return; 34 | } 35 | 36 | LoraNode::~LoraNode() 37 | { 38 | } 39 | 40 | void LoraNode::begin(uint8_t taskIntervalInMs, uint8_t spreadingFactor, uint32_t frequencyInHz, int8_t txPowerInDbm) 41 | { 42 | taskInterval = taskIntervalInMs; 43 | currentTransactionContext = 0; 44 | SX126x::begin(spreadingFactor, frequencyInHz, txPowerInDbm); 45 | } 46 | 47 | void LoraNode::LoRaConfig(uint8_t spreadingFactor, uint8_t bandwidth, uint8_t codingRate, uint16_t preambleLength, uint8_t payloadLen, bool crcOn, bool invertIrq) 48 | { 49 | SX126x::LoRaConfig(spreadingFactor, bandwidth, codingRate, preambleLength, payloadLen, crcOn, invertIrq); 50 | } 51 | 52 | void LoraNode::RegisterCallback(uint32_t radioId, LORACMDID cmdId, void* cb) 53 | { 54 | CALLBACK_ELEM cbElem; 55 | cbElem.radioId = radioId; 56 | cbElem.cmdId = cmdId; 57 | cbElem.cbFunction = cb; 58 | 59 | cbList.push_back(cbElem); 60 | } 61 | 62 | void LoraNode::Send_LORA_ACTOR_RESP(uint8_t status) 63 | { 64 | ACTOR_RESP data; 65 | data.status = status; 66 | QueueResponse(LORA_ACTOR_RESP, (void*)&data, sizeof(data)); 67 | } 68 | 69 | void LoraNode::Send_LORA_SENSOR_RESP(SENSOR_RESP *pResp) 70 | { 71 | uint8_t len = sizeof(pResp->nSensors); 72 | len += pResp->nSensors * sizeof(SENSOR_ELEM); 73 | QueueResponse(LORA_SENSOR_RESP, pResp, len); 74 | } 75 | 76 | void LoraNode::Send_LORA_EEPWRITE_RESP(void) 77 | { 78 | QueueResponse(LORA_EEPREAD_RESP, NULL, 0); 79 | } 80 | 81 | void LoraNode::Send_LORA_EEPREAD_RESP(void) 82 | { 83 | QueueResponse(LORA_EEPREAD_RESP, NULL, 0); 84 | } 85 | 86 | void LoraNode::Send_LORA_PROPERTY_RESP(void) 87 | { 88 | QueueResponse(LORA_EEPREAD_RESP, NULL, 0); 89 | } 90 | 91 | void LoraNode::QueueResponse(LORACMDID cmdId, void* pData, uint16_t len) 92 | { 93 | if ( !pData || (0 == len) ) 94 | return; 95 | 96 | frameLen = sizeof(LORAHEAD) + len; 97 | //Todo: Jeder Node kann immer nur einem Gateway zugeordnet sein. 98 | //Bisher ist diese Zuordnung per #define fest im Code verankert. 99 | //Spaeter koennte diese Zuordnung per Eeprom-Konfiguration festgelegt werden. 100 | dataFrame.head.radioId = DEVICE_LORA_GATEWAY; 101 | dataFrame.head.cmdId = cmdId; 102 | dataFrame.head.transactionContext = currentTransactionContext; 103 | memcpy(&dataFrame.data, pData, len); 104 | timer = LORA_MILLISECONDS(LORA_RESP_WAITTIME); 105 | } 106 | 107 | void LoraNode::ReceiveReq(void) 108 | { 109 | CALLBACK_ELEM cbElem; 110 | uint8_t rxLen = 0; 111 | bool found = false; 112 | 113 | if ( true == ReceiveMode() ) 114 | { 115 | rxLen = SX126x::Receive((uint8_t*)&dataFrame, sizeof(LORAFRAME)); 116 | if ( (rxLen >= 4) && (rxLen <= sizeof(LORAFRAME)) ) 117 | { 118 | #ifndef LORANODE_NO_ENCRYPT 119 | EncryptDecrypt((uint8_t*)&dataFrame, (uint8_t*)&dataFrame, rxLen); 120 | #endif 121 | 122 | for(int i=0;i 0) ) 188 | timer--; 189 | } 190 | 191 | #ifndef LORANODE_NO_ENCRYPT 192 | void LoraNode::EncryptDecrypt(uint8_t* input, uint8_t* output, uint16_t len) 193 | { 194 | if ( encryptDecryptState == 0 ) 195 | { 196 | memcpy(output, input, len); 197 | return; 198 | } 199 | 200 | if ( encryptDecryptState == 1 ) 201 | { 202 | //wenn wir hier sind wuerde das bedeuten, dass in einem Taskzyklus 2x gesendet 203 | //oder 2x empfangen oder sowohl empfangen als auch gesendet wurde. 204 | //Das kann eigentlich nicht sein, denn die Statemachines im Gateway und den Endgeraeten 205 | //lassen das nicht zu. 206 | spritz_setup(&encryptDecryptCtx, (uint8_t*)&encryptDecryptKey, sizeof(encryptDecryptKey)); 207 | encryptDecryptState = 2; 208 | } 209 | 210 | encryptDecryptState = 1; 211 | spritz_crypt(&encryptDecryptCtx, input, len, output); 212 | } 213 | 214 | void LoraNode::EncryptDecryptKey(CIPHERKEY * key) 215 | { 216 | encryptDecryptKey = *key; 217 | spritz_setup(&encryptDecryptCtx, (uint8_t*)&encryptDecryptKey, sizeof(encryptDecryptKey)); 218 | encryptDecryptState = 2; 219 | } 220 | #endif 221 | void LoraNode::Service(void) 222 | { 223 | #ifndef LORANODE_NO_ENCRYPT 224 | if ( encryptDecryptState == 1 ) 225 | { 226 | spritz_setup(&encryptDecryptCtx, (uint8_t*)&encryptDecryptKey, sizeof(encryptDecryptKey)); 227 | encryptDecryptState = 2; 228 | } 229 | #endif 230 | Timer(); 231 | ReceiveReq(); 232 | Send(); 233 | } 234 | -------------------------------------------------------------------------------- /LoraNode.h: -------------------------------------------------------------------------------- 1 | #ifndef _LORANODE_H 2 | #define _LORANODE_H 3 | #include "LoraCommon.h" 4 | #include 5 | #include "QList.h" 6 | 7 | #ifndef LORANODE_NO_ENCRYPT 8 | #include 9 | #endif 10 | 11 | 12 | #define DEVICE_CLASS_MASK 0xFF000000 13 | #define DEVICE_TYPE_MASK 0x00FF0000 14 | #define DEVICE_ADDRESS_MASK 0x0000FFFF 15 | 16 | #define DEVICE_CLASS_CONTROLLER 0x01000000 17 | #define DEVICE_CLASS_AKTOR 0x02000000 18 | #define DEVICE_CLASS_SENSOR 0x03000000 19 | #define DEVICE_CLASS_AKTORSENSOR 0x04000000 20 | #define DEVICE_CLASS_INFRASTRUCTURE 0x05000000 21 | 22 | #define DEVICE_TYPE_HUT4C 0x00010000 23 | #define DEVICE_TYPE_HUT6C 0x00020000 24 | #define DEVICE_TYPE_INWALL 0x00030000 25 | #define DEVICE_TYPE_BRIDGE 0x00040000 26 | #define DEVICE_TYPE_GATEWAY 0x00050000 27 | 28 | 29 | typedef struct _tagLORATRANSACTION LORATRANSACTION; 30 | typedef struct _tagCallbackElem CALLBACK_ELEM; 31 | 32 | typedef void(*cbLORA_ACTOR_REQ) (uint16_t id, uint8_t action); 33 | typedef void(*cbLORA_SENSOR_REQ) (uint8_t startId, uint8_t nSensors); 34 | typedef void(*cbLORA_EEPWRITE_REQ) (void); 35 | typedef void(*cbLORA_EEPREAD_REQ) (void); 36 | typedef void(*cbLORA_PROPERTY_REQ) (void); 37 | 38 | 39 | class LoraNode : private SX126x 40 | { 41 | public: 42 | LoraNode(int a, int b, int c, int d); 43 | ~LoraNode(); 44 | 45 | void begin (uint8_t taskIntervalInMs, uint8_t spreadingFactor, uint32_t frequencyInHz, int8_t txPowerInDbm); 46 | void LoRaConfig (uint8_t spreadingFactor, uint8_t bandwidth, uint8_t codingRate, uint16_t preambleLength, uint8_t payloadLen, bool crcOn, bool invertIrq); 47 | void Service (void); 48 | void RegisterCallback (uint32_t radioId, LORACMDID cmdId, void* callback); 49 | 50 | void Send_LORA_ACTOR_RESP (uint8_t status); 51 | void Send_LORA_SENSOR_RESP (SENSOR_RESP *pResp); 52 | void Send_LORA_EEPWRITE_RESP (void); 53 | void Send_LORA_EEPREAD_RESP (void); 54 | void Send_LORA_PROPERTY_RESP (void); 55 | #ifndef LORANODE_NO_ENCRYPT 56 | void EncryptDecrypt (uint8_t* input, uint8_t* output, uint16_t len); 57 | void EncryptDecryptKey (CIPHERKEY * key); 58 | #endif 59 | 60 | private: 61 | uint32_t ownRadioId; 62 | LORAFRAME dataFrame; 63 | uint8_t frameLen; 64 | uint8_t currentTransactionContext; 65 | 66 | uint16_t timer; 67 | uint8_t taskInterval; //intervall in dem die Funktion Service aufgerufen wird, wichtig zur Berechnugn der Timer 68 | #ifndef LORANODE_NO_ENCRYPT 69 | CIPHERKEY encryptDecryptKey; 70 | uint8_t encryptDecryptState; //0: no encryption, 1: setup required, 2: encrypt prepared 71 | spritz_ctx encryptDecryptCtx; 72 | #endif 73 | QList cbList; 74 | 75 | cbLORA_ACTOR_REQ *onLORA_ACTOR_REQ; 76 | cbLORA_SENSOR_REQ *onLORA_SENSOR_REQ; 77 | cbLORA_EEPWRITE_REQ *onLORA_EEPWRITE_REQ; 78 | cbLORA_EEPREAD_REQ *onLORA_EEPREAD_REQ; 79 | cbLORA_PROPERTY_REQ *onLORA_PROPERTY_REQ; 80 | 81 | void QueueResponse (LORACMDID cmdId, void* pData, uint16_t len); 82 | void Send (void); 83 | void ReceiveReq (void); 84 | void Timer (void); 85 | }; 86 | #endif //_LORANODE_H 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LoRa 2 | An Arduino Library for peer-to-peer LoRa data communication for home automation purpose. 3 | 4 | This library sits on top of my [SX126x](https://github.com/tinytronix/SX126x) LoRa driver and implements a C++ class for a Lora gateway and a C++ class for multiple Lora nodes. Both classes implement their part of a communication protocol to let both talk to each other. 5 | 6 | It can be used to set up a private LoRa network for e.g. home automation infrastructure which consists of one gateway and arbitrary nodes like sensors, actors and so on. 7 | 8 | # Features 9 | - low protocol overhead (only 7 bytes) 10 | - 32Bit radio id for each LoRa node 11 | - physical devices can have more than one radio id (logical device support) 12 | - end to end message transaction handle 13 | - up to 121 Bytes of payload 14 | - receive confirmation 15 | - message retransmission if confirmation fails 16 | - optional encryption (decide at compile time) 17 | - callback interface 18 | - tx power adaption 19 | - round trip (request -> confirmation) ~70ms at SF5, 125kHz 20 | 21 | In general all communication is initiated by the gateway (data REQUEST). The nodes can only send (data RESPONSE) if they 22 | were adressed by the gateway. 23 | 24 | ## Prerequisites 25 | - Install the SX126x Lora driver into Arduino IDE (https://github.com/tinytronix/SX126x) 26 | - Install SpritzCipher into Arduino IDE if you want Encryption (https://github.com/abderraouf-adjal/ArduinoSpritzCipher) 27 | - Install QList into Arduino IDE (https://github.com/SloCompTech/QList) 28 | 29 | ## Where to start 30 | Please refer to the [gateway](https://github.com/tinytronix/LoRa/blob/master/examples/gateway.ino) and [node](https://github.com/tinytronix/LoRa/blob/master/examples/node.ino) example! 31 | 32 | ## FAQ 33 | Q: Is this thing LoraWAN compatible?
34 | A: No. You may use the SX126x driver and the hardware as a base for a LoRaWAN device but this projects does not support LoRaWAN. 35 | The gateway and the node software implement an completely different (home brewed) communication protocol.
36 | -------------------------------------------------------------------------------- /examples/gateway.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | * 5 | * This example can be used for the lora gateway 6 | * (in the Arduino IDE set board to "Arduino Pro or Pro Mini") 7 | * 8 | */ 9 | 10 | //------------------------------------------------------------------------------------- 11 | // 12 | // Defines 13 | // 14 | //------------------------------------------------------------------------------------- 15 | #define TASK_TIME_MS 25 16 | #define TIME_SECONDS(x) ((x) * (1000/TASK_TIME_MS)) 17 | #define TIME_MILLISECONDS(x) ((x) / TASK_TIME_MS) 18 | 19 | #define RF_FREQUENCY 433000000 // Hz center frequency 20 | #define TX_OUTPUT_POWER 22 // dBm tx output power 21 | /* Langsame Datenrate 22 | #define LORA_BANDWIDTH 4 // bandwidth=125khz 0:250kHZ,1:125kHZ,2:62kHZ,3:20kHZ.... look for radio line 392 23 | #define LORA_SPREADING_FACTOR 7 // spreading factor=11 [SF5..SF12] 24 | #define LORA_CODINGRATE 4 // [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] 25 | */ 26 | #define LORA_BANDWIDTH 6 // bandwidth=125khz 0:250kHZ,1:125kHZ,2:62kHZ,3:20kHZ.... look for radio line 392 27 | #define LORA_SPREADING_FACTOR 5 // spreading factor=11 [SF5..SF12] 28 | #define LORA_CODINGRATE 4 // [1: 4/5,2: 4/6,3: 4/7, 4: 4/8] 29 | 30 | #define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx 31 | #define LORA_SYMBOL_TIMEOUT 0 // Symbols 32 | #define LORA_FIX_LENGTH_PAYLOAD_ON false // variable data payload 33 | #define LORA_IQ_INVERSION_ON false 34 | #define LORA_PAYLOADLENGTH 0 // 0: variable receive length 35 | // 1..255 payloadlength 36 | 37 | 38 | 39 | //------------------------------------------------------------------------------------- 40 | // 41 | // globale Objekte 42 | // 43 | //------------------------------------------------------------------------------------- 44 | LoraGateway lora(PD5, //Port-Pin Output: SPI select 45 | PD6, //Port-Pin Output: Reset 46 | PD7, //Port-Pin Input: Busy 47 | PB0 //Port-Pin Input: Interrupt DIO1 48 | ); 49 | 50 | CIPHERKEY cipher; 51 | 52 | uint8_t data; 53 | uint8_t* pRxData; 54 | uint8_t rxLen; 55 | uint8_t txTimer; 56 | 57 | //------------------------------------------------------------------------------------- 58 | // 59 | // setup 60 | // 61 | //------------------------------------------------------------------------------------- 62 | void setup() 63 | { 64 | rxLen = 0; 65 | txTimer = 0; 66 | Serial.begin(9600); 67 | delay(500); 68 | 69 | Serial.println("\n"); 70 | Serial.print("LoRa Gateway"); 71 | 72 | lora.begin(TASK_TIME_MS, //important to maintain RX TX timing in the Service Function 73 | SX126X_PACKET_TYPE_LORA, 74 | RF_FREQUENCY, //frequency in Hz 75 | TX_OUTPUT_POWER); //tx power in dBm 76 | 77 | //for encryption #define LORANODE_NO_ENCRYPT before including LoraGateway.h 78 | //cipher = {0x00, 0x00, 0x00}; 79 | //lora.EncryptDecryptKey(&cipher); 80 | 81 | lora.LoRaConfig(LORA_SPREADING_FACTOR, 82 | LORA_BANDWIDTH, 83 | LORA_CODINGRATE, 84 | LORA_PREAMBLE_LENGTH, 85 | LORA_PAYLOADLENGTH, 86 | true, //crcOn 87 | false //invertIrq 88 | ); 89 | } 90 | 91 | 92 | //------------------------------------------------------------------------------------- 93 | // 94 | // main loop 95 | // 96 | //------------------------------------------------------------------------------------- 97 | void loop() 98 | { 99 | if ( txTimer == 0 ) 100 | { 101 | ACTOR_REQ req; 102 | 103 | req.id = 3; //Actor id 104 | req.action = 1; //Actor action 105 | 106 | //triggers callback onLORA_ACTOR_REQ(uint16_t id, uint8_t action) 107 | //on client node side 108 | lora.Send(0x12345678, //ID of Lora Node device 109 | LORA_ACTOR_REQ, //Command ID 110 | (uint8_t*)&req, //pointer to data 111 | (uint8_t)sizeof(ACTOR_REQ)); //length 112 | 113 | 114 | txTimer = TIME_SECONDS(1); 115 | } 116 | else 117 | { 118 | txTimer--; 119 | } 120 | 121 | lora.Service(); 122 | 123 | rxLen = lora.GetResponseData(&pRxData); 124 | if ( rxLen ) 125 | Serial.println("Response: LORA_ACTOR_RESP"); 126 | 127 | delay(TASK_TIME_MS); 128 | } 129 | -------------------------------------------------------------------------------- /examples/node.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | * 5 | * This example can be used for node components: 6 | * - LoraBridge Module (in the Arduino IDE set board to "Arduino Pro or Pro Mini") 7 | * - LoraInwallShuttermodule (uses internal clock, needs this bootloader: https://www.arduino.cc/en/Tutorial/ArduinoToBreadboard) 8 | * - LoraPowerswitch (uses internal clock, needs this bootloader: https://www.arduino.cc/en/Tutorial/ArduinoToBreadboard) 9 | */ 10 | 11 | //------------------------------------------------------------------------------------- 12 | // 13 | // Defines 14 | // 15 | //------------------------------------------------------------------------------------- 16 | #define TASK_TIME_MS 25 17 | #define TIME_SECONDS(x) ((x) * (1000/TASK_TIME_MS)) 18 | #define TIME_MILLISECONDS(x) ((x) / TASK_TIME_MS) 19 | 20 | 21 | #define RF_FREQUENCY 433000000 // Hz center frequency 22 | #define TX_OUTPUT_POWER 22 // dBm tx output power 23 | /* Langsame Datenrate 24 | #define LORA_BANDWIDTH 4 // bandwidth=125khz 0:250kHZ,1:125kHZ,2:62kHZ,3:20kHZ.... look for radio line 392 25 | #define LORA_SPREADING_FACTOR 7 // spreading factor=11 [SF5..SF12] 26 | #define LORA_CODINGRATE 4 // [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] 27 | */ 28 | #define LORA_BANDWIDTH 6 // bandwidth=125khz 0:250kHZ,1:125kHZ,2:62kHZ,3:20kHZ.... look for radio line 392 29 | #define LORA_SPREADING_FACTOR 5 // spreading factor=11 [SF5..SF12] 30 | #define LORA_CODINGRATE 4 // [1: 4/5,2: 4/6,3: 4/7, 4: 4/8] 31 | 32 | #define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx 33 | #define LORA_SYMBOL_TIMEOUT 0 // Symbols 34 | #define LORA_FIX_LENGTH_PAYLOAD_ON false // variable data payload 35 | #define LORA_IQ_INVERSION_ON false 36 | #define LORA_PAYLOADLENGTH 0 // 0: variable receive length 37 | // 1..255 payloadlength 38 | 39 | 40 | //------------------------------------------------------------------------------------- 41 | // 42 | // globale Typdefinitionen 43 | // 44 | //------------------------------------------------------------------------------------- 45 | 46 | 47 | //------------------------------------------------------------------------------------- 48 | // 49 | // globale Objekte 50 | // 51 | //------------------------------------------------------------------------------------- 52 | LoraNode lora(PD5, //Port-Pin Output: SPI select 53 | PD6, //Port-Pin Output: Reset 54 | PD7, //Port-Pin Input: Busy 55 | PB0 //Port-Pin Input: Interrupt DIO1 56 | ); 57 | 58 | 59 | 60 | //------------------------------------------------------------------------------------- 61 | // 62 | // globale Objekte 63 | // 64 | //------------------------------------------------------------------------------------- 65 | void setup() 66 | { 67 | Serial.begin(9600); 68 | delay(500); 69 | 70 | Serial.println("\n"); 71 | Serial.print("LoRa Node"); 72 | 73 | lora.begin(TASK_TIME_MS, 74 | SX126X_PACKET_TYPE_LORA, 75 | RF_FREQUENCY, //frequency in Hz 76 | TX_OUTPUT_POWER); //tx power in dBm 77 | 78 | //for encryption #define LORANODE_NO_ENCRYPT before including LoraGateway.h 79 | //cipher = {0x00,0x00,0x00}; 80 | //lora.EncryptDecryptKey(&cipher); 81 | 82 | lora.LoRaConfig(LORA_SPREADING_FACTOR, 83 | LORA_BANDWIDTH, 84 | LORA_CODINGRATE, 85 | LORA_PREAMBLE_LENGTH, 86 | LORA_PAYLOADLENGTH, 87 | true, //crcOn 88 | false //invertIrq 89 | ); 90 | 91 | lora.RegisterCallback(0x12345678, //Lora node ID 92 | LORA_ACTOR_REQ, //Lora command id 93 | (void*)onLORA_ACTOR_REQ);//callback function for command LORA_ACTOR_REQ 94 | } 95 | 96 | 97 | //------------------------------------------------------------------------------------- 98 | // 99 | // onLORA_ACTOR_REQ 100 | // This callback is called whenever a LORA_ACTOR_REQ message from the gateway 101 | // was received. The callback is called from lora.Service() so it is synchron to 102 | // the arduino main loop. 103 | //------------------------------------------------------------------------------------- 104 | void onLORA_ACTOR_REQ(uint16_t id, uint8_t action) 105 | { 106 | Serial.println("onLORA_ACTOR_REQ"); 107 | lora.Send_LORA_ACTOR_RESP(1); 108 | } 109 | 110 | 111 | //------------------------------------------------------------------------------------- 112 | // 113 | // loop 114 | // 115 | //------------------------------------------------------------------------------------- 116 | void loop() 117 | { 118 | lora.Service(); 119 | delay(TASK_TIME_MS); 120 | } 121 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | # Schlüsselwörter für Datentypen: 2 | LoraNode KEYWORD1 3 | # Schlüsselwörter für Methoden: KEYWORD2 4 | begin KEYWORD2 5 | LoRaConfig KEYWORD2 6 | Receive KEYWORD2 7 | Send KEYWORD2 8 | ReceiveStatus KEYWORD2 -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LoraNode", 3 | "frameworks": "Arduino", 4 | "keywords": "SX1268, SX1262, DRF1268T, SX1262T, LoRa, Lora", 5 | "description": "Implement a homeautomation protocol using the SX126x Arduino library", 6 | "authors": 7 | [ 8 | { 9 | "name": "Frank.Engel", 10 | "email": "frank.engel@gmx.de", 11 | "url": "", 12 | "maintainer": true 13 | }, 14 | ], 15 | "repository": 16 | { 17 | "type": "git", 18 | "url": "https://github.com/tinytronix/LoraNode" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=LoraNode 2 | version=1.0.0 3 | author=tinytronix 4 | maintainer=tinytronix 5 | sentence=Homeautomation Protocol for SX126x driver library 6 | paragraph= 7 | category=Sensor 8 | url= 9 | architectures=* 10 | --------------------------------------------------------------------------------