Helmut Tschemernjak / RadioShuttle-STM32L4

Dependents:   Turtle_RadioShuttle

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers RadioShuttle.h Source File

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