Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: Turtle_RadioShuttle
RadioShuttle.h
00001 /* 00002 * This is an unpublished work copyright 00003 * (c) 2019 Helmut Tschemernjak 00004 * 30826 Garbsen (Hannover) Germany 00005 * 00006 * 00007 * Use is granted to registered RadioShuttle licensees only. 00008 * Licensees must own a valid serial number and product code. 00009 * Details see: www.radioshuttle.de 00010 */ 00011 00012 #ifndef __RADIOSHUTTLE_H__ 00013 #define __RADIOSHUTTLE_H__ 00014 00015 #ifdef ARDUINO 00016 #include "arduino-mbed.h" 00017 #include <time.h> 00018 #include <assert.h> 00019 #undef min 00020 #undef max 00021 #undef map 00022 #define MyTimeout Timeout 00023 #define MyTimer Timer 00024 #define STATIC_ASSERT _Static_assert 00025 #define ASSERT assert 00026 using namespace std; 00027 #define FEATURE_LORA 1 00028 00029 #else 00030 00031 #if defined(DEVICE_LPTICKER) || defined(DEVICE_LOWPOWERTIMER) // LOWPOWERTIMER in older mbed versions 00032 #define MyTimeout LowPowerTimeout 00033 #define MyTimer LowPowerTimer 00034 #else 00035 #define MyTimeout Timeout 00036 #define MyTimer Timer 00037 #endif 00038 00039 #endif 00040 00041 #include <list> 00042 #include <map> 00043 #include "radio.h" 00044 #include "RadioStatusInterface.h" 00045 #include "RadioSecurityInterface.h" 00046 00047 #ifdef ARDUINO 00048 #define map std::map // map clashes with Arduino map() 00049 #endif 00050 #ifdef FEATURE_LORA 00051 00052 00053 typedef enum RSErrorCode { 00054 RS_NoErr = 0, 00055 RS_DuplicateAppID, 00056 RS_AppID_NotFound, 00057 RS_StationNotConnected, 00058 RS_NoPasswordSet, 00059 RS_PasswordSet, 00060 RS_NoSecurityInterface, 00061 RS_MsgID_NotFound, 00062 RS_NoRadioConfigured, // No radio added so far 00063 RS_NoRadioAvailable, // Radio could not be detected 00064 RS_RadioNotFound, // The specified radio is not available 00065 RS_UnknownModemType, // No FSK no LoRa modem type 00066 RS_MessageSizeExceeded, // Message size too long 00067 RS_InvalidProductCode, // license does not match 00068 RS_InvalidParam, // invalid parameter. 00069 RS_OutOfMemory, // unable to allocate memory 00070 } RSCode; 00071 00072 00073 00074 // Shuttle 00075 class RadioShuttle 00076 { 00077 public: 00078 typedef uint32_t devid_t; 00079 static const int DEV_ID_ANY = 0; 00080 static const int TX_POWER_AUTO = 9999; 00081 00082 struct RadioProfile { 00083 int Frequency; // in Hz 00084 int Bandwidth; // in Hz 00085 int TXPower; // in dBm 00086 int SpreadingFaktor;// 7-12 00087 int FrequencyOffset;// +/- in Hz 00088 }; 00089 00090 enum RadioType { 00091 RS_RadioType_Invalid = 0, 00092 RS_Node_Offline, // Sleep mode until sending, < 10k RAM 00093 RS_Node_Checking, // Sleep mode, checks for messages regulary, < 10k RAM 00094 RS_Node_Online, // Always powered-on, < 10k RAM 00095 RS_Station_Basic, // Always active for one or more apps < 15k RAM 00096 RS_Station_Server, // Always active, lots of memory, routing options, < 1 MB RAM 00097 }; 00098 00099 typedef void (*AppRecvHandler)(int AppID, devid_t stationID, int msgID, int status, void *data, int length); 00100 00101 00102 enum MsgStatus { 00103 MS_SentCompleted, // A previous SendMsg has been sent 00104 MS_SentCompletedConfirmed, // A previous SendMsg has been sent and confirmed 00105 MS_SentTimeout, // A timeout occurred, number of retries exceeded 00106 00107 MS_RecvData, // A simple input message 00108 MS_RecvDataConfirmed, // Received a confirmed message 00109 MS_NoStationFound, 00110 MS_NoStationSupportsApp, 00111 MS_AuthenicationRequired, 00112 00113 MS_StationConnected, // A confirmation that the connection was accepted 00114 MS_StationDisconnected, // A confirmation that the disconnect was accepted 00115 }; 00116 00117 enum MsgFlags { 00118 MF_Response = 0x01, // A response from a previous request, or request 00119 MF_NeedsConfirm = 0x02, // Station needs to acknloglage receivd 00120 MF_LowPriority = 0x04, // Transfer within one minute 00121 MF_HighPriority = 0x08, // ImmEdate transfer 00122 MF_MoreDataToCome = 0x10, // Additional data is wait to be sent 00123 MF_Connect = 0x20, // Connect a node to the station with password 00124 MF_Encrypted = 0x40, // Message is encrypted 00125 MF_Authentication = 0x80, // Message requires prior authentication 00126 MF_SwitchOptions = 0x100,// Tell the node to switch the channel for this trans ID 00127 MF_FlagsMask = 0b111111111, // max flags for the RadioShuttle protocol, see msgFlags : 9 00128 /* 00129 * optional control flags are here. 00130 */ 00131 CF_FreeData = 0x200, // if a data buffer is provided, free it afterwords. 00132 CF_CopyData = 0x400, // create a copy of the data. 00133 }; 00134 00135 struct RadioStats { 00136 int RxPackets; 00137 int TxPackets; 00138 int RxErrorCount; 00139 int channelBusyCount; 00140 long long rxBytes; 00141 long long txBytes; 00142 int noAuthMessageCount; 00143 int unkownMessageCount; 00144 int appNotSupported; 00145 int protocolError; 00146 int noMemoryError; 00147 int decryptError; 00148 int lastRSSI; 00149 int lastSNR; 00150 devid_t lastRXdeviceID; 00151 time_t startupTime; 00152 }; 00153 00154 /* 00155 * Constructor requires a radio 00156 */ 00157 RadioShuttle(const char *deviceName); 00158 00159 /* 00160 * Destructor, stop radios, free resources 00161 */ 00162 ~RadioShuttle(); 00163 00164 /* 00165 * Adds a license to use the RadioShuttle, licenses are 00166 * may be bundled with the board or are available for 00167 * purchase at: www.radioshuttle.com 00168 * The license is bound to a special board ID and a fixed 00169 * node deviceID. 00170 */ 00171 RSCode AddLicense(devid_t deviceID, uint32_t productCode); 00172 00173 /* 00174 * Adds a new radio with a given profile 00175 * Optional profile, must be called before startup 00176 */ 00177 RSCode AddRadio(Radio *radio, RadioModems_t modem, const struct RadioProfile *profile = NULL); 00178 00179 /* 00180 * This allows to swtich between RS_Node_Offline and RS_Node_Online 00181 * after the Startup() is already completed. 00182 */ 00183 RSCode UpdateNodeStartup(RadioType newRadioType); 00184 00185 /* 00186 * The status interface allows custom status callbacks for 00187 * reporting send/receive/timeout activity e.g.: LED blinks 00188 */ 00189 RSCode AddRadioStatus(RadioStatusInterface *statusIntf); 00190 00191 /* 00192 * Support function for password hash generation and encryption 00193 * The external API interface has the advantage that the security 00194 * can be upgraded independently of the RadioShuttle library. 00195 * AES hardware accelleration can be one reason for it. 00196 */ 00197 RSCode AddRadioSecurity(RadioSecurityInterface *securityIntf); 00198 00199 /* 00200 * Starts the service with the specified RadioType 00201 * The optional deviceID allows to specify a custom ID, e.g. for failover. 00202 */ 00203 RSCode Startup(RadioType radioType, devid_t deviceID = 0); 00204 00205 /* 00206 * get the current radio type 00207 */ 00208 RadioType GetRadioType(void); 00209 00210 /* 00211 * Registers an application, the AppType is unique worldwide 00212 * and must be used for sending and receiving app data. 00213 * Multiple AppID registrations are supported. 00214 * The password can be a string to the password, 00215 * in case of binary passwd data the pwLen must be set. 00216 * If the password is set, clients must call Connect() prior 00217 * to any SendMsg. 00218 */ 00219 RSCode RegisterApplication(int AppID, AppRecvHandler, void *password = NULL, int pwLen = 0); 00220 /* 00221 * Removes the AppID 00222 */ 00223 RSCode DeRegisterApplication(int AppID); 00224 00225 /* 00226 * Check if the password is specified for an app 00227 */ 00228 RSCode AppRequiresAuthentication(int AppID); 00229 00230 /* 00231 * Start a pairing process to connect a node to a station 00232 */ 00233 RSCode StartPairing(char *name = NULL); 00234 /* 00235 * Connect the node against a station, it can be called multiple times 00236 * if communication with multiple station IDs is used. 00237 * The connect verifies the password against the app of remote station. 00238 */ 00239 RSCode Connect(int AppID, devid_t stationID = DEV_ID_ANY); 00240 00241 /* 00242 * Inform the station that an app is discontinuing 00243 */ 00244 RSCode Disconnect(int AppID, devid_t stationID = ~0); 00245 00246 /* 00247 * Prepare sending data to a remote application 00248 * The request is queued for sending. 00249 * valid flags see enum MsgFlags 00250 * txPower allows to overwrite the txPower in dBm. 00251 * The data is busy until the AppRecvHandler is called 00252 */ 00253 RSCode SendMsg(int AppID, void *data, int len, int flags = 0, devid_t stationID = DEV_ID_ANY, int txPower = TX_POWER_AUTO, int *msgID = NULL); 00254 /* 00255 * Removes a message from the queue 00256 */ 00257 RSCode KillMsg(int AppID, int msgID); 00258 00259 /* 00260 * Sets a new profile for a given radio with a given profile 00261 * This can be called anytime after the RadioShuttle startup. 00262 */ 00263 RSCode UpdateRadioProfile(Radio *radio, RadioType radioType, const struct RadioProfile *profile); 00264 00265 /* 00266 * Sets the size value to the largest messages available 00267 * for all configured radios 00268 * The flags are important because encrypted messages need more space 00269 */ 00270 RSCode MaxMessageSize(int *size, int msgFlags = 0); 00271 00272 /* 00273 * Get statistics of all messages and errors 00274 * A pointer reference containing the RadioStats must be provided. 00275 * The statistics contain the information of the first radio, unless 00276 * the RadioEntry parameter is provided for a specified radio. 00277 */ 00278 RSCode GetStatistics(struct RadioStats **stats, Radio *radio = NULL); 00279 00280 00281 /* 00282 * Dump all received and sent packets 00283 */ 00284 void EnablePacketTrace(devid_t stationID = DEV_ID_ANY, bool sents = false, bool recvs = false, Radio *radio = 0); 00285 00286 /* 00287 * The RadioShuttle is idle when there are no ongoing jobs, etc. 00288 */ 00289 bool Idle(void); 00290 00291 /* 00292 * Converts a RadioShuttle error code into a string. 00293 */ 00294 const char *StrError(RSErrorCode err); 00295 00296 /* 00297 * Converts the radio type into a clear text name 00298 */ 00299 const char *GetRadioName(RadioType radioType); 00300 00301 /* 00302 * Starts the main RadioShuttle loop, returns 0 when nothing needs to be done 00303 * Retuns > 0 when it should be called again 00304 * RunShuttle is called on user level (non-interrupt level) 00305 */ 00306 int RunShuttle(void); 00307 00308 private: 00309 enum PacketStatus { 00310 PS_Queued, 00311 PS_Sent, 00312 PS_GotSendSlot, 00313 PS_WaitForConfirm, 00314 PS_SendRequestCompleted, 00315 PS_SendRequestConfirmed, 00316 PS_SendTimeout, 00317 }; 00318 00319 struct RadioEntry; // forward decl. 00320 struct ReceivedMsgEntry { 00321 void *RxData; 00322 int RxSize; 00323 int rssi; 00324 int snr; 00325 struct RadioEntry *re; 00326 }; 00327 00328 00329 struct RadioEntry { 00330 Radio *radio; 00331 RadioEvents_t radioEvents; 00332 const RadioProfile *profile; 00333 RadioModems_t modem; 00334 volatile signed char _CADdetected; 00335 uint16_t lastTxSize; 00336 int lastTxPower; 00337 int timeOnAir12Bytes; 00338 struct ReceivedMsgEntry rxMsg; 00339 struct RadioStats rStats; 00340 int maxTimeOnAir; 00341 int retry_ms; 00342 uint32_t lastTxDone; 00343 volatile bool txDoneReceived; 00344 volatile const char *intrDelayedMsg; 00345 uint32_t random; 00346 uint32_t random2; 00347 }; 00348 00349 struct AppEntry { 00350 int AppID; 00351 AppRecvHandler handler; 00352 int msgID; 00353 void *password; 00354 uint8_t pwLen; 00355 bool pwdConnected; 00356 }; 00357 00358 struct ConnectEntry { 00359 devid_t stationID; 00360 int AppID; 00361 bool authorized; 00362 uint32_t random[2]; 00363 }; 00364 00365 struct SendMsgEntry { 00366 int AppID; 00367 void *data; 00368 int len; 00369 int flags; 00370 devid_t stationID; 00371 int txPower; 00372 int msgID; 00373 int retryCount; 00374 bool releaseData; 00375 AppEntry *aep; 00376 ConnectEntry *cep; 00377 PacketStatus pStatus; 00378 int respWindow; 00379 uint32_t responseTime; 00380 uint32_t lastSentTime; 00381 uint32_t lastTimeOnAir; 00382 uint32_t confirmTimeout; 00383 int retry_ms; 00384 uint8_t channel; 00385 uint8_t factor; 00386 uint32_t securityData[8]; 00387 uint32_t tmpRandom[2]; 00388 }; 00389 00390 struct SignalStrengthEntry { 00391 int rx_dBm; 00392 devid_t stationID; 00393 time_t lastUpdate; 00394 int rcnCnt; 00395 }; 00396 00397 struct TimeOnAirSlotEntry { 00398 devid_t stationID; 00399 int AppID; 00400 uint8_t channel; 00401 uint8_t factor; 00402 uint32_t busy_time; 00403 int busy_ms; 00404 }; 00405 00406 struct EncryptionHeader { 00407 uint32_t version : 3; // 3-bit encryption version 00408 uint32_t dataSum : 13; // Checksum of all packet data 00409 uint16_t msgSize : 11; // Same as in RadioHeader 00410 uint16_t msgID : 5; // Same as in RadioHeader 00411 uint32_t random; 00412 }; 00413 00414 enum RadioHeaderVersions { 00415 RSMagic = 0b1011, 00416 RSHeaderFully_v1 = 0b001, 00417 RSHeaderPacked_v1 = 0b010, 00418 RSHeaderFullySize_v1 = 16, 00419 RSHeaderPackedSize_v1 = 12, 00420 msgIDv1Mask = 0b11111, 00421 Packedv1MaxApps = (1<<11)-1, 00422 Packedv1MaxRespWindow = (1<<11)-1, 00423 Fullyv1MaxRespWindow = (1<<16)-1, 00424 Packedv1MaxDeviceID = (1<<21)-1, 00425 MaxWinScale = (1<<4)-1, 00426 DataSumBits = 13, 00427 }; 00428 00429 /* 00430 * The RadioShuttle communication header (similar to UDP), 00431 * but optimized for radios. 00432 * A packet crc checksum is done on the link layer, no field needed here 00433 * The source always sends this request to the other side 00434 * - Check immediately for a response after sending 00435 * - Check again for a response in responseWindow*2 milliseconds 00436 * 00437 */ 00438 struct RadioHeader { 00439 // 4 bytes 00440 uint16_t magic : 4; // 4-bit magic, 00441 uint16_t version : 3; // 3-bit version 00442 uint16_t msgFlags: 9; // e.g.: Request/Response NeedsConfirm, Priority, Encrypted 00443 union { 00444 struct { 00445 uint16_t msgSize : 11; // 11-bit message size allows a maximum of 2048 bytes 00446 uint16_t msgID : 5; // ID for the app communication, truncated to 5-bit 00447 } data; 00448 struct { 00449 uint16_t channel : 4; // Specify the channel to switch to for this transaction 00450 uint16_t factor : 3; // Specifies the spreading factor index (6-12) 00451 uint16_t winScale: 4; // Set the window scaling factor 00452 } option; 00453 } s; 00454 union { 00455 struct { // 8 bytes 00456 uint32_t appID : 11; // First 2048 application identifiers for the request 00457 devid_t destination : 21; // Device ID of the destination 2^11 first 2 million devices 00458 uint32_t respWindow : 11; // Wait for the respWindow (ms) max 2048 ms before sending data. 00459 devid_t source : 21; // Device ID of the destination 2^11 first 2 million devices 00460 } packedv1; 00461 struct { // 12 bytes 00462 uint16_t appID; // Application identifier for the request 00463 uint16_t respWindow; // We listen for a 2nd resonse in responseWindow (ms) 00464 devid_t destination; // Device ID of the destination 00465 devid_t source; // DeviceID of the source 00466 } fullyv1; 00467 } u; 00468 }; 00469 00470 struct WireDumpSettings { 00471 devid_t stationID; 00472 bool sents; 00473 bool recvs; 00474 Radio *radio; 00475 }; 00476 00477 /* 00478 * Radio specific callbacks are public to allow C wrapper function to call us. 00479 */ 00480 public: 00481 void RS_TxDone(Radio *radio, void *userData); 00482 void RS_RxDone(Radio *radio, void *userData, uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr); 00483 void RS_TxTimeout(Radio *radio, void *userData); 00484 void RS_RxTimeout(Radio *radio, void *userData); 00485 void RS_RxError(Radio *radio, void *userData); 00486 void RS_CadDone(Radio *radio, void *userData, bool channelActivityDetected); 00487 00488 private: 00489 /* 00490 * The CadDetection will turn the radio into a Cad listening mode 00491 * to detect radio-modulated signals on the channel. 00492 * This takes a few ms (at SF7) and the interrupt RS_CadDone will report 00493 * the status. 00494 * The radio->_CADdetected contains the result (0 for idle, 1 for busy) 00495 */ 00496 bool CadDetection(RadioEntry *re); 00497 00498 /* 00499 * init the RX/TX of the Radio. 00500 */ 00501 RSCode _initRadio(RadioEntry *re); 00502 00503 /* 00504 * The processing function returns a status code if it has been able to process 00505 * this message. true' for messages processed, false for messages skipped. 00506 */ 00507 bool ProcessResponseMessage(ReceivedMsgEntry *rme, AppEntry *aep, SendMsgEntry *mep, int msgFlags, void *data, int len, devid_t source, devid_t respWindow, uint8_t channel, uint8_t factor); 00508 00509 bool ProcessRequestMessage(ReceivedMsgEntry *rme, AppEntry *aep, int msgFlags, void *data, int len, int msgID, devid_t source, uint32_t respWindow, uint8_t channel, uint8_t factor); 00510 00511 void MessageSecurityError(ReceivedMsgEntry *rme, AppEntry *aep, int msgID, devid_t source, uint8_t channel, uint8_t factor); 00512 00513 void SaveTimeOnAirSlot(devid_t destination, int AppID, int msgFlags, int respWindow, uint8_t channel, uint8_t factor, int timeOnAir); 00514 00515 /* 00516 * Our main send function is responsible for header packing, 00517 * compression and encryption, and finally sends a packet via the radio. 00518 * It returns true if we have been able to sent the message. 00519 */ 00520 bool SendMessage(RadioEntry *re, void *data, int len, int msgID, int AppID, devid_t stationID, int flags, int txPower, int respWindow, uint8_t channel, uint8_t factor); 00521 00522 /* 00523 * We keep a little cache list of the power needed for different stations 00524 * This saves a lot of energy and reduces signal strength to keep the network 00525 * less busy. For example, other radio networks need not receive our signals. 00526 */ 00527 int CalculateTXPower(RadioEntry *re, devid_t stationID); 00528 bool UpdateSignalStrength(devid_t stationID, int dBm); 00529 bool DeleteSignalStrength(devid_t stationID); 00530 00531 00532 /* 00533 * Our main receive function is responsible for header unpacking, 00534 * de-compression and decryption, and finally provides the unpacked data. 00535 * It returns true if we have been able to detect and unpack the message. 00536 */ 00537 bool ReceiveMessage(ReceivedMsgEntry *rme, void **data, int &len, int &msgID, int &AppID, int &flags, devid_t &destination, devid_t &source, int &respWindow, uint8_t &channel, uint8_t &factor); 00538 00539 /* 00540 * We need to process all input messages, the _recv list should be empty ASAP 00541 * because the data is only temporarily available until the next packet. 00542 */ 00543 void ProcessReceivedMessages(void); 00544 00545 00546 void PacketTrace(RadioEntry *re, const char *name, RadioHeader *rh, void *data, int len, bool sent, ReceivedMsgEntry *rme); 00547 00548 /* 00549 * A dummy function which is called for every timeout, the timer wakes up 00550 * the sleep and therefore the RunShuttle starts processing. 00551 */ 00552 void TimeoutFunc(void); 00553 00554 uint32_t GetDataSum(int maxbits, void *data, int len); 00555 00556 00557 private: 00558 const char *_name; 00559 devid_t _deviceID; 00560 devid_t _tmpdeviceID; 00561 uint8_t _uuid[16]; 00562 RadioType _radioType; 00563 int _maxMTUSize; 00564 list<RadioEntry> _radios; 00565 map<int, AppEntry> _apps; 00566 map<std::pair<devid_t,int>, ConnectEntry> _connections; 00567 list<SendMsgEntry> _sends; 00568 list<ReceivedMsgEntry> _recvs; 00569 map<devid_t, SignalStrengthEntry> _signals; 00570 list<TimeOnAirSlotEntry> _airtimes; 00571 MyTimeout *timer; 00572 MyTimer *ticker; 00573 volatile uint32_t prevWakeup; 00574 int SetTimerCount; 00575 00576 static const RadioProfile defaultProfile[]; 00577 volatile bool busyInShuttle; 00578 WireDumpSettings _wireDumpSettings; 00579 const static int MAX_SENT_RETRIES = 3; // Defines the number of retries of sents (with confirm) 00580 const static int RX_TIMEOUT_1HOUR = 3600000; 00581 RadioStatusInterface *_statusIntf; 00582 RadioSecurityInterface *_securityIntf; 00583 uint32_t _; 00584 }; 00585 00586 #endif // __RADIOSHUTTLE_H__ 00587 #endif // FEATURE_LORA
Generated on Mon Jul 18 2022 05:09:52 by
1.7.2