├── README.md ├── docs ├── LoRa-zeichnung-4web_wifi-1.png └── RadioShuttleProtocol.rtf ├── examples ├── GenericPingPong.cpp ├── GenericPingPong.h ├── RadioTest.cpp ├── RadioTest.h └── RadioTest │ ├── RTCUtil.cpp │ ├── RadioTest.ino │ └── xPinMap.h ├── library.properties ├── src ├── RadioSecurity.cpp ├── RadioSecurity.h ├── RadioSecurityInterface.h ├── RadioShuttle.cpp ├── RadioShuttle.h ├── RadioStatus.cpp ├── RadioStatus.h └── RadioStatusInterface.h └── util ├── rs_aes.c ├── rs_aes.h ├── rs_sha256.c └── rs_sha256.h /README.md: -------------------------------------------------------------------------------- 1 | ![Title image](/docs/LoRa-zeichnung-4web_wifi-1.png) 2 | # RadioShuttleLib 3 | __Peer-to-peer LoRa wireless protocol software__ 4 | We developed a wireless protocol for peer-to-peer communication as well as server communication. The protocol is capable of efficiently sending messages between simple radio modules (e.g. LoRa) in a fast, secure and reliable way. The RadioShuttle protocol supports thousands of nodes, security via SHA256 based auhorization, and AES encrypted messages. This software is equally suitable as a node or as a server. RadioShuttle protocol offers an easy-to-use C++ API and turnkey sample applications for Arduino and Mbed OS. 5 | 6 | We do not make use of the LoRaWAN protocol because is lacks efficiency, does not support direct node-to-node communication, and is too costly and complicated for many applications. 7 | 8 | RadioShuttle integrates into standard MQTT environments via its MQTT gateway (ESP32 ECO Power board). It also supports MQTT push messages to mobile clients via our MQTT Push Client app for Android and iOS. 9 | 10 | We have a varity of RadioShuttle compatible boards available (LongRa, ECO Power and Turtle boards). The source code allows users to get an insight of our protocol. We support custom board developments utilizing the RadioShuttle protocol. Inquiries are welcome. 11 | 12 | Helmut Tschemernjak 13 | www.radioshuttle.de 14 | 15 | 16 | ## Prerequisites for testing the RadioShuttle protocol 17 | The easiest way to test the protocol is to get a pair of the RadioShuttle supported boards. The solution includes hardware and software ready to go. Everyone (even non-programmers) should be able to get example applications up and running. See details on www.radioshuttle.de 18 | 19 | ## Supported platforms 20 | - Mbed OS (STM32L4, STM32L0 ) 21 | - Arduino (ESP32, D21) 22 | - Linux planned 23 | - Supported boards: LongRa, ECO Power, and Turtle boards from RadioShuttle.de 24 | 25 | 26 | ## Supported radio drivers 27 | The library depends on a common radio driver. At present, the SX1276GenericLib is used: 28 | - https://github.com/RadioShuttle/SX1276GenericLib 29 | Custom radio drivers for other radio chipsets can be developed when using the same radio API as the SX1276GenericLib driver. 30 | 31 | 32 | ## TODOs 33 | - xx 34 | 35 | 36 | ## License and contributions 37 | Most of the software is provided under the Apache 2.0 license, see individual file headers. Contributions to this project are accepted under the same license. One protocol source file: RadioShuttle.cpp is under copyright of Helmut Tschemernjak and is subject to a license fee for any use. RadioShuttle boards customers are already licensed. 38 | 39 | 40 | ## Credits 41 | This protocol implementation has initially been written by the RadioShuttle engineers (www.radioshuttle.de). Many thanks to everyone who helped bringing this project forward. 42 | 43 | 44 | ## Links 45 | - RadioShuttle website: https://www.radioshuttle.de/ 46 | - The RadioShuttle wireless protocol: https://www.radioshuttle.de/en/radioshuttle-en/protocol/ 47 | - LoRa basics: https://www.radioshuttle.de/en/lora-en/basics/ 48 | - MQTT basics: https://www.radioshuttle.de/en/mqtt-en/the-basics/ 49 | - MQTT Push Client (app) https://www.radioshuttle.de/en/mqtt-en/mqtt-push-client-app/ 50 | - Supported radio chips: https://github.com/RadioShuttle/SX1276GenericLib 51 | -------------------------------------------------------------------------------- /docs/LoRa-zeichnung-4web_wifi-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RadioShuttle/RadioShuttleLib/9edda3b96d76af6b1713f26ccf879cb80f3c9dea/docs/LoRa-zeichnung-4web_wifi-1.png -------------------------------------------------------------------------------- /docs/RadioShuttleProtocol.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fnil\fcharset0 Menlo-Regular;} 3 | {\colortbl;\red255\green255\blue255;\red255\green0\blue0;} 4 | \paperw11900\paperh16840\margl1440\margr1440\vieww20480\viewh16280\viewkind0 5 | \pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0 6 | 7 | \f0\b\fs28 \cf0 The RadioShuttle protocol\ 8 | 9 | \b0 \ 10 | The protocol is designed to send little messages between two or more devices. For simplification, we call the pair 11 | \i node 12 | \i0 and 13 | \i station 14 | \i0 . Here is the communication order:\ 15 | \ 16 | 17 | \b a) Simple on-demand message (RS_Node_Online/RS_Node_Checking/RS_Node_Offline)\ 18 | 19 | \b0 1. Node sends a message request before sending the data, containing only the 20 | \f1\fs26 \CocoaLigature0 RadioHeader.\ 21 | \'93MF_Response\'94 must be switched off in the flags. The header contains\ 22 | the msgSize in the used RadioHeader, declaring hereby the expected data size,\ 23 | the app ID, a msgID, and a response Window.\ 24 | \ 25 | 2. The response from the station should be sent immediately with the\ 26 | \'93MF_Response\'94 flag. In case the response cannot be sent\ 27 | immediately, the response window is used by the station for the\ 28 | scheduled time to send the response. In case the station cannot\ 29 | handle the app it stays quiet and forgets about this request. The station\ 30 | response may contain the flag \'93MF_SwitchOptions\'94 which allows turning on \ 31 | additional options (channel, spreading factor and windowScale). An empty \ 32 | options field means no change.\ 33 | \ 34 | 3. After the node has received the station response, it knows when it is allowed \ 35 | to send the final message data. The stations provided respWindow tells\ 36 | the node when to send the data (usually when there is no other traffic scheduled).\ 37 | The data message can contain the \'93flag MF_MoreDataToCome\'94, to indicate that\ 38 | there are more messages pending for this app, In this case the station\ 39 | sends a confirmation message (see 4.) with a new respWindow to send the\ 40 | next data message.\ 41 | \ 42 | 4. Optional confirmation message. In case the request contains the \ 43 | flag \'93MF_SentCompletedConfirmed\'94 or \'93MF_MoreDataToCome\'94 the station must\ 44 | confirm the reception using the \'93MF_RecvDataConfirmed\'94 flag in a \ 45 | radio header response when requested. In case of \'93MF_MoreDataToCome\'94\ 46 | the response message has be to sent with a new respWindow to\ 47 | allow the node to send the next message. \ 48 | \ 49 | Example:\ 50 | Node Station\ 51 | - - - > Node to Station message request (\'93RadioHeader\'94 with 12 or 16 bytes)\ 52 | < - - - Station to Node response (only \'93RadioHeader\'94 with response window time)\ 53 | Channel switching may apply if \'93MF_SwitchOptions\'94 is specified\ 54 | - - - > Node data to Station (communication completed, header + data)\ 55 | < - - - Optionally acknowledge (when RadioHeader contains \'93 56 | \fs22 MF_NeedsConfirm\'94) 57 | \fs26 \ 58 | \ 59 | \ 60 | \ 61 | 62 | \f0\b\fs28 \CocoaLigature1 b) Simple on-demand message RS_StationBasic/RS_StationServer\ 63 | 64 | \b0 As the stations have an own recording when the air is busy they can just send messages\ 65 | anytime when there is no scheduled traffic for a given time window.\ 66 | 67 | \b \ 68 | c) Receiving messages for RS_Node_Online\ 69 | 70 | \b0 As these types of node are always powered on, they stay always in receive mode 71 | \f1\fs26 \CocoaLigature0 , 72 | \f0\fs28 \CocoaLigature1 which\ 73 | means they can handle message requests as described. The station basically sends a \ 74 | message to the 75 | \f1\fs26 \CocoaLigature0 \'93 76 | \f0\fs28 \CocoaLigature1 RS_Node_Online 77 | \f1\fs26 \CocoaLigature0 \'94 78 | \f0\fs28 \CocoaLigature1 including the data because the station knows when \ 79 | the air is free to send messages.\ 80 | 81 | \f1\fs26 \CocoaLigature0 \ 82 | Example:\ 83 | Node Station\ 84 | < - - - Station to Node message request (RadioHeader with 12 or 16 bytes)\ 85 | Channel switching may apply if \'93MF_SwitchOptions\'94 are specified\ 86 | - - - > Node to Station response (only RadioHeader with response window time = 0)\ 87 | < - - - Station to Node_Online (Request with data, header + data)\ 88 | - - - > Optionally acknowledge (when RadioHeader contains \'93 89 | \fs22 MF_NeedsConfirm\'94) 90 | \fs26 \ 91 | \ 92 | \ 93 | 94 | \f0\b\fs28 \CocoaLigature1 d) Receiving messages for RS_Node_Offline/RS_Node_Checking\ 95 | 96 | \b0 The node 97 | \f1\fs26 \CocoaLigature0 \'93 98 | \f0\fs28 \CocoaLigature1 RS_Node_Offline 99 | \f1\fs26 \CocoaLigature0 \'94 100 | \f0\fs28 \CocoaLigature1 is completely turned off (sleep mode) unless it starts. During the sleep period it will not be able to receive messages, unless it wakes up and sends a message request.\ 101 | \ 102 | The \'93RS_Node_Checking\'94 can send a checking window time slot where it repeatedly turns into receive\ 103 | mode 104 | \f1\fs26 \CocoaLigature0 , 105 | \f0\fs28 \CocoaLigature1 to allow listening to a RadioHeader packet at a specified time, e.g. every 90.010 seconds. The response of the checking window includes the current timestamp in milliseconds to allow to use correct sending slots. From time to time the checking node should repeat the checking window requests to update its timestamp in milliseconds. The time slot resolution is in milliseconds.\ 106 | In case 107 | \f1\fs26 \CocoaLigature0 \'93 108 | \f0\fs28 \CocoaLigature1 RS_Node_Checking 109 | \f1\fs26 \CocoaLigature0 \'94 110 | \f0\fs28 \CocoaLigature1 receives responses from multiple stations it should use the response from the station with the best rssi and the second best rssi signal. (Main and backup stations.)\ 111 | The checking node should be able to request the specified checking window, e.g. 6500 ms 112 | \f1\fs26 \CocoaLigature0 , 113 | \f0\fs28 \CocoaLigature1 \ 114 | and the station should respond with a window equal or less. The checking node should enter only in receive mode for reading the preamble and a few bytes (CAD detection). If no signal is active it\ 115 | goes to sleep until the next checking period arrives. (The duration of the preamble detection depends in the spreading factor and bandwidth.)\cf2 \ 116 | \cf0 \ 117 | 118 | \b Response windows\ 119 | 120 | \b0 To keep it simple without syncing the time of all devices, the response window is specified in milliseconds, the window time starts counting after the complete packet has been received. The sender needs to take into account the air duration of the packet, the receiver starts counting the response window after it received the message. The packet RadioHeader response window allows windows of up to 2048 ms, the \ 121 | full RadioHeader allows 65536 ms. The option \'93 122 | \f1\fs26 \CocoaLigature0 MF_SwitchOptions\'94 allows a winScale scaling which will multiply the windows size.\ 123 | \pard\tx543\pardeftab543\pardirnatural\partightenfactor0 124 | 125 | \fs22 \cf0 winScale 1 uses respWindow * 1\ 126 | winScale 2 uses respWindow * 2\ 127 | winScale 3 uses respWindow * 3\ 128 | \'85\ 129 | winScale 15 use respWindow * 15 130 | \fs26 \ 131 | \pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0 132 | \cf0 \ 133 | \ 134 | 135 | \f0\b\fs28 \CocoaLigature1 Password authentication\ 136 | 137 | \b0 Each app can use an optional password when specified with the RegisterApplication() call. The password can be a simple string or a block of binary data. For binary data it is required to specify the password length parameter. A 16 byte long random password is recommended or even 32 bytes to avoid simple password attacks. The password can be empty (NULL) which means there is no password authentication. If the app password is specified it must be identical on all nodes and station communicating together. When the password is set, the node must call Connect() with the app ID and the remote station ID. The password is never transferred directly over the air, instead of the client receives a random number which is used to create a hash code of the password and send the hash product for authentication to the station which compares the product with its password/random number hash.\ 138 | The RadioSecurityInterface specifies an API for providing the password hashing and data encryption algorithm. It uses SHA256 for passwords and AES128 for content encryption.\ 139 | \ 140 | 141 | \b Message content encryption\ 142 | 143 | \b0 If passwords are specified (see Password Authentication), message data encryption can be enabled per 144 | \f1\fs26 \CocoaLigature0 \'93 145 | \f0\fs28 \CocoaLigature1 SendMsg() 146 | \f1\fs26 \CocoaLigature0 \'94 147 | \f0\fs28 \CocoaLigature1 call specifying \'93MF_Encrypted\'94 in the flags field. For encrypting the data the RadioSecurityInterface provided security implementation uses AES 128-bit which ensures nobody in the middle can decrypt the messages and send fake messages. For non-encrypted messages the size of an app message can be as little as one byte. Encrypted messages are automatically padded to a multiple of 128-bit which means at 16 bytes per message.\ 148 | \ 149 | Encrypted messages considerations/disadvantages\ 150 | - Minimum data size is 16 bytes (consider that this is a 50 ms data transfer overhead using SF7, with SF11 it is many times longer).\ 151 | - The communication works only between one node and one station (connected pair)\ 152 | Non-encrypted messages can be sent to multiple stations (e.g.: broadcast messages for device ID 0)\ 153 | which can be processed by any station supporting this app ID, which allows failover solutions.\ 154 | - For simple data like a temperature sensor 155 | \f1\fs26 \CocoaLigature0 , 156 | \f0\fs28 \CocoaLigature1 or similar 157 | \f1\fs26 \CocoaLigature0 , 158 | \f0\fs28 \CocoaLigature1 it may not be required to use encrypted messages, however for security concerned messages (e.g. door alarm, light switch) encrypted messages are a must have.\ 159 | \ 160 | 161 | \b Device IDs\ 162 | 163 | \b0 Each node communicating with the RadioShuttle protocol must have a unique number \'96\'a0each node as well as each station. Think about this like MAC addresses. This unique device IDs are required to allow secure and reliable communication. RadioShuttle licenses will find the device ID labeled on the RadioShuttle enabled board with an access code 164 | \f1\fs26 \CocoaLigature0 , 165 | \f0\fs28 \CocoaLigature1 or can purchase licenses at www.radioshuttle.de.\ 166 | \ 167 | 168 | \b App IDs\ 169 | 170 | \b0 Each node or station can support multiple different services which are called app IDs (application IDs) which are unique worldwide. These app IDs are, similar to TCP/IP port numbers, just an ID to route communication messages. The worldwide unique app IDs can be obtained free of charge for developers at www.radioshuttle.de by agreeing to stay compatible with the RadioShuttle protocol.\ 171 | The central app registration has also the benefit to share the app data content with others, to ensure solution compatibility. However is not needed to share any app ID content details. Think about a basic communication that goes from one device ID (e.g. 1111) using app ID 1234 to device ID 2222 being received by app ID 1234 when available on this device. Node/stations receiving unknown app ID requests will automatically ignore these.\ 172 | \ 173 | 174 | \b Message Retry Count considerations\ 175 | 176 | \b0 The Radioshuttle protocol uses a retry count of three attempts to complete a message communication. It makes no sense to have this retry count adjustable because when there is not reliable communication the retry attempts just keeps the network busy. More over the message request/response is not retrying every single sent or acknowledge packet three times, if the message transaction does not complete it tries it again. Single packets like acknowledge and a receives message are not retried, instead the entire message transaction is retried when needed.\ 177 | \ 178 | In case the message transfers are not reliable and often time out check the following:\ 179 | - Is the antenna and its connection correct?\ 180 | - Is it a long distance than switch a a higher spreading factor (e.g. from SF7 to SF9 or up to SF11)\ 181 | - Maybe the bandwidth setting is to high (e.g. 500 kHz switch to a smaller bandwidth (125 kHz, lower than 125 kHz require a TCXO crystal)\ 182 | - Is the station location optimal?\ 183 | Lets say in a case covering a multi-family house, it may be a smart to but the station across the building. Another option is to put the station into the middle of the building. \ 184 | \ 185 | } -------------------------------------------------------------------------------- /examples/GenericPingPong.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "mbed.h" 3 | #include "xPinMap.h" 4 | #include "GenericPingPong.h" 5 | #if defined(DEVICE_LPTICKER) || defined(DEVICE_LOWPOWERTIMER) // LOWPOWERTIMER in older mbed versions 6 | #define MyTimeout LowPowerTimeout 7 | #else 8 | #define MyTimeout Timeout 9 | #endif 10 | #include "sx1276-mbed-hal.h" 11 | #include "main.h" 12 | 13 | #ifdef FEATURE_LORA_PING_PONG 14 | 15 | /* Set this flag to '1' to display debug messages on the console */ 16 | #define DEBUG_MESSAGE 1 17 | 18 | /* Set this flag to '1' to use the LoRa modulation or to '0' to use FSK modulation */ 19 | #define USE_MODEM_LORA 1 20 | #define USE_MODEM_FSK !USE_MODEM_LORA 21 | #define RF_FREQUENCY RF_FREQUENCY_868_1 // Hz 22 | #define TX_OUTPUT_POWER 14 // 14 dBm 23 | 24 | #if USE_MODEM_LORA == 1 25 | 26 | #define LORA_BANDWIDTH 125000 // see LoRa bandwidth map for details 27 | #define LORA_SPREADING_FACTOR LORA_SF7 28 | #define LORA_CODINGRATE LORA_ERROR_CODING_RATE_4_5 29 | 30 | #define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx 31 | #define LORA_SYMBOL_TIMEOUT 5 // Symbols 32 | #define LORA_FIX_LENGTH_PAYLOAD_ON false 33 | #define LORA_FHSS_ENABLED false 34 | #define LORA_NB_SYMB_HOP 4 35 | #define LORA_IQ_INVERSION_ON false 36 | #define LORA_CRC_ENABLED true 37 | 38 | #elif USE_MODEM_FSK == 1 39 | 40 | #define FSK_FDEV 25000 // Hz 41 | #define FSK_DATARATE 19200 // bps 42 | #define FSK_BANDWIDTH 50000 // Hz 43 | #define FSK_AFC_BANDWIDTH 83333 // Hz 44 | #define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx 45 | #define FSK_FIX_LENGTH_PAYLOAD_ON false 46 | #define FSK_CRC_ENABLED true 47 | 48 | #else 49 | #error "Please define a modem in the compiler options." 50 | #endif 51 | 52 | 53 | #define RX_TIMEOUT_VALUE 3500 // in ms 54 | 55 | //#define BUFFER_SIZE 32 // Define the payload size here 56 | #define BUFFER_SIZE 64 // Define the payload size here 57 | 58 | /* 59 | * Global variables declarations 60 | */ 61 | typedef enum 62 | { 63 | LOWPOWER = 0, 64 | IDLE, 65 | 66 | RX, 67 | RX_TIMEOUT, 68 | RX_ERROR, 69 | 70 | TX, 71 | TX_TIMEOUT, 72 | 73 | CAD, 74 | CAD_DONE 75 | } AppStates_t; 76 | 77 | volatile AppStates_t State = LOWPOWER; 78 | 79 | /*! 80 | * Radio events function pointer 81 | */ 82 | static RadioEvents_t RadioEvents; 83 | 84 | /* 85 | * Global variables declarations 86 | */ 87 | SX1276Generic *Radio; 88 | 89 | 90 | const uint8_t PingMsg[] = { 0xff, 0xff, 0x00, 0x00, 'P', 'I', 'N', 'G'};// "PING"; 91 | const uint8_t PongMsg[] = { 0xff, 0xff, 0x00, 0x00, 'P', 'O', 'N', 'G'};// "PONG"; 92 | 93 | uint16_t BufferSize = BUFFER_SIZE; 94 | uint8_t *Buffer; 95 | 96 | DigitalOut *led3; 97 | 98 | 99 | int SX1276PingPong() 100 | { 101 | #if( defined ( TARGET_KL25Z ) || defined ( TARGET_LPC11U6X ) ) 102 | DigitalOut *led = new DigitalOut(LED2); 103 | #elif defined(TARGET_NUCLEO_L073RZ) || defined (TARGET_DISCO_L072CZ_LRWAN1) 104 | DigitalOut *led = new DigitalOut(LED4); // RX red 105 | led3 = new DigitalOut(LED3); // TX blue 106 | #else 107 | DigitalOut *led = new DigitalOut(LED1); 108 | led3 = led; 109 | #endif 110 | 111 | Buffer = new uint8_t[BUFFER_SIZE]; 112 | *led3 = 1; 113 | 114 | #ifdef TARGET_DISCO_L072CZ_LRWAN1 115 | Radio = new SX1276Generic(NULL, MURATA_SX1276, 116 | LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET, 117 | LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5, 118 | LORA_ANT_RX, LORA_ANT_TX, LORA_ANT_BOOST, LORA_TCXO); 119 | #elif defined(HELTECL432_REV1) 120 | Radio = new SX1276Generic(NULL, HELTEC_L4_1276, 121 | LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET, 122 | LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5, 123 | LORA_ANT_PWR); 124 | #else // RFM95 125 | Radio = new SX1276Generic(NULL, RFM95_SX1276, 126 | LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET, 127 | LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5); 128 | 129 | #endif 130 | 131 | uint8_t i; 132 | bool isMaster = true; 133 | 134 | dprintf("SX1276 Ping Pong Demo Application" ); 135 | dprintf("Freqency: %.1f", (double)RF_FREQUENCY/1000000.0); 136 | dprintf("TXPower: %d dBm", TX_OUTPUT_POWER); 137 | #if USE_MODEM_LORA == 1 138 | dprintf("Bandwidth: %d Hz", LORA_BANDWIDTH); 139 | dprintf("Spreading factor: SF%d", LORA_SPREADING_FACTOR); 140 | #elif USE_MODEM_FSK == 1 141 | dprintf("Bandwidth: %d kHz", FSK_BANDWIDTH); 142 | dprintf("Baudrate: %d", FSK_DATARATE); 143 | #endif 144 | // Initialize Radio driver 145 | RadioEvents.TxDone = OnTxDone; 146 | RadioEvents.RxDone = OnRxDone; 147 | RadioEvents.RxError = OnRxError; 148 | RadioEvents.TxTimeout = OnTxTimeout; 149 | RadioEvents.RxTimeout = OnRxTimeout; 150 | if (Radio->Init( &RadioEvents ) == false) { 151 | while(1) { 152 | dprintf("Radio could not be detected!"); 153 | wait_ms(1000); 154 | } 155 | } 156 | 157 | switch(Radio->DetectBoardType()) { 158 | case SX1276MB1LAS: 159 | if (DEBUG_MESSAGE) 160 | dprintf(" > Board Type: SX1276MB1LAS <"); 161 | break; 162 | case SX1276MB1MAS: 163 | if (DEBUG_MESSAGE) 164 | dprintf(" > Board Type: SX1276MB1LAS <"); 165 | break; 166 | case MURATA_SX1276: 167 | if (DEBUG_MESSAGE) 168 | dprintf(" > Board Type: MURATA_SX1276_STM32L0 <"); 169 | break; 170 | case RFM95_SX1276: 171 | if (DEBUG_MESSAGE) 172 | dprintf(" > HopeRF RFM95xx <"); 173 | break; 174 | default: 175 | dprintf(" > Board Type: unknown <"); 176 | } 177 | 178 | Radio->SetChannel(RF_FREQUENCY ); 179 | 180 | #if USE_MODEM_LORA == 1 181 | 182 | if (LORA_FHSS_ENABLED) 183 | dprintf(" > LORA FHSS Mode <"); 184 | if (!LORA_FHSS_ENABLED) 185 | dprintf(" > LORA Mode <"); 186 | 187 | Radio->SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH, 188 | LORA_SPREADING_FACTOR, LORA_CODINGRATE, 189 | LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON, 190 | LORA_CRC_ENABLED, LORA_FHSS_ENABLED, LORA_NB_SYMB_HOP, 191 | LORA_IQ_INVERSION_ON, 2000 ); 192 | 193 | Radio->SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, 194 | LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, 195 | LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, 0, 196 | LORA_CRC_ENABLED, LORA_FHSS_ENABLED, LORA_NB_SYMB_HOP, 197 | LORA_IQ_INVERSION_ON, true ); 198 | 199 | #elif USE_MODEM_FSK == 1 200 | 201 | dprintf(" > FSK Mode <"); 202 | Radio->SetTxConfig( MODEM_FSK, TX_OUTPUT_POWER, FSK_FDEV, 0, 203 | FSK_DATARATE, 0, 204 | FSK_PREAMBLE_LENGTH, FSK_FIX_LENGTH_PAYLOAD_ON, 205 | FSK_CRC_ENABLED, 0, 0, 0, 2000 ); 206 | 207 | Radio->SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, 208 | 0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, 209 | 0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, FSK_CRC_ENABLED, 210 | 0, 0, false, true ); 211 | 212 | #else 213 | 214 | #error "Please define a modem in the compiler options." 215 | 216 | #endif 217 | 218 | if (DEBUG_MESSAGE) 219 | dprintf("Starting Ping-Pong loop"); 220 | 221 | 222 | Radio->Rx( RX_TIMEOUT_VALUE ); 223 | 224 | while( 1 ) 225 | { 226 | #ifdef TARGET_STM32L4 227 | WatchDogUpdate(); 228 | #endif 229 | 230 | switch( State ) 231 | { 232 | case RX: 233 | *led3 = 0; 234 | if( isMaster == true ) 235 | { 236 | if( BufferSize > 0 ) 237 | { 238 | if( memcmp(Buffer, PongMsg, sizeof(PongMsg)) == 0 ) 239 | { 240 | *led = !*led; 241 | dprintf( "...Pong" ); 242 | // Send the next PING frame 243 | memcpy(Buffer, PingMsg, sizeof(PingMsg)); 244 | // We fill the buffer with numbers for the payload 245 | for( i = sizeof(PingMsg); i < BufferSize; i++ ) 246 | { 247 | Buffer[i] = i - sizeof(PingMsg); 248 | } 249 | wait_ms( 10 ); 250 | Radio->Send( Buffer, BufferSize ); 251 | } 252 | else if( memcmp(Buffer, PingMsg, sizeof(PingMsg)) == 0 ) 253 | { // A master already exists then become a slave 254 | dprintf( "...Ping" ); 255 | *led = !*led; 256 | isMaster = false; 257 | // Send the next PONG frame 258 | memcpy(Buffer, PongMsg, sizeof(PongMsg)); 259 | // We fill the buffer with numbers for the payload 260 | for( i = sizeof(PongMsg); i < BufferSize; i++ ) 261 | { 262 | Buffer[i] = i - sizeof(PongMsg); 263 | } 264 | wait_ms( 10 ); 265 | Radio->Send( Buffer, BufferSize ); 266 | } 267 | else // valid reception but neither a PING or a PONG message 268 | { // Set device as master ans start again 269 | isMaster = true; 270 | Radio->Rx( RX_TIMEOUT_VALUE ); 271 | } 272 | } 273 | } 274 | else 275 | { 276 | if( BufferSize > 0 ) 277 | { 278 | if( memcmp(Buffer, PingMsg, sizeof(PingMsg)) == 0 ) 279 | { 280 | *led = !*led; 281 | dprintf( "...Ping" ); 282 | // Send the reply to the PING string 283 | memcpy(Buffer, PongMsg, sizeof(PongMsg)); 284 | // We fill the buffer with numbers for the payload 285 | for( i = sizeof(PongMsg); i < BufferSize; i++ ) 286 | { 287 | Buffer[i] = i - sizeof(PongMsg); 288 | } 289 | wait_ms( 10 ); 290 | Radio->Send( Buffer, BufferSize ); 291 | } 292 | else // valid reception but not a PING as expected 293 | { // Set device as master and start again 294 | isMaster = true; 295 | Radio->Rx( RX_TIMEOUT_VALUE ); 296 | } 297 | } 298 | } 299 | State = LOWPOWER; 300 | break; 301 | case TX: 302 | *led3 = 1; 303 | if( isMaster == true ) 304 | { 305 | dprintf("Ping..." ); 306 | } 307 | else 308 | { 309 | dprintf("Pong..." ); 310 | } 311 | Radio->Rx( RX_TIMEOUT_VALUE ); 312 | State = LOWPOWER; 313 | break; 314 | case RX_TIMEOUT: 315 | if( isMaster == true ) 316 | { 317 | // Send the next PING frame 318 | memcpy(Buffer, PingMsg, sizeof(PingMsg)); 319 | for( i = sizeof(PingMsg); i < BufferSize; i++ ) 320 | { 321 | Buffer[i] = i - sizeof(PingMsg); 322 | } 323 | wait_ms( 10 ); 324 | Radio->Send( Buffer, BufferSize ); 325 | } 326 | else 327 | { 328 | Radio->Rx( RX_TIMEOUT_VALUE ); 329 | } 330 | State = LOWPOWER; 331 | break; 332 | case RX_ERROR: 333 | // We have received a Packet with a CRC error, send reply as if packet was correct 334 | if( isMaster == true ) 335 | { 336 | // Send the next PING frame 337 | memcpy(Buffer, PingMsg, sizeof(PingMsg)); 338 | for( i = 4; i < BufferSize; i++ ) 339 | { 340 | Buffer[i] = i - 4; 341 | } 342 | wait_ms( 10 ); 343 | Radio->Send( Buffer, BufferSize ); 344 | } 345 | else 346 | { 347 | // Send the next PONG frame 348 | memcpy(Buffer, PongMsg, sizeof(PongMsg)); 349 | for( i = sizeof(PongMsg); i < BufferSize; i++ ) 350 | { 351 | Buffer[i] = i - sizeof(PongMsg); 352 | } 353 | wait_ms( 10 ); 354 | Radio->Send( Buffer, BufferSize ); 355 | } 356 | State = LOWPOWER; 357 | break; 358 | case TX_TIMEOUT: 359 | Radio->Rx( RX_TIMEOUT_VALUE ); 360 | State = LOWPOWER; 361 | break; 362 | case LOWPOWER: 363 | break; 364 | default: 365 | State = LOWPOWER; 366 | break; 367 | } 368 | } 369 | } 370 | 371 | void OnTxDone(void *radio, void *userThisPtr, void *userData) 372 | { 373 | SX1276Generic *r = (SX1276Generic *)radio; 374 | r->Sleep( ); 375 | State = TX; 376 | if (DEBUG_MESSAGE) 377 | dprintf("> OnTxDone"); 378 | } 379 | 380 | void OnRxDone(void *radio, void *userThisPtr, void *userData, uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr) 381 | { 382 | SX1276Generic *r = (SX1276Generic *)radio; 383 | r->Sleep( ); 384 | BufferSize = size; 385 | memcpy( Buffer, payload, BufferSize ); 386 | State = RX; 387 | if (DEBUG_MESSAGE) 388 | dprintf("> OnRxDone: RssiValue=%d dBm, SnrValue=%d", rssi, snr); 389 | dump("Data:", payload, size); 390 | } 391 | 392 | void OnTxTimeout(void *radio, void *userThisPtr, void *userData) 393 | { 394 | SX1276Generic *r = (SX1276Generic *)radio; 395 | *led3 = 0; 396 | r->Sleep( ); 397 | State = TX_TIMEOUT; 398 | if(DEBUG_MESSAGE) 399 | dprintf("> OnTxTimeout"); 400 | } 401 | 402 | void OnRxTimeout(void *radio, void *userThisPtr, void *userData) 403 | { 404 | SX1276Generic *r = (SX1276Generic *)radio; 405 | *led3 = 0; 406 | r->Sleep( ); 407 | Buffer[BufferSize-1] = 0; 408 | State = RX_TIMEOUT; 409 | if (DEBUG_MESSAGE) 410 | dprintf("> OnRxTimeout"); 411 | } 412 | 413 | void OnRxError(void *radio, void *userThisPtr, void *userData) 414 | { 415 | SX1276Generic *r = (SX1276Generic *)radio; 416 | r->Sleep( ); 417 | State = RX_ERROR; 418 | if (DEBUG_MESSAGE) 419 | dprintf("> OnRxError"); 420 | } 421 | #endif 422 | -------------------------------------------------------------------------------- /examples/GenericPingPong.h: -------------------------------------------------------------------------------- 1 | /* 2 | / _____) _ | | 3 | ( (____ _____ ____ _| |_ _____ ____| |__ 4 | \____ \| ___ | (_ _) ___ |/ ___) _ \ 5 | _____) ) ____| | | || |_| ____( (___| | | | 6 | (______/|_____)_|_|_| \__)_____)\____)_| |_| 7 | ( C )2014 Semtech 8 | 9 | Description: Contains the callbacks for the IRQs and any application related details 10 | 11 | License: Revised BSD License, see LICENSE.TXT file include in the project 12 | 13 | Maintainer: Miguel Luis and Gregory Cristian 14 | */ 15 | #ifndef __GENERICPINGPONG_H__ 16 | #define __GENERICPINGPONG_H__ 17 | 18 | #ifdef FEATURE_LORA_PING_PONG 19 | int SX1276PingPong(void); 20 | #else 21 | #define SX1276PingPong(x) void() 22 | #endif 23 | /* 24 | * Callback functions prototypes 25 | */ 26 | /*! 27 | * @brief Function to be executed on Radio Tx Done event 28 | */ 29 | void OnTxDone(void *radio, void *userThisPtr, void *userData); 30 | 31 | /*! 32 | * @brief Function to be executed on Radio Rx Done event 33 | */ 34 | void OnRxDone(void *radio, void *userThisPtr, void *userData, uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); 35 | 36 | /*! 37 | * @brief Function executed on Radio Tx Timeout event 38 | */ 39 | void OnTxTimeout(void *radio, void *userThisPtr, void *userData); 40 | 41 | /*! 42 | * @brief Function executed on Radio Rx Timeout event 43 | */ 44 | void OnRxTimeout(void *radio, void *userThisPtr, void *userData); 45 | 46 | /*! 47 | * @brief Function executed on Radio Rx Error event 48 | */ 49 | void OnRxError(void *radio, void *userThisPtr, void *userData); 50 | 51 | /*! 52 | * @brief Function executed on Radio Fhss Change Channel event 53 | */ 54 | void OnFhssChangeChannel(void *radio, void *userThisPtr, void *userData, uint8_t channelIndex ); 55 | 56 | /*! 57 | * @brief Function executed on CAD Done event 58 | */ 59 | void OnCadDone(void *radio, void *userThisPtr, void *userDataco); 60 | 61 | #endif // GenericPingPong.h 62 | -------------------------------------------------------------------------------- /examples/RadioTest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The file is Licensed under the Apache License, Version 2.0 3 | * (c) 2017 Helmut Tschemernjak 4 | * 30826 Garbsen (Hannover) Germany 5 | */ 6 | 7 | #include "mbed.h" 8 | #include "xPinMap.h" 9 | #include "sx1276-mbed-hal.h" 10 | #include "RadioShuttle.h" 11 | #include "RadioStatus.h" 12 | #include "RadioSecurity.h" 13 | #include "main.h" 14 | #ifdef FEATURE_NVPROPERTY 15 | #include 16 | #include "NVProperty.h" 17 | #endif 18 | 19 | 20 | #if defined(FEATURE_LORA) && defined(FEATURE_RADIOTEST) 21 | 22 | #define CHECK_ERROR_RET(func, err) { \ 23 | if (err) { \ 24 | dprintf("Error in %s: %s", func, rs->StrError(err)); \ 25 | return err; \ 26 | } \ 27 | } 28 | 29 | #ifndef TARGET_STM32L4 30 | #define RADIO_SERVER 1 31 | #endif 32 | 33 | 34 | bool usePassword = false; // password the can used indepenend of AES 35 | bool server; // automatically being set if radioTypeMode RadioShuttle::RS_Station_Basic 36 | bool useAES = false; // AES needs the usePassword option on 37 | const char *appPassword; 38 | 39 | static const int myTempSensorApp = 0x0001; // Must be unique world wide. 40 | #ifdef RADIO_SERVER 41 | int myDeviceID = 1; 42 | int remoteDeviceID = 14; 43 | uint32_t myCode = 0; 44 | RadioShuttle::RadioType radioTypeMode = RadioShuttle::RS_Station_Basic; // 1 = RS_Node_Offline, 3 = RS_Node_Online, 4 = RS_Station_Basic 45 | #else 46 | int myDeviceID = 14; 47 | int remoteDeviceID = 1; 48 | uint32_t myCode = 0; 49 | RadioShuttle::RadioType radioTypeMode = RadioShuttle::RS_Node_Offline; // 1 = RS_Node_Offline, 3 = RS_Node_Online, 4 = RS_Station_Basic 50 | #endif 51 | 52 | 53 | /* 54 | * For details review: SX1276GenericLib/sx1276/sx1276.h 55 | * Supported spreading factors SF 7,8, 9, 10, 11, (12 does not work well) 56 | * Working frequencies using the 125000 bandwidth which leaves 57 | * sufficient distance to the neighbour channel 58 | * EU: 868.1, 868.3, 868.5 (Default LoRaWAN EU channels) 59 | * EU: 865.1, 865.3, 865.5, 865.7, 865.9 (additional channels) 60 | * EU: 866.1, 866.3, 866.5, 866.7, 866.9 (additional channels) 61 | * EU: 867.1, 867.3, 867.5, 867.7, 867.9 (additional channels) 62 | * Utilisation of these channels should not exceed 1% per hour per node 63 | * Bandwidth changes other than 125k requires different channels distances 64 | */ 65 | RadioShuttle::RadioProfile myProfile[] = { 66 | /* 67 | * Our default profile 68 | * frequency, bandwidth, TX power, spreading factor, frequency-offset 69 | */ 70 | { 868100000, 125000, 14, 7, 0 }, 71 | { 0, 0, 0, 0, 0 }, 72 | }; 73 | 74 | 75 | struct sensor { 76 | uint8_t version; 77 | uint8_t padding; 78 | uint16_t pm25; 79 | uint16_t pm10; 80 | uint16_t id; 81 | } PMAppData; 82 | 83 | 84 | void TempSensorRecvHandler(int AppID, RadioShuttle::devid_t stationID, int msgID, int status, void *buffer, int length) 85 | { 86 | switch(status) { 87 | case RadioShuttle::MS_SentCompleted: // A SendMsg has been sent. 88 | dprintf("MSG_SentCompleted: id=%d %d bytes", msgID, length); 89 | break; 90 | case RadioShuttle::MS_SentCompletedConfirmed:// A SendMsg has been sent and confirmed 91 | dprintf("MSG_SentCompletedConfirmed: id=%d %d bytes", msgID, length); 92 | break; 93 | case RadioShuttle::MS_SentTimeout: // A timeout occurred, number of retries exceeded 94 | dprintf("MSG_SentTimeout ID: %d", msgID); 95 | break; 96 | 97 | case RadioShuttle::MS_RecvData: // a simple input message 98 | dprintf("MSG_RecvData ID: %d, len=%d", msgID, length); 99 | // dump("MSG_RecvData", buffer, length); 100 | #ifdef PM_SENSOR 101 | { 102 | struct sensor *p = (sensor *)buffer; 103 | if (length == sizeof(struct sensor) && p->version == 1) { 104 | dprintf("ParticalData: PM10: %.1f (μg/m3) PM2.5: %.1f (μg/m3) ID: %d", (float)p->pm10 / 10.0, (float)p->pm25 / 10.0, p->id); 105 | } 106 | } 107 | #endif 108 | break; 109 | case RadioShuttle::MS_RecvDataConfirmed: // received a confirmed message 110 | dprintf("MSG_RecvDataConfirmed ID: %d, len=%d", msgID, length); 111 | // dump("MSG_RecvDataConfirmed", buffer, length); 112 | break; 113 | case RadioShuttle::MS_NoStationFound: 114 | dprintf("MSG_NoStationFound"); 115 | break; 116 | case RadioShuttle::MS_NoStationSupportsApp: 117 | dprintf("MSG_NoStationSupportsApp"); 118 | break; 119 | case RadioShuttle::MS_AuthenicationRequired: // the password does not match. 120 | dprintf("MSG_AuthenicationRequired"); 121 | break; 122 | 123 | case RadioShuttle::MS_StationConnected: // a confirmation that the connection was accepted 124 | dprintf("MSG_StationConnected"); 125 | break; 126 | case RadioShuttle::MS_StationDisconnected: // a confirmation that the disconnect was accepted 127 | dprintf("MSG_StationDisconnected"); 128 | break; 129 | default: 130 | break; 131 | } 132 | } 133 | 134 | 135 | Radio *radio; 136 | RadioShuttle *rs; 137 | RadioStatusInterface *statusIntf; 138 | RadioSecurityInterface *securityIntf; 139 | 140 | int InitRadio() 141 | { 142 | Radio *radio; 143 | RSCode err; 144 | 145 | #ifdef FEATURE_NVPROPERTY 146 | NVProperty prop; 147 | int value; 148 | 149 | myDeviceID = prop.GetProperty(prop.LORA_DEVICE_ID, 0); 150 | myCode = prop.GetProperty(prop.LORA_CODE_ID, 0); 151 | if ((value = prop.GetProperty(prop.LORA_RADIO_TYPE, 0)) != 0) 152 | radioTypeMode = (RadioShuttle::RadioType)value; 153 | remoteDeviceID = 1; 154 | 155 | if (myDeviceID == 0 || myCode == 0 || radioTypeMode == 0) { 156 | dprintf("LORA_DEVICE_ID or LORA_CODE_ID or LORA_RADIO_TYPE not set, use PropertyEditor to set this!"); 157 | return -1; 158 | } 159 | /* 160 | * Here are optional properties for custom settings 161 | */ 162 | if ((value = prop.GetProperty(prop.LORA_REMOTE_ID, 0)) != 0) 163 | remoteDeviceID = value; 164 | if ((value = prop.GetProperty(prop.LORA_FREQUENCY, 0)) != 0) 165 | myProfile[0].Frequency = value; 166 | if ((value = prop.GetProperty(prop.LORA_BANDWIDTH, 0)) != 0) 167 | myProfile[0].Bandwidth = value; 168 | if ((value = prop.GetProperty(prop.LORA_SPREADING_FACTOR, 0)) != 0) 169 | myProfile[0].SpreadingFaktor = value; 170 | if ((value = prop.GetProperty(prop.LORA_TXPOWER, 0)) != 0) 171 | myProfile[0].TXPower = value; 172 | if ((value = prop.GetProperty(prop.LORA_FREQUENCY_OFFSET, 0)) != 0) 173 | myProfile[0].FrequencyOffset = value; 174 | appPassword = prop.GetProperty(prop.LORA_APP_PWD, (const char *)NULL); 175 | #endif 176 | 177 | if (radioTypeMode >= RadioShuttle::RS_Station_Basic) 178 | server = true; 179 | 180 | #ifdef TARGET_DISCO_L072CZ_LRWAN1 181 | radio = new SX1276Generic(NULL, MURATA_SX1276, 182 | LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET, 183 | LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5, 184 | LORA_ANT_RX, LORA_ANT_TX, LORA_ANT_BOOST, LORA_TCXO); 185 | #elif defined(HELTECL432_REV1) 186 | radio = new SX1276Generic(NULL, HELTEC_L4_1276, 187 | LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET, 188 | LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5, 189 | LORA_ANT_PWR); 190 | #else // RFM95 191 | radio = new SX1276Generic(NULL, RFM95_SX1276, 192 | LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET, 193 | LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5); 194 | #endif 195 | 196 | 197 | statusIntf = new MyRadioStatus(); 198 | securityIntf = new RadioSecurity(); 199 | 200 | rs = new RadioShuttle("MyRadioShuttle"); 201 | 202 | rs->EnablePacketTrace(RadioShuttle::DEV_ID_ANY, true, true); 203 | 204 | err = rs->AddLicense(myDeviceID, myCode); 205 | CHECK_ERROR_RET("AddLicense", err); 206 | 207 | err = rs->AddRadio(radio, MODEM_LORA, myProfile); 208 | CHECK_ERROR_RET("AddRadio", err); 209 | dprintf("Radio: %.1f MHz, SF%d, %.f kHz", (float)myProfile[0].Frequency/1000000.0, myProfile[0].SpreadingFaktor, (float)myProfile[0].Bandwidth/1000.0); 210 | 211 | rs->AddRadioStatus(statusIntf); 212 | CHECK_ERROR_RET("AddRadioStatus", err); 213 | 214 | rs->AddRadioSecurity(securityIntf); 215 | CHECK_ERROR_RET("AddRadioSecurity", err); 216 | 217 | /* 218 | * The password parameter can be NULL if no password is required 219 | */ 220 | err = rs->RegisterApplication(myTempSensorApp, &TempSensorRecvHandler, (void *)appPassword); 221 | CHECK_ERROR_RET("RegisterApplication", err); 222 | 223 | if (server) { 224 | // usually RadioShuttle::RS_Station_Basic, set via properties 225 | err = rs->Startup(radioTypeMode); 226 | dprintf("Startup as a Server: %s ID=%d", rs->GetRadioName(rs->GetRadioType()), myDeviceID); 227 | } else { 228 | // usually RadioShuttle::RS_Node_Online or RadioShuttle, set via properties 229 | err = rs->Startup(radioTypeMode); 230 | dprintf("Startup as a Node: %s ID=%d", rs->GetRadioName(rs->GetRadioType()), myDeviceID); 231 | if (!err && rs->AppRequiresAuthentication(myTempSensorApp) == RS_PasswordSet) { 232 | err = rs->Connect(myTempSensorApp, remoteDeviceID); 233 | } 234 | } 235 | CHECK_ERROR_RET("Startup", err); 236 | return 0; 237 | } 238 | 239 | void DeInitRadio() 240 | { 241 | if (securityIntf) { 242 | delete securityIntf; 243 | securityIntf = NULL; 244 | } 245 | if (statusIntf) { 246 | delete statusIntf; 247 | statusIntf = NULL; 248 | } 249 | if (rs) { 250 | delete rs; 251 | rs = NULL; 252 | } 253 | if (radio) { 254 | delete radio; 255 | radio = NULL; 256 | } 257 | } 258 | 259 | /* 260 | * this is a example basic loop for RadioShuttle 261 | */ 262 | int RadioTest() 263 | { 264 | extern volatile int pressedCount; 265 | 266 | if (InitRadio() != 0) 267 | return -1; 268 | 269 | 270 | for(;;) { 271 | static int cnt = 0; 272 | if (cnt != pressedCount) { 273 | if (cnt > 0) { 274 | int flags = 0; 275 | flags |= RadioShuttle::MF_NeedsConfirm; // optional 276 | if (useAES && appPassword) 277 | flags |= RadioShuttle::MF_Encrypted; 278 | if (server) { 279 | static char msg[] = "The server feels very good today"; 280 | rs->SendMsg(myTempSensorApp, msg, sizeof(msg), flags, remoteDeviceID); 281 | } else { 282 | static char msg[] = "Hello, the temperature is 26 celsius"; 283 | rs->SendMsg(myTempSensorApp, msg, sizeof(msg), flags, remoteDeviceID); 284 | } 285 | } 286 | cnt = pressedCount; 287 | } 288 | 289 | if (rs->Idle() && rs->GetRadioType() == RadioShuttle::RS_Node_Offline) { 290 | sleep(); // uses deepsleep() when idle lowest power mode; 291 | } else { 292 | sleep(); // timer and radio interrupts will wakeup us 293 | } 294 | rs->RunShuttle(); // process all pending events 295 | } 296 | return 0; 297 | } 298 | 299 | 300 | int RadioUpdate(bool keyPressed) 301 | { 302 | if (!rs) 303 | return 0; 304 | 305 | if (keyPressed) { 306 | int flags = 0; 307 | flags |= RadioShuttle::MF_NeedsConfirm; // optional 308 | if (usePassword && useAES) 309 | flags |= RadioShuttle::MF_Encrypted; 310 | if (server) { 311 | static char msg[] = "The server feels very good today"; 312 | rs->SendMsg(myTempSensorApp, msg, sizeof(msg), flags, remoteDeviceID); 313 | } else { 314 | static char msg[] = "Hello, the temperature is 26 celsius"; 315 | rs->SendMsg(myTempSensorApp, msg, sizeof(msg), flags, remoteDeviceID); 316 | } 317 | } 318 | rs->RunShuttle(); 319 | return 0; 320 | } 321 | 322 | bool RadioISIdle() 323 | { 324 | if (!rs) 325 | return true; 326 | return rs->Idle(); 327 | } 328 | 329 | void InitLoRaChipWithShutdown() 330 | { 331 | #ifdef LORA_CS 332 | if (LORA_CS == NC) 333 | return; 334 | #ifdef HELTECL432_REV1 335 | Radio *radio = new SX1276Generic(NULL, HELTEC_L4_1276, 336 | LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET, 337 | LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5, LORA_ANT_PWR); 338 | #else 339 | Radio *radio = new SX1276Generic(NULL, RFM95_SX1276, 340 | LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET, 341 | LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5); 342 | #endif 343 | 344 | RadioEvents_t radioEvents; 345 | memset(&radioEvents, 0, sizeof(radioEvents)); 346 | if (radio->Init(&radioEvents)) { 347 | radio->Sleep(); 348 | delete radio; 349 | } 350 | #endif 351 | } 352 | 353 | void RadioContinuesTX(void) 354 | { 355 | Radio *radio; 356 | 357 | #ifdef FEATURE_NVPROPERTY 358 | NVProperty prop; 359 | int value; 360 | 361 | /* 362 | * Here are optional properties for custom settings 363 | */ 364 | if ((value = prop.GetProperty(prop.LORA_FREQUENCY, 0)) != 0) 365 | myProfile[0].Frequency = value; 366 | if ((value = prop.GetProperty(prop.LORA_TXPOWER, 0)) != 0) 367 | myProfile[0].TXPower = value; 368 | if ((value = prop.GetProperty(prop.LORA_SPREADING_FACTOR, 0)) != 0) 369 | myProfile[0].SpreadingFaktor = value; 370 | #endif 371 | 372 | 373 | #ifdef TARGET_DISCO_L072CZ_LRWAN1 374 | radio = new SX1276Generic(NULL, MURATA_SX1276, 375 | LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET, 376 | LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5, 377 | LORA_ANT_RX, LORA_ANT_TX, LORA_ANT_BOOST, LORA_TCXO); 378 | #elif defined(HELTECL432_REV1) 379 | radio = new SX1276Generic(NULL, HELTEC_L4_1276, 380 | LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET, 381 | LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5, 382 | LORA_ANT_PWR); 383 | #else // RFM95 384 | radio = new SX1276Generic(NULL, RFM95_SX1276, 385 | LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET, 386 | LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5); 387 | #endif 388 | 389 | dprintf("RadioContinuesTX test, press reset to abort"); 390 | while(true) { 391 | int secs = 10; 392 | radio->SetTxContinuousWave(myProfile[0].Frequency, myProfile[0].TXPower, secs); 393 | wait_ms(secs * 1000); 394 | rprintf("."); 395 | } 396 | } 397 | 398 | #endif // FEATURE_LORA 399 | -------------------------------------------------------------------------------- /examples/RadioTest.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The file is Licensed under the Apache License, Version 2.0 3 | * (c) 2017 Helmut Tschemernjak 4 | * 30826 Garbsen (Hannover) Germany 5 | */ 6 | 7 | #ifndef __RADIOTEST_H__ 8 | #define __RADIOTEST_H__ 9 | 10 | extern int InitRadio(void); 11 | extern void DeInitRadio(void); 12 | extern void RadioTest(); 13 | extern int RadioUpdate(bool keyPressed); 14 | extern bool RadioISIdle(); 15 | extern void InitLoRaChipWithShutdown(); 16 | extern void RadioContinuesTX(void); 17 | 18 | #endif // RadioTest.h 19 | -------------------------------------------------------------------------------- /examples/RadioTest/RTCUtil.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The file is Licensed under the Apache License, Version 2.0 3 | * (c) 2018 Helmut Tschemernjak 4 | * 30826 Garbsen (Hannover) Germany 5 | */ 6 | 7 | #ifdef ARDUINO 8 | 9 | #include "xPinMap.h" 10 | #include 11 | #include 12 | #include 13 | #if defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_ARCH_SAMD) 14 | #include 15 | #elif ARDUINO_ARCH_ESP32 16 | #include 17 | #include 18 | #include 19 | #if defined (FEATURE_SI7021) || defined (FEATURE_RTC_DS3231) 20 | #include 21 | #endif 22 | #ifdef FEATURE_RTC_DS3231 23 | #include "ds3231.h" 24 | #endif 25 | #ifdef FEATURE_SI7021 26 | #include "HELIOS_Si7021.h" 27 | #endif 28 | #endif 29 | #if LORA_CS != NC 30 | #include 31 | #endif 32 | 33 | #ifdef FEATURE_LORA 34 | 35 | NVProperty prop; // global property store supports OTP, Flash and SRAM 36 | static void InitPropertyEditor(InterruptIn *intrButton); 37 | 38 | 39 | void InitLoRaChipWithShutdown() 40 | { 41 | #ifdef LORA_CS 42 | if (LORA_CS == NC) 43 | return; 44 | Radio *radio = new SX1276Generic(NULL, RFM95_SX1276, 45 | LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET, 46 | LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5); 47 | RadioEvents_t radioEvents; 48 | memset(&radioEvents, 0, sizeof(radioEvents)); 49 | if (radio->Init(&radioEvents)) { 50 | radio->Sleep(); 51 | delete radio; 52 | } 53 | #endif 54 | } 55 | 56 | bool ESP32DeepsleepWakeup; 57 | 58 | #if defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_ARCH_SAMD) 59 | 60 | #ifdef BAT_MESURE_ADC 61 | float GetBatteryVoltage(bool print) 62 | { 63 | #ifdef D21_LONGRA_REV_750 64 | if (DSU->STATUSB.bit.DBGPRES) // skip this when the debugger uses the SWD pin 65 | return 0; 66 | #endif 67 | int adcBits = 12; 68 | int adcSampleCount = 12; 69 | float volt = 0; 70 | float vref = 1.0; 71 | 72 | #ifdef BAT_MESURE_EN 73 | DigitalOut swd(BAT_MESURE_EN); 74 | swd = 0; 75 | #endif 76 | 77 | analogReadResolution(adcBits); 78 | analogReference(AR_INTERNAL1V0); 79 | 80 | float adcValue = 0; 81 | for (int i = 0; i < adcSampleCount; i++) { 82 | adcValue += analogRead(BAT_MESURE_ADC); 83 | } 84 | adcValue /= (float)adcSampleCount; 85 | adcValue += 0.5; // for proper rounding 86 | 87 | float voltstep = vref/(float)(1<= 58) 118 | secs = interval; 119 | rtc.setAlarmSeconds(secs); 120 | rtc.enableAlarm(rtc.MATCH_SS); 121 | } 122 | 123 | void RTCInit(const char *date, const char *timestr, InterruptIn *intrButton) 124 | { 125 | rtc.begin(); 126 | rtc.attachInterrupt(alarmMatch); 127 | alarmMatch(); 128 | time_t t = cvt_date(date, timestr); 129 | if (rtc.getYear() == 0 || rtc.getEpoch() < (uint32_t)t) { 130 | rtc.setEpoch(t); 131 | } 132 | setTickerStartSecs(rtc.getSeconds() + (rtc.getMinutes()*60) + rtc.getHours() * 3600); 133 | dprintf("RTC Clock: %d/%d/%d %02d:%02d:%02d", rtc.getDay(), rtc.getMonth(), rtc.getYear() + 2000, rtc.getHours(), rtc.getMinutes(), rtc.getSeconds()); 134 | 135 | uint8_t reason = PM->RCAUSE.reg; 136 | if (reason & (PM_RCAUSE_WDT | PM_RCAUSE_BOD33)) { 137 | dprintf("Boot: CPU(D21) reset by: %s %s", 138 | PM->RCAUSE.reg & PM_RCAUSE_WDT ? "WatchDog" : "", 139 | PM->RCAUSE.reg & PM_RCAUSE_BOD33 ? "Brown-Out" : ""); 140 | } 141 | #ifdef BOOSTER_EN50 142 | boost50 = 0; 143 | boost33 = 0; 144 | #endif 145 | #ifdef DISPLAY_EN 146 | displayEnable = 1; // disconnects the display from the 3.3 power 147 | #endif 148 | #ifdef BAT_MESURE_ADC 149 | GetBatteryVoltage(); 150 | #endif 151 | /* 152 | * the default is 32 secs, at present it cannot be changed. 153 | */ 154 | InitWatchDog(); 155 | InitPropertyEditor(intrButton); 156 | } 157 | 158 | bool runPropertyEdtior() 159 | { 160 | NVPropertyEditorInit(&MYSERIAL); 161 | NVPropertyEditor(); 162 | NVIC_SystemReset(); 163 | return false; 164 | } 165 | 166 | #elif ARDUINO_ARCH_ESP32 167 | 168 | uint64_t ESP32WakeupGPIOStatus; 169 | 170 | #ifdef FEATURE_SI7021 171 | HELIOS_Si7021 *sensorSI7021; 172 | #endif 173 | 174 | #if defined(ESP32_ECO_POWER_REV_1) || defined(ARDUINO_HELTEC_WIFI_LORA_32_V2) || defined(ARDUINO_heltec_wifi_lora_32_V2) || defined(ARDUINO_HELTEC_WIRELESS_STICK) 175 | float GetBatteryVoltage(bool print) 176 | { 177 | int adcBits = 12; 178 | int adcSampleCount = 12; 179 | float volt = 0; 180 | float correct = 0; 181 | float vref; 182 | #if defined(ARDUINO_HELTEC_WIFI_LORA_32_V2) || defined(ARDUINO_heltec_wifi_lora_32_V2) 183 | vref = (float)prop.GetProperty(prop.ADC_VREF, 1100)/1000.0; 184 | vref = (vref / 1.100) * 1.995; 185 | adc_attenuation_t adc_attn = ADC_6db; 186 | correct = -0.18; 187 | #else 188 | vref = (float)prop.GetProperty(prop.ADC_VREF, 1100)/1000.0; 189 | adc_attenuation_t adc_attn = ADC_0db; 190 | correct = -0.22; 191 | #endif 192 | 193 | 194 | #ifdef BAT_MESURE_EN 195 | DigitalOut extPWR(BAT_MESURE_EN); 196 | extPWR = EXT_POWER_ON; 197 | #endif 198 | 199 | analogReadResolution(adcBits); 200 | analogSetAttenuation(adc_attn); 201 | analogSetPinAttenuation(BAT_MESURE_ADC, adc_attn); 202 | 203 | float adcValue = 0; 204 | for (int i = 0; i < adcSampleCount; i++) { 205 | adcValue += analogRead(BAT_MESURE_ADC); // BAT_MESURE_ADC is ADC1 206 | } 207 | 208 | #ifdef BAT_MESURE_EN 209 | extPWR = EXT_POWER_OFF; 210 | DigitalIn tmpPWR(BAT_MESURE_EN); 211 | tmpPWR.mode(PullUp); // turn off the power, PullUp will keep it off in deepsleep 212 | #endif 213 | 214 | adcValue /= (float)adcSampleCount; 215 | adcValue += 0.5; // for proper rounding. 216 | 217 | float voltstep = vref/(float)(1< ds.unixtime) { 250 | time_t t = cvt_date(date, timestr); 251 | t -= (ntp.GetGMTOffset() + ntp.GetDayLightOffset(t)); // convert to UTC 252 | 253 | if (!ds.unixtime || t > ds.unixtime ) { 254 | struct tm *tmp = gmtime(&t); 255 | ds.sec = tmp->tm_sec; 256 | ds.min = tmp->tm_min; 257 | ds.hour = tmp->tm_hour; 258 | ds.wday = tmp->tm_wday; 259 | ds.mday = tmp->tm_mday; 260 | ds.mon = tmp->tm_mon + 1; 261 | ds.year = tmp->tm_year + 1900; 262 | DS3231_set(ds); 263 | DS3231_get(&ds); 264 | #if 0 265 | dprintf("%02d:%02d:%02d wday(%d) mday(%d) mon(%d) year(%d), ", ds.hour, ds.min, ds.sec, ds.wday, ds.mday, ds.mon, ds.year); 266 | dprintf("RTC: %.2f°C", DS3231_get_treg()); 267 | time_t now = ds.unixtime; 268 | dprintf("ctime: %s", ctime(&now)); 269 | #endif 270 | } 271 | struct timeval tv; 272 | memset(&tv, 0, sizeof(tv)); 273 | tv.tv_sec = ds.unixtime; 274 | struct timezone tz; 275 | memset(&tz, 0, sizeof(tz)); 276 | tz.tz_minuteswest = (ntp.GetGMTOffset() + ntp.GetDayLightOffset(t)/60); 277 | tz.tz_dsttime = 1; 278 | ntp.SetTimeZone(tv.tv_sec); 279 | settimeofday(&tv, &tz); 280 | dprintf("RTC Clock: %d/%d/%d %02d:%02d:%02d UTC", ds.mday, ds.mon, ds.year, ds.hour, ds.min, ds.sec); 281 | } 282 | int i = prop.GetProperty(prop.RTC_AGING_CAL, 0); 283 | if (i && i != DS3231_get_aging()) 284 | DS3231_set_aging(i); 285 | ntp.SetRTCUpdateProc(&RTCUpdateHandler); 286 | #else // without ds3231 287 | if (!now) { 288 | time_t t = cvt_date(date, timestr); 289 | t -= (ntp.GetGMTOffset() + ntp.GetDayLightOffset(t)); // convert to UTC 290 | struct timeval tv; 291 | memset(&tv, 0, sizeof(tv)); 292 | tv.tv_sec = t; 293 | struct timezone tz; 294 | memset(&tz, 0, sizeof(tz)); 295 | tz.tz_minuteswest = (ntp.GetGMTOffset() + ntp.GetDayLightOffset(t))/60; 296 | tz.tz_dsttime = 1; 297 | ntp.SetTimeZone(tv.tv_sec); 298 | settimeofday(&tv, &tz); 299 | } 300 | #endif 301 | 302 | ESP32WakeupGPIOStatus = ESP32WakeupGPIOStatusLow | (uint64_t) ESP32WakeupGPIOStatusHigh << 32; 303 | esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause(); 304 | if (wakeup_reason) 305 | dprintf("Boot: %s (bootCount: %d)", ESP32WakeUpReason(wakeup_reason), ++bootCount); 306 | dprintf("Boot: CPU(Pro): %s", ESP32ResetReason(rtc_get_reset_reason(0))); 307 | if (ESP.getChipCores() == 2) 308 | dprintf("Boot: CPU(App): %s", ESP32ResetReason(rtc_get_reset_reason(1))); 309 | dprintf("%s: Rev: %d, %d MHz, IDF(%s)", ESP.getChipModel(), ESP.getChipRevision(), ESP.getCpuFreqMHz(), ESP.getSdkVersion()); 310 | 311 | 312 | if (wakeup_reason == ESP_SLEEP_WAKEUP_UNDEFINED) { 313 | /* 314 | * Run this only on reset boot, not from deepsleep. 315 | */ 316 | #ifdef EXT_POWER_SW 317 | DigitalIn extPWR(EXT_POWER_SW); 318 | extPWR.mode(PullUp); // turn off the power, PullUp will keep it off in deepsleep 319 | #endif 320 | #ifdef BAT_MESURE_ADC 321 | GetBatteryVoltage(); 322 | #endif 323 | 324 | #ifdef FEATURE_SI7021 325 | sensorSI7021 = new HELIOS_Si7021(); 326 | if (sensorSI7021->hasSensor()) { 327 | hasSensor = true; 328 | dprintf("%s: Rev(%d) %.2f°C Humidity: %.2f%%", sensorSI7021->getModelName(), sensorSI7021->getRevision(), sensorSI7021->readTemperature(), sensorSI7021->readHumidity()); 329 | } else { 330 | delete sensorSI7021; 331 | sensorSI7021 = NULL; 332 | } 333 | #endif 334 | } else { 335 | /* 336 | * runs on deepsleep wakeup 337 | */ 338 | ESP32DeepsleepWakeup = true; 339 | #ifdef FEATURE_SI7021 340 | if (hasSensor) { 341 | sensorSI7021 = new HELIOS_Si7021(); 342 | dprintf("%s: %.2f°C Humidity: %.2f%%", sensorSI7021->getModelName(), sensorSI7021->readTemperature(), sensorSI7021->readHumidity()); 343 | } 344 | #endif 345 | } 346 | /* 347 | * the default of two minutes should be fine for networking hangs, etc. 348 | * specify optional parameter in ms e.g.:(120 * 1000) 349 | */ 350 | InitWatchDog(); 351 | InitPropertyEditor(intrButton); 352 | } 353 | 354 | void 355 | RTCUpdateHandler(void) 356 | { 357 | /* 358 | * will be called when the RTCUpdate issues an update. 359 | */ 360 | #ifdef FEATURE_RTC_DS3231 361 | time_t t = time(NULL); 362 | struct ts ds; 363 | struct tm *tmp = gmtime(&t); 364 | ds.sec = tmp->tm_sec; 365 | ds.min = tmp->tm_min; 366 | ds.hour = tmp->tm_hour; 367 | ds.wday = tmp->tm_wday; 368 | ds.mday = tmp->tm_mday; 369 | ds.mon = tmp->tm_mon + 1; 370 | ds.year = tmp->tm_year + 1900; 371 | DS3231_set(ds); 372 | dprintf("RTC update issued"); 373 | #endif 374 | } 375 | 376 | bool runPropertyEdtior() 377 | { 378 | NVPropertyEditorInit(&MYSERIAL); 379 | NVPropertyEditor(); 380 | esp_restart(); 381 | return false; 382 | } 383 | 384 | #else 385 | #error "Unkown platform" 386 | #endif 387 | 388 | 389 | /* 390 | * The automatic property editor startup: 391 | * When the RTCInit is being called with a button parameter 392 | * the InitPropertyEditor installs a Timer called every second. 393 | * The userButtonTimerFunc will check if the button is hold down 394 | * for 5 continues seconds and installs a callback to start 395 | * the editor later via sleep/deepsleep code within the loop. 396 | * We cannot call the Editor within the Interrupt function, 397 | * therefore the sleep/deepsleep processing calls the 398 | * runPropertyEdtior function via the registered callback. 399 | */ 400 | RTC_DATA_ATTR int buttonCount = 0; 401 | RTC_DATA_ATTR int timerCount = 0; 402 | 403 | Timeout *userButtonTimer; 404 | InterruptIn *userButtonIntr; 405 | 406 | static void userButtonTimerFunc(void) 407 | { 408 | if (timerCount++ >= 10) { 409 | userButtonTimer->detach(); 410 | return; 411 | } 412 | userButtonTimer->attach(callback(&userButtonTimerFunc), 1000); 413 | if (userButtonIntr->read() == 0) 414 | buttonCount++; 415 | else 416 | buttonCount = 0; 417 | 418 | if (buttonCount >= 5) { 419 | userButtonTimer->detach(); 420 | idleCbs.RegisterIdeCallback(callback(&runPropertyEdtior)); 421 | } 422 | } 423 | 424 | static void InitPropertyEditor(InterruptIn *intrButton) 425 | { 426 | if (!intrButton) 427 | return; 428 | userButtonIntr = (InterruptIn *)intrButton; 429 | if (!userButtonTimer) 430 | userButtonTimer = new Timeout; 431 | userButtonTimerFunc(); // start Timer 432 | } 433 | 434 | #endif // FEATURE_LORA 435 | #endif // ARDUINO 436 | -------------------------------------------------------------------------------- /examples/RadioTest/RadioTest.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * The file is Licensed under the Apache License, Version 2.0 3 | * (c) 2017 Helmut Tschemernjak 4 | * 30826 Garbsen (Hannover) Germany 5 | */ 6 | 7 | 8 | /* 9 | * When the USE_DEMOBOARD_PAIR is disabled the following properties must be set 10 | * once using the PropertyEditor (see Examples->Arduino-mbed-APIs): 11 | * For LongRa boards only (already factory preset on the ECO Power boards): 12 | * LORA_DEVICE_ID (if not set, see on back of PCB, e.g. s10=123 (for ID 123) 13 | * LORA_CODE_ID (if not set, see on back of PCB, e.g. s11=0x6b17e559 14 | * Always: 15 | * LORA_RADIO_TYPE 16 | * - for the server s14=4 (RS_Station_Basic) 17 | * - for the client s14=3 (RS_Node_Online) or s14=1 (RS_Node_Offline) 18 | * LORA_REMOTE_ID, e.g.: s12=123 (replace 123 with your remote board ID) 19 | * Optionally: 20 | * LORA_APP_PWD, e.g.: s20=Hello (must be identical for all boards using this app) 21 | * The password will only allow clients to communicate with the same password 22 | * For AES128-bit content encryption, in addition to a password, set useAES = true (in line 46) 23 | */ 24 | 25 | #include "xPinMap.h" 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | 34 | #ifdef FEATURE_LORA 35 | 36 | 37 | #define CHECK_ERROR_RET(func, err) { \ 38 | if (err) { \ 39 | dprintf("Error in %s: %s", func, rs->StrError(err)); \ 40 | return err; \ 41 | } \ 42 | } 43 | 44 | 45 | bool server; // automatically being set if radioTypeMode RadioShuttle::RS_Station_Basic 46 | bool useAES = false; // AES needs the usePassword option on 47 | 48 | #define myTempSensorApp 0x0001 // Must be unique world wide. 49 | int myDeviceID; 50 | int myCode; 51 | int remoteDeviceID; 52 | RadioShuttle::RadioType radioTypeMode; // 1 = RS_Node_Offline, 3 = RS_Node_Online, 4 = RS_Station_Basic 53 | const char *appPassword; 54 | 55 | /* 56 | * For details review: SX1276GenericLib/sx1276/sx1276.h 57 | * Supported spreading factors SF 7,8, 9, 10, 11, (12 does not work well) 58 | * Working frequencies using the 125000 bandwidth which leaves 59 | * sufficient distance to the neighbour channel 60 | * EU: 868.1, 868.3, 868.5 (Default LoRaWAN EU channels) 61 | * EU: 865.1, 865.3, 865.5, 865.7, 865.9 (additional channels) 62 | * EU: 866.1, 866.3, 866.5, 866.7, 866.9 (additional channels) 63 | * EU: 867.1, 867.3, 867.5, 867.7, 867.9 (additional channels) 64 | * Utilisation of these channels should not exceed 1% per hour per node 65 | * Bandwidth changes other than 125k requires different channels distances 66 | * The last entry is a Freqency 67 | */ 68 | RadioShuttle::RadioProfile myProfile[] = { 69 | /* 70 | * Our default profile 71 | * frequency, bandwidth, TX power, spreading factor, frequency-offset 72 | */ 73 | { 868100000, 125000, 14, 7, 0}, 74 | { 0, 0, 0, 0, 0 }, 75 | }; 76 | 77 | 78 | void TempSensorRecvHandler(int AppID, RadioShuttle::devid_t stationID, int msgID, int status, void *buffer, int length) 79 | { 80 | UNUSED(stationID); 81 | UNUSED(buffer); 82 | UNUSED(AppID); 83 | 84 | switch (status) { 85 | case RadioShuttle::MS_SentCompleted: // A SendMsg has been sent. 86 | dprintf("MSG_SentCompleted: id=%d %d bytes", msgID, length); 87 | break; 88 | case RadioShuttle::MS_SentCompletedConfirmed:// A SendMsg has been sent and confirmed 89 | dprintf("MSG_SentCompletedConfirmed: id=%d %d bytes", msgID, length); 90 | break; 91 | case RadioShuttle::MS_SentTimeout: // A timeout occurred, number of retries exceeded 92 | dprintf("MSG_SentTimeout ID: %d", msgID); 93 | break; 94 | case RadioShuttle::MS_RecvData: // a simple input message 95 | dprintf("MSG_RecvData ID: %d, len=%d", msgID, length); 96 | // dump("MSG_RecvData", buffer, length); 97 | break; 98 | case RadioShuttle::MS_RecvDataConfirmed: // received a confirmed message 99 | dprintf("MSG_RecvDataConfirmed ID: %d, len=%d", msgID, length); 100 | // dump("MSG_RecvDataConfirmed", buffer, length); 101 | break; 102 | case RadioShuttle::MS_NoStationFound: 103 | dprintf("MSG_NoStationFound"); 104 | break; 105 | case RadioShuttle::MS_NoStationSupportsApp: 106 | dprintf("MSG_NoStationSupportsApp"); 107 | break; 108 | case RadioShuttle::MS_AuthenicationRequired: // the password does not match. 109 | dprintf("MSG_AuthenicationRequired"); 110 | break; 111 | case RadioShuttle::MS_StationConnected: // a confirmation that the connection was accepted 112 | dprintf("MSG_StationConnected"); 113 | break; 114 | case RadioShuttle::MS_StationDisconnected: // a confirmation that the disconnect was accepted 115 | dprintf("MSG_StationDisconnected"); 116 | break; 117 | default: 118 | break; 119 | } 120 | } 121 | 122 | /* 123 | * DigitalOut is a simple C++ wrapper around the GPIO ports (compatible with mbed.org). 124 | * It is easy to use and easier to understand, it allows reading and setting the object. 125 | * DigitalOut, DigitalIn and DigitalInOut are available. 126 | */ 127 | DigitalOut led(LED); 128 | 129 | /* 130 | * InterruptIn allows input registration for a specific pin. 131 | * intr.mode() "PullUp" or "PullDown" is supported. 132 | * intr.fall, intr.rise, high, low allows registering an interrupt 133 | * handler to be called upon a pin change event. 134 | * A callback() wrapper is required to provide C or C++ handlers 135 | * (compatible with mbed.org). 136 | */ 137 | InterruptIn userKeyIntr(SW0); 138 | volatile int pressedCount = 0; 139 | 140 | void SwitchInput(void) { 141 | dprintf("SwitchInput"); 142 | led = !led; 143 | pressedCount++; 144 | } 145 | 146 | 147 | Radio *radio; // the LoRa network interface 148 | RadioShuttle *rs; // the RadioShuttle protocol 149 | RadioStatusInterface *statusIntf; // the optional status interface 150 | RadioSecurityInterface *securityIntf; // the optional security interface 151 | 152 | int InitRadio() 153 | { 154 | RSCode err; 155 | 156 | #if defined(ARDUINO_HELTEC_WIFI_LORA_32_V2) || defined(ARDUINO_WIRELESS_STICK) || defined(ARDUINO_WIRELESS_STICK_LITE) 157 | // Vext power switch active 158 | radio = new SX1276Generic(NULL, HELTEC_L4_1276, 159 | LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET, 160 | LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5, LORA_ANT_PWR); 161 | #else 162 | // RFM95 compatible 163 | radio = new SX1276Generic(NULL, RFM95_SX1276, 164 | LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET, 165 | LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5); 166 | #endif 167 | 168 | statusIntf = new MyRadioStatus(); 169 | 170 | securityIntf = new RadioSecurity(); 171 | 172 | rs = new RadioShuttle("MyRadioShuttle"); 173 | 174 | rs->EnablePacketTrace(RadioShuttle::DEV_ID_ANY, true, true); 175 | 176 | err = rs->AddLicense(myDeviceID, myCode); 177 | CHECK_ERROR_RET("AddLicense", err); 178 | 179 | err = rs->AddRadio(radio, MODEM_LORA, myProfile); 180 | CHECK_ERROR_RET("AddRadio", err); 181 | dprintf("Radio: %d Hz, SF%d, %d kHz", myProfile[0].Frequency, myProfile[0].SpreadingFaktor, myProfile[0].Bandwidth / 1000); 182 | 183 | rs->AddRadioStatus(statusIntf); 184 | CHECK_ERROR_RET("AddRadioStatus", err); 185 | 186 | rs->AddRadioSecurity(securityIntf); 187 | CHECK_ERROR_RET("AddRadioSecurity", err); 188 | 189 | /* 190 | * The password parameter can be NULL if no password is required 191 | */ 192 | err = rs->RegisterApplication(myTempSensorApp, &TempSensorRecvHandler, (void *)appPassword); 193 | 194 | CHECK_ERROR_RET("RegisterApplication", err); 195 | 196 | if (server) { 197 | // usually RadioShuttle::RS_Station_Basic, set via properties 198 | err = rs->Startup(radioTypeMode); 199 | dprintf("Startup as a Server: %s ID=%d", rs->GetRadioName(rs->GetRadioType()), myDeviceID); 200 | } else { 201 | // usually RadioShuttle::RS_Node_Online or RadioShuttle, set via properties 202 | err = rs->Startup(radioTypeMode); 203 | dprintf("Startup as a Node: %s ID=%d", rs->GetRadioName(rs->GetRadioType()), myDeviceID); 204 | if (!err && rs->AppRequiresAuthentication(myTempSensorApp) == RS_PasswordSet) { 205 | err = rs->Connect(myTempSensorApp, remoteDeviceID); 206 | } 207 | } 208 | CHECK_ERROR_RET("Startup", err); 209 | return 0; 210 | } 211 | 212 | 213 | void DeInitRadio() 214 | { 215 | if (securityIntf) { 216 | delete securityIntf; 217 | securityIntf = NULL; 218 | } 219 | if (statusIntf) { 220 | delete statusIntf; 221 | statusIntf = NULL; 222 | } 223 | if (rs) { 224 | delete rs; 225 | rs = NULL; 226 | } 227 | if (radio) { 228 | delete radio; 229 | radio = NULL; 230 | } 231 | } 232 | 233 | 234 | void setup() { 235 | userKeyIntr.mode(PullUp); 236 | userKeyIntr.debounce(); 237 | MYSERIAL.begin(115200); 238 | InitSerial(&MYSERIAL, 5000, &led, !userKeyIntr.read()); // wait 5000ms that the Serial Monitor opens, otherwise turn off USB, use 0 for USB always on. 239 | SPI.begin(); 240 | RTCInit(__DATE__, __TIME__, &userKeyIntr); 241 | 242 | led = 1; 243 | 244 | bool nodeOffline = prop.GetProperty(prop.LORA_RADIO_TYPE, 0) == RadioShuttle::RS_Node_Offline; 245 | if (!SerialUSB_active && nodeOffline) { 246 | #ifdef ARDUINO_SAMD_ZERO 247 | userKeyIntr.low(callback(&SwitchInput)); // in deepsleep, D21 supports only low/high. 248 | #else 249 | userKeyIntr.fall(callback(&SwitchInput)); 250 | #endif 251 | } else { 252 | userKeyIntr.fall(callback(&SwitchInput)); 253 | } 254 | 255 | if (ESP32DeepsleepWakeup) { 256 | /* 257 | * We received a wakeup after an ESP32 deepsleep timeout or IO activity 258 | * on registered pins. Here we can add some checking code and quickly enter 259 | * into deepsleep() again, when appropriate (nothing to do). 260 | * Omitting the additional startup code and messages will save energy 261 | */ 262 | // Your code goes here: 263 | // deepsleep(); 264 | } 265 | 266 | dprintf("Welcome to RadioShuttle v%d.%d", RS_MAJOR, RS_MINOR); 267 | 268 | /* 269 | * The following are required properties for the operation 270 | */ 271 | myDeviceID = prop.GetProperty(prop.LORA_DEVICE_ID, 0); 272 | myCode = prop.GetProperty(prop.LORA_CODE_ID, 0); 273 | radioTypeMode = (RadioShuttle::RadioType)prop.GetProperty(prop.LORA_RADIO_TYPE, 0); 274 | remoteDeviceID = 1; 275 | 276 | #define USE_DEMOBOARD_PAIR 277 | #ifdef USE_DEMOBOARD_PAIR 278 | /* 279 | * for a demo, we ship a pair of boards with odd/even numbers, e.g. IDs 13/14 280 | * 13 for a server, 14 for a node 281 | */ 282 | if (myDeviceID & 0x01) { // odd demo board IDs are servers 283 | server = true; 284 | remoteDeviceID = myDeviceID + 1; 285 | radioTypeMode = RadioShuttle::RS_Station_Basic; 286 | } else { 287 | server = false; 288 | remoteDeviceID = myDeviceID - 1; 289 | radioTypeMode = RadioShuttle::RS_Node_Online; 290 | } 291 | #endif 292 | 293 | if (myDeviceID == 0 || myCode == 0 || radioTypeMode == 0) { 294 | dprintf("LORA_DEVICE_ID or LORA_CODE_ID or LORA_RADIO_TYPE not set, use PropertyEditor to set this!"); 295 | return; 296 | } 297 | if (radioTypeMode >= RadioShuttle::RS_Station_Basic) 298 | server = true; 299 | 300 | /* 301 | * Here are optional properties for custom settings 302 | */ 303 | int value; 304 | if ((value = prop.GetProperty(prop.LORA_REMOTE_ID, 0)) != 0) 305 | remoteDeviceID = value; 306 | if ((value = prop.GetProperty(prop.LORA_FREQUENCY, 0)) != 0) 307 | myProfile[0].Frequency = value; 308 | if ((value = prop.GetProperty(prop.LORA_BANDWIDTH, 0)) != 0) 309 | myProfile[0].Bandwidth = value; 310 | if ((value = prop.GetProperty(prop.LORA_SPREADING_FACTOR, 0)) != 0) 311 | myProfile[0].SpreadingFaktor = value; 312 | if ((value = prop.GetProperty(prop.LORA_TXPOWER, 0)) != 0) 313 | myProfile[0].TXPower = value; 314 | if ((value = prop.GetProperty(prop.LORA_FREQUENCY_OFFSET, 0)) != 0) 315 | myProfile[0].FrequencyOffset = value; 316 | appPassword = prop.GetProperty(prop.LORA_APP_PWD, (const char *)NULL); 317 | 318 | if (InitRadio() != 0) 319 | return; 320 | } 321 | 322 | 323 | void loop() { 324 | static int cnt = 0; 325 | 326 | if (cnt != pressedCount) { 327 | int flags = 0; 328 | flags |= RadioShuttle::MF_NeedsConfirm; // optional 329 | if (useAES && appPassword) 330 | flags |= RadioShuttle::MF_Encrypted; 331 | 332 | if (server) { 333 | static char msg[] = "The server feels very good today"; 334 | rs->SendMsg(myTempSensorApp, msg, sizeof(msg)-1, flags, remoteDeviceID); 335 | } else { 336 | static char msg[] = "Hello, the temperature is 26 celsius"; 337 | rs->SendMsg(myTempSensorApp, msg, sizeof(msg)-1, flags, remoteDeviceID); 338 | } 339 | cnt = pressedCount; 340 | } 341 | 342 | led = 0; 343 | if (!SerialUSB_active && rs->Idle() && rs->GetRadioType() == RadioShuttle::RS_Node_Offline) { 344 | /* 345 | * In deepsleep() the CPU is turned off, lowest power mode. 346 | * On the D21, we receive a RTC wakeup every 5 seconds to allow working 347 | * On the ESP32 an RTC a deep sleep restarts every 10 secs 348 | */ 349 | deepsleep(); 350 | } else { 351 | sleep(); // timer and radio interrupts will wakeup us 352 | } 353 | led = 1; 354 | rs->RunShuttle(); // process all pending events 355 | } 356 | 357 | #endif // FEATURE_LORA 358 | -------------------------------------------------------------------------------- /examples/RadioTest/xPinMap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The file is Licensed under the Apache License, Version 2.0 3 | * (c) 2018 Helmut Tschemernjak 4 | * 30826 Garbsen (Hannover) Germany 5 | */ 6 | #ifdef ARDUINO 7 | #define RS_MAJOR 4 8 | #define RS_MINOR 3 9 | 10 | #ifdef ARDUINO_SAMD_ATMEL_SAMD21_XPRO_V1 11 | 12 | #define FEATURE_LORA 1 13 | 14 | #define SW0 3 // switch needs pullup. 15 | #define LED LED_BUILTIN 16 | 17 | #define MYSERIAL Serial 18 | 19 | #define LORA_SPI_MOSI PIN_SPI_MOSI // PA06 20 | #define LORA_SPI_MISO PIN_SPI_MISO // PA04 21 | #define LORA_SPI_SCLK PIN_SPI_SCK // PA07 22 | #define LORA_CS PIN_SPI_SS // PA05 23 | #define LORA_RESET PIN_A0 // PB00 24 | #define LORA_DIO0 PIN_A1 // PB01 used for Rx, Tx Interrupt 25 | #define LORA_DIO1 PIN_A2 // PA10 Fifo Level/Full, RxTimeout/Cad Detection Interrupt, unused in RadioShuttle 26 | #define LORA_DIO2 PIN_A3 // PA11 FhssChangeChannel when FreqHop is on (unused in RadioShuttle) 27 | #define LORA_DIO3 PIN_A4 // PA02 used Cad Detection in RS_Node_Offline/Checking mode 28 | #define LORA_DIO4 PIN_A5 // PA03 FSK mode preamble detected, unused in RadioShuttle 29 | #define LORA_DIO5 NC // NC FSK mode ready / ClockOut, unused in RadioShuttle 30 | 31 | #elif __SAMD21G18A__ // Zero 32 | #define FEATURE_LORA 1 33 | 34 | // #define D21_LONGRA_REV_200 1 // board with Lipo power supply/charger, mini USB 35 | // #define D21_LONGRA_REV_301 1 // board with Lipo power supply/charger, micro USB 36 | // #define D21_LONGRA_REV_630 1 // board with Lipo power supply/charger, micro USB 37 | // #define D21_LONGRA_REV_720 1 // Maker Faire Hannover revision, micro USB 38 | #define D21_LONGRA_REV_750 1 // LongRa revision with more pins, micro USB 39 | 40 | 41 | 42 | #if defined(D21_LONGRA_REV_301) || defined(D21_LONGRA_REV_200) 43 | 44 | #define SW0 1 // switch needs pullup. 45 | #define LED 0 46 | #define LED2 LED 47 | #define MYSERIAL SerialUSB 48 | 49 | #define LORA_SPI_MOSI PIN_SPI_MOSI // PA06? 50 | #define LORA_SPI_MISO PIN_SPI_MISO // PA04? 51 | #define LORA_SPI_SCLK PIN_SPI_SCK // PA07? 52 | #define LORA_CS 2 // PA05? 53 | #define LORA_RESET 38 // PB00? 54 | #define LORA_DIO0 11 // used for Rx, Tx Interrupt 55 | #define LORA_DIO1 13 // Fifo Level/Full, RxTimeout/Cad Detection Interrupt, unused in RadioShuttle 56 | #define LORA_DIO2 10 // FhssChangeChannel when FreqHop is on, unused in RadioShuttle 57 | #define LORA_DIO3 3 // used Cad Detection in RS_Node_Offline/Checking mode 58 | #define LORA_DIO4 5 // FSK mode preamble detected, unused in RadioShuttle 59 | #define LORA_DIO5 NC // FSK mode ready / ClockOut, unused in RadioShuttle 60 | 61 | #elif defined (D21_LONGRA_REV_630) || defined (D21_LONGRA_REV_720) 62 | 63 | #define SW0 12 // PA19 switch needs pullup 64 | #define LED LED_BUILTIN // PA17 65 | #define MYSERIAL SerialUSB 66 | 67 | #define LORA_SPI_MOSI PIN_SPI_MOSI // PB10 68 | #define LORA_SPI_MISO PIN_SPI_MISO // PA12 69 | #define LORA_SPI_SCLK PIN_SPI_SCK // PB11 70 | #define LORA_CS 3 // PA09 71 | #define LORA_RESET 38 // PA13 72 | #define LORA_DIO0 16 // PB09 used for Rx, Tx Interrupt 73 | #define LORA_DIO1 5 // PA15 Fifo Level/Full, RxTimeout/Cad Detection Interrupt, unused in RadioShuttle 74 | #define LORA_DIO2 2 // PA14 FhssChangeChannel when FreqHop is on, unused in RadioShuttle 75 | #define LORA_DIO3 18 // PA05 used Cad Detection in RS_Node_Offline/Checking mode 76 | #define LORA_DIO4 17 // PA04 FSK mode preamble detected, unused in RadioShuttle 77 | #define LORA_DIO5 NC // FSK mode ready / ClockOut, unused in RadioShuttle 78 | 79 | #define BOOSTER_EN33 9 // Enable 3.3 volt 150mA max 80 | #define BOOSTER_EN50 8 // Enable 5.0 volt 150mA max 81 | #define DISPLAY_EN 4 // Turn on display power (3.3 V must be enabled first) 82 | #define EXT_POWER_ON 0 83 | #define EXT_POWER_OFF 1// For LongRa 7.2 new resistors are required before the following can be enabled 84 | // #define BAT_MESURE_EN 27 // Optional: turn for measurement PA28 85 | // #define BAT_MESURE_ADC 19 // Analog-in for battery measurement PB02/A5 86 | // #define BAT_VOLTAGE_DIVIDER ((82.0+220.0)/82.0) // 82k + 220k 1% 87 | 88 | #define DISPLAY_SDA PIN_WIRE_SDA 89 | #define DISPLAY_SCL PIN_WIRE_SCL 90 | #define DISPLAY_ADDRESS 0x3c 91 | 92 | #elif defined (D21_LONGRA_REV_750) 93 | 94 | #define SW0 12 // PA19 switch needs pullup 95 | #define LED LED_BUILTIN // PA17 96 | #define LED2 LED 97 | #define MYSERIAL SerialUSB 98 | 99 | #define LORA_SPI_MOSI PIN_SPI_MOSI // PB10 100 | #define LORA_SPI_MISO PIN_SPI_MISO // PA12 101 | #define LORA_SPI_SCLK PIN_SPI_SCK // PB11 102 | #define LORA_CS 3 // PA09 103 | #define LORA_RESET 27 // PA28 104 | #define LORA_DIO0 38 // PA13 used for Rx, Tx Interrupt 105 | #define LORA_DIO1 NC // Fifo Level/Full, RxTimeout/Cad Detection Interrupt, unused in RadioShuttle 106 | #define LORA_DIO2 NC // PA14 FhssChangeChannel when FreqHop is on, unused in RadioShuttle 107 | #define LORA_DIO3 NC // PA05 used Cad Detection in RS_Node_Offline/Checking mode 108 | #define LORA_DIO4 NC // PA04 FSK mode preamble detected, unused in RadioShuttle 109 | #define LORA_DIO5 NC // FSK mode ready / ClockOut, unused in RadioShuttle 110 | 111 | #define BOOSTER_EN33 9 // Enable 3.3 volt 150mA max 112 | #define BOOSTER_EN50 8 // Enable 5.0 volt 150mA max 113 | #define DISPLAY_EN 4 // Turn on display power (3.3 V must be enabled first) 114 | #define EXT_POWER_ON 0 115 | #define EXT_POWER_OFF 1 116 | #define BAT_MESURE_EN 45 // Opptional turn for measurement PA31/SWD 117 | #define BAT_MESURE_ADC 19 // Analog-in for battery measurement PB02/A5 118 | #define BAT_VOLTAGE_DIVIDER ((82.0+220.0)/82.0) // 82k + 220k 1% 119 | 120 | #define DISPLAY_SDA SDA 121 | #define DISPLAY_SCL SCL 122 | #define DISPLAY_ADDRESS 0x3c 123 | #define DISPLAY_RESET NC 124 | 125 | #elif defined(ARDUINO_SAMD_FEATHER_M0) // Feather M0 w/Radio 126 | 127 | #define SW0 12 // switch needs pullup, must be conected to the headers 128 | #define LED LED_BUILTIN // 13 129 | #define LED2 LED 130 | #define MYSERIAL Serial // this is a USB Serial, however the Feather M0 calls it only Serial. 131 | 132 | #define LORA_SPI_MOSI PIN_SPI_MOSI // PA12 133 | #define LORA_SPI_MISO PIN_SPI_MISO // PB10 134 | #define LORA_SPI_SCLK PIN_SPI_SCK // PB11 135 | #define LORA_CS 8 // PA06 136 | #define LORA_RESET 4 // PA08 137 | #define LORA_DIO0 3 // PA09 used for Rx, Tx Interrupt, Cad Detection 138 | #define LORA_DIO1 NC // Fifo Level/Full, RxTimeout/Cad Detection Interrupt, unused in RadioShuttle 139 | #define LORA_DIO2 NC // FhssChangeChannel when FreqHop is on, unused in RadioShuttle 140 | #define LORA_DIO3 NC // used Cad Detection in RS_Node_Offline/Checking mode 141 | #define LORA_DIO4 NC // FSK mode preamble detected, unused in RadioShuttle 142 | #define LORA_DIO5 NC // FSK mode ready / ClockOut, unused in RadioShuttle 143 | 144 | // For Feather M0 new resistors are required before the following can be enabled 145 | // #define BAT_MESURE_ADC D9 // Analog-in for batterie measurement PA07/D9 146 | // #define BAT_VOLTAGE_DIVIDER ((82.0+220.0)/82.0) // 82k + 220k 1% (R6/R3) 147 | 148 | /* 149 | * PIN_LED_RXL and PIN_LED_TXL are already defined. 150 | */ 151 | #else 152 | 153 | #error "Unkown D21 board revision" 154 | 155 | #endif 156 | 157 | #elif defined(ARDUINO_ESP32_DEV) // the ESP32 ECO Power Boards (with and without LoRa) 158 | 159 | #define ESP32_ECO_POWER_REV_1 // our rev1 ESP32 ECO Power Boards 160 | 161 | #ifdef ESP32_ECO_POWER_REV_1 162 | #define ESP32_ECO_POWER 163 | #define FEATURE_LORA 1 164 | 165 | #define SW0 0 // uses hardware pullup 166 | #define LED 2 // green LED 167 | #define LED2 12 // red LED 168 | #define MYSERIAL Serial // the regular serial IO1_TXD0/IO3_RXD0 169 | #define FEATURE_RTC_DS3231 // an I2C clock. 170 | #define FEATURE_SI7021 // Temperature & Humidity add-on sensor 171 | 172 | #define LORA_SPI_MOSI MOSI // MOSI 23 Arduino-Dev 173 | #define LORA_SPI_MISO MISO // MISO 19 Arduino-Dev 174 | #define LORA_SPI_SCLK SCK // SCK 18 Arduino-Dev 175 | #define LORA_CS 5 // LORA_DEFAULT_SS_PIN 176 | #define LORA_RESET 17 // LORA_DEFAULT_RESET_PIN 177 | #define LORA_DIO0 16 // LORA_DEFAULT_DIO0_PIN 178 | #define LORA_DIO1 NC // Fifo Level/Full, RxTimeout/Cad Detection Interrupt, unused in RadioShuttle 179 | #define LORA_DIO2 NC // FhssChangeChannel when FreqHop is on, unused in RadioShuttle 180 | #define LORA_DIO3 NC // used Cad Detection in RS_Node_Offline/Checking mode 181 | #define LORA_DIO4 NC // FSK mode preamble detected, unused in RadioShuttle 182 | #define LORA_DIO5 NC // FSK mode ready / ClockOut, unused in RadioShuttle 183 | 184 | #define RTC_I2C_SDA SDA // DS3231 RTC Chip 185 | #define RTC_I2C_SDL SDL // 186 | #define RTC_INTR SWO // DS3231 RTC interrupt sharred with the switch 187 | 188 | #define EXT_POWER_SW 15 // Switch VDD on pin JP10 189 | #define EXT_POWER_ON 0 190 | #define EXT_POWER_OFF 1 191 | 192 | #define BAT_MESURE_EN EXT_POWER_SW // Turn power on for messurement 193 | #define BAT_MESURE_ADC 35 // Analog-in for batterie measurement 194 | #define BAT_VOLTAGE_DIVIDER ((82.0+220.0)/82.0) // 82k + 220k 1% 195 | 196 | #define DISPLAY_SDA SDA 197 | #define DISPLAY_SCL SCL 198 | #define DISPLAY_ADDRESS 0x3c 199 | #define DISPLAY_RESET NC 200 | 201 | #else 202 | 203 | #define SW0 0 // no pullup 204 | #define LED 2 // red LED 205 | #define MYSERIAL Serial // 206 | 207 | #endif 208 | 209 | #elif defined(ARDUINO_Heltec_WIFI_LoRa_32) \ 210 | || defined(ARDUINO_WIFI_LORA_32) || defined(ARDUINO_HELTEC_WIFI_LORA_32_V2) || defined(ARDUINO_heltec_wifi_lora_32_V2) \ 211 | || defined(ARDUINO_HELTEC_WIRELESS_STICK) || defined(ARDUINO_HELTEC_WIRELESS_STICK_LITE) // the Heltec boards 212 | #define FEATURE_LORA 1 213 | 214 | #if defined(ARDUINO_HELTEC_WIFI_LORA_32_V2) || defined(ARDUINO_heltec_wifi_lora_32_V2) || defined(ARDUINO_HELTEC_WIRELESS_STICK) || defined(ARDUINO_HELTEC_WIRELESS_STICK_LITE) 215 | #define EXT_POWER_SW Vext 216 | #define EXT_POWER_ON 0 217 | #define EXT_POWER_OFF 1 218 | 219 | #define BAT_MESURE_EN EXT_POWER_SW // Turn power on for messurement 220 | #define BAT_MESURE_ADC 37 // Analog-in for batterie measurement 221 | #define BAT_VOLTAGE_DIVIDER ((100.0+220.0)/100.0) // 100k + 220k 1% 222 | #else 223 | #define EXT_POWER_SW NC 224 | #endif 225 | 226 | #define SW0 0 // no pullup, TODO check setup code 227 | #ifdef ARDUINO_Heltec_WIFI_LoRa_32 228 | #define LED 25 // 229 | #endif 230 | #define LED2 LED // 231 | #define MYSERIAL Serial // this is a USB Serial, however the Feather M0 calls it only Serial. 232 | 233 | #define LORA_SPI_MOSI MOSI // MOSI 27 Heltec, 23 Arduino-Dev 234 | #define LORA_SPI_MISO MISO // MISO 19 Heltec, 19 Arduino-Dev 235 | #define LORA_SPI_SCLK SCK // SCK 5 Heltec, 18 Arduino-Dev 236 | #define LORA_CS SS // LORA_DEFAULT_SS_PIN 237 | #ifdef ARDUINO_Heltec_WIFI_LoRa_32 238 | #define LORA_RESET 14 // LORA_DEFAULT_RESET_PIN 239 | #define LORA_DIO0 26 // LORA_DEFAULT_DIO0_PIN 240 | #else 241 | #define LORA_RESET RST_LoRa // LORA_DEFAULT_RESET_PIN 242 | #define LORA_DIO0 DIO0 // LORA_DEFAULT_DIO0_PIN 243 | #endif 244 | #define LORA_DIO1 NC // Fifo Level/Full, RxTimeout/Cad Detection Interrupt, unused in RadioShuttle 245 | #define LORA_DIO2 NC // FhssChangeChannel when FreqHop is on, unused in RadioShuttle 246 | #define LORA_DIO3 NC // used Cad Detection in RS_Node_Offline/Checking mode 247 | #define LORA_DIO4 NC // FSK mode preamble detected, unused in RadioShuttle 248 | #define LORA_DIO5 NC // FSK mode ready / ClockOut, unused in RadioShuttle 249 | #define LORA_ANT_PWR EXT_POWER_SW // the analog switch is getting turned off go save energy 250 | 251 | #ifdef ARDUINO_Heltec_WIFI_LoRa_32 252 | #define DISPLAY_ADDRESS 0x3c 253 | #define DISPLAY_SDA 4 254 | #define DISPLAY_SCL 15 255 | #define DISPLAY_RESET 16 256 | 257 | #elif defined(ARDUINO_WIFI_LORA_32) || defined(ARDUINO_HELTEC_WIFI_LORA_32_V2) || defined(ARDUINO_heltec_wifi_lora_32_V2) || defined(ARDUINO_HELTEC_WIRELESS_STICK) 258 | #define DISPLAY_ADDRESS 0x3c 259 | #define DISPLAY_SDA SDA_OLED 260 | #define DISPLAY_SCL SCL_OLED 261 | #define DISPLAY_RESET RST_OLED 262 | #endif 263 | 264 | #elif defined(ARDUINO_ESP32S2_DEV) || defined(ARDUINO_ESP32S2_USB) // the ESP32 S2 DevKit Boards 265 | 266 | #define FEATURE_LORA 1 267 | 268 | #define SW0 0 // no pullup, TODO check setup code 269 | #define LED 2 // no LED connected 270 | #define LED2 LED 271 | #define MYSERIAL Serial // the regular serial IO1_TXD0/IO3_RXD0 272 | 273 | #elif defined(ARDUINO_ESP32C3_DEV) // the ESP32-C3-DevKitM-1 Boards 274 | 275 | #define FEATURE_LORA 1 276 | 277 | #define SW0 9 // no pullup, TODO check setup code 278 | #define LED 2 // no LED connected 279 | #define LED2 LED 280 | #define MYSERIAL Serial // the regular serial IO1_TXD0/IO3_RXD0 281 | 282 | 283 | #else 284 | #error "unkown board" 285 | #endif 286 | 287 | #endif // Arduino 288 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=RadioShuttle Library 2 | version=4.1.0 3 | author=Helmut Tschemernjak 4 | maintainer=Helmut Tschemernjak 5 | sentence=A radio networking protocol stack being highly optimized for low-latency radio communication over the AIR. The RadioSuttle protocol supports thousends of clients with reliable and secure communication. It depends on the SX1276GenericLib library. 6 | paragraph= 7 | category=Communication 8 | url=https://github.com/RadioShuttle/RadioShuttleLib 9 | architectures=samd,esp32 10 | -------------------------------------------------------------------------------- /src/RadioSecurity.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The file is licensed under the Apache License, Version 2.0 3 | * (c) 2019 Helmut Tschemernjak 4 | * 30826 Garbsen (Hannover) Germany 5 | */ 6 | 7 | #ifdef ARDUINO 8 | #include 9 | #define FEATURE_LORA 1 10 | #include "arduino-util.h" 11 | #endif 12 | 13 | #ifdef __MBED__ 14 | #include "mbed.h" 15 | #include "xPinMap.h" 16 | #endif 17 | 18 | #ifdef FEATURE_LORA 19 | 20 | #include "RadioSecurityInterface.h" 21 | #include "RadioSecurity.h" 22 | 23 | #include "rs_sha256.h" 24 | #include "rs_aes.h" 25 | 26 | #ifndef DPRINTF_AVAILABLE 27 | #define dprintf(...) void() 28 | #define dump(a,b,c) void() 29 | #endif 30 | 31 | RadioSecurity::RadioSecurity(void) 32 | { 33 | 34 | } 35 | 36 | RadioSecurity::~RadioSecurity(void) 37 | { 38 | } 39 | 40 | int 41 | RadioSecurity::GetSecurityVersion(void) 42 | { 43 | return _securityVers; 44 | } 45 | 46 | int 47 | RadioSecurity::GetHashBlockSize(void) 48 | { 49 | return SHA256_BLOCK_SIZE; 50 | } 51 | 52 | void 53 | RadioSecurity::HashPassword(void *seed, int seedLen, void *password, int pwLen, void *hashResult) 54 | { 55 | SHA256_CTX *shactx = (SHA256_CTX *)new uint8_t[sizeof(SHA256_CTX)]; 56 | if (!shactx) 57 | return; 58 | 59 | sha256_init(shactx); 60 | if (seedLen) 61 | sha256_update(shactx, (BYTE *)seed, seedLen); 62 | if (password) 63 | sha256_update(shactx, (BYTE *)password, pwLen); 64 | sha256_final(shactx, (BYTE *)hashResult); 65 | 66 | delete[] shactx; 67 | } 68 | 69 | 70 | int 71 | RadioSecurity::GetEncryptionBlockSize(void) 72 | { 73 | return AES128_KEYLEN; 74 | } 75 | 76 | 77 | 78 | void * 79 | RadioSecurity::CreateEncryptionContext(void *key, int keyLen, void *seed, int seedlen) 80 | { 81 | AES_CTX *aesctx = (AES_CTX *)new uint8_t[sizeof(AES_CTX)]; 82 | 83 | uint8_t mykey[AES128_KEYLEN]; 84 | uint8_t myseed[AES128_KEYLEN]; 85 | 86 | if (seed) { 87 | memset(myseed, 0, sizeof(myseed)); 88 | memcpy(myseed, seed, seedlen > AES128_KEYLEN ? AES128_KEYLEN : seedlen); 89 | } 90 | memset(mykey, 0, sizeof(mykey)); 91 | memcpy(mykey, key, keyLen > AES128_KEYLEN ? AES128_KEYLEN : keyLen); 92 | 93 | AES128_InitContext(aesctx, mykey, seed ? myseed : NULL); 94 | 95 | return aesctx; 96 | } 97 | 98 | void 99 | RadioSecurity::DestroyEncryptionContext(void *context) 100 | { 101 | delete[] (AES_CTX *)context; 102 | } 103 | 104 | 105 | void 106 | RadioSecurity::EncryptMessage(void *context, const void *input, void *output, int len) 107 | { 108 | uint8_t *in = (uint8_t *)input; 109 | uint8_t *out = (uint8_t *)output; 110 | int off = 0; 111 | 112 | while (off < len) { 113 | AES128_ECB_encrypt((AES_CTX *)context, in + off, out + off); 114 | off += AES128_KEYLEN; 115 | } 116 | } 117 | 118 | 119 | void 120 | RadioSecurity::DecryptMessage(void *context, const void *input, void *output, int len) 121 | { 122 | uint8_t *in = (uint8_t *)input; 123 | uint8_t *out = (uint8_t *)output; 124 | int off = 0; 125 | 126 | while (off < len) { 127 | AES128_ECB_decrypt((AES_CTX *)context, in + off, out + off); 128 | off += AES128_KEYLEN; 129 | } 130 | } 131 | 132 | void 133 | RadioSecurity::EncryptTest(void) 134 | { 135 | uint8_t key[] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}; 136 | uint8_t iv[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; 137 | 138 | { 139 | 140 | dprintf("ECB encrypt: "); 141 | void *context = CreateEncryptionContext(key, sizeof(key)); 142 | 143 | // static void test_encrypt_ecb(void) 144 | uint8_t in[] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}; 145 | uint8_t out[] = {0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, 0xa8, 0x9e, 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97}; 146 | uint8_t buffer[16]; 147 | 148 | 149 | AES128_ECB_encrypt((AES_CTX *)context, in, buffer); 150 | 151 | 152 | if (memcmp((char*) out, (char*) buffer, 16) == 0) 153 | { 154 | dprintf("SUCCESS!"); 155 | } else { 156 | dprintf("FAILURE!"); 157 | } 158 | DestroyEncryptionContext(context); 159 | } 160 | 161 | { 162 | dprintf("ECB decrypt: "); 163 | void *context = CreateEncryptionContext(key, sizeof(key)); 164 | 165 | uint8_t in[] = {0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, 0xa8, 0x9e, 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97}; 166 | uint8_t out[] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}; 167 | uint8_t buffer[16]; 168 | 169 | AES128_ECB_decrypt((AES_CTX *)context, in, buffer); 170 | 171 | if(memcmp((char*) out, (char*) buffer, 16) == 0) { 172 | dprintf("SUCCESS!"); 173 | } else { 174 | dprintf("FAILURE!"); 175 | } 176 | DestroyEncryptionContext(context); 177 | } 178 | 179 | 180 | { 181 | dprintf("CBC encrypt: "); 182 | void *context = CreateEncryptionContext(key, sizeof(key), iv, sizeof(iv)); 183 | 184 | uint8_t in[] = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 185 | 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 186 | 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, 187 | 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 }; 188 | uint8_t out[] = { 0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d, 189 | 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2, 190 | 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16, 191 | 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7 }; 192 | 193 | uint8_t buffer[64]; 194 | 195 | 196 | AES128_CBC_encrypt_buffer((AES_CTX *)context, buffer, in, 64); 197 | 198 | 199 | if(memcmp((char*) out, (char*) buffer, 64) == 0) { 200 | dprintf("SUCCESS!"); 201 | } else { 202 | dprintf("FAILURE!"); 203 | } 204 | DestroyEncryptionContext(context); 205 | } 206 | 207 | { 208 | dprintf("CBC decrypt: "); 209 | void *context = CreateEncryptionContext(key, sizeof(key), iv, sizeof(iv)); 210 | 211 | uint8_t in[] = { 0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d, 212 | 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2, 213 | 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16, 214 | 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7 }; 215 | uint8_t out[] = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 216 | 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 217 | 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, 218 | 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 }; 219 | uint8_t buffer[64]; 220 | 221 | 222 | AES128_CBC_decrypt_buffer((AES_CTX *)context, buffer+0, in+0, 16); 223 | AES128_CBC_decrypt_buffer((AES_CTX *)context, buffer+16, in+16, 16); 224 | AES128_CBC_decrypt_buffer((AES_CTX *)context, buffer+32, in+32, 16); 225 | AES128_CBC_decrypt_buffer((AES_CTX *)context, buffer+48, in+48, 16); 226 | 227 | if (memcmp((char*) out, (char*) buffer, 64) == 0) { 228 | dprintf("SUCCESS!"); 229 | } else { 230 | dprintf("FAILURE!"); 231 | } 232 | DestroyEncryptionContext(context); 233 | } 234 | } 235 | 236 | 237 | #endif // FEATURE_LORA 238 | -------------------------------------------------------------------------------- /src/RadioSecurity.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The file is licensed under the Apache License, Version 2.0 3 | * (c) 2019 Helmut Tschemernjak 4 | * 30826 Garbsen (Hannover) Germany 5 | */ 6 | 7 | #ifndef __RADIOSECURITY_H__ 8 | #define __RADIOSECURITY_H__ 9 | 10 | 11 | class RadioSecurity : public RadioSecurityInterface { 12 | public: 13 | RadioSecurity(); 14 | virtual ~RadioSecurity(); 15 | virtual int GetSecurityVersion(void); 16 | /* 17 | * The hash block size for SHA256 in bytes 18 | */ 19 | virtual int GetHashBlockSize(void); 20 | virtual void HashPassword(void *seed, int seedLen, void *password, int pwLen, void *hashResult); 21 | 22 | virtual int GetEncryptionBlockSize(void); 23 | virtual void *CreateEncryptionContext(void *key, int keyLen, void *seed = NULL, int seedlen = 0); 24 | virtual void DestroyEncryptionContext(void *context); 25 | virtual void EncryptMessage(void *context, const void *input, void *output, int len); 26 | virtual void DecryptMessage(void *context, const void *input, void *output, int len); 27 | virtual void EncryptTest(void); 28 | private: 29 | static int const _securityVers = 1; 30 | }; 31 | 32 | #endif // RadioSecurity.h 33 | -------------------------------------------------------------------------------- /src/RadioSecurityInterface.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The file is licensed under the Apache License, Version 2.0 3 | * (c) 2019 Helmut Tschemernjak 4 | * 30826 Garbsen (Hannover) Germany 5 | */ 6 | 7 | #ifndef __RADIOSECURITYINTERFACE_H__ 8 | #define __RADIOSECURITYINTERFACE_H__ 9 | 10 | class RadioSecurityInterface { 11 | public: 12 | virtual ~RadioSecurityInterface() { } 13 | 14 | /* 15 | * Get security protocol version to allow 16 | * and differentiate between multiple security versions 17 | */ 18 | virtual int GetSecurityVersion(void) = 0; 19 | 20 | /* 21 | * The block size for the hash code (e.g. SHA256) in bytes 22 | */ 23 | virtual int GetHashBlockSize(void) = 0; 24 | 25 | /* 26 | * The calculation for the public password hash code utilizes a seed (e.g. random) 27 | * and the cleartext password 28 | */ 29 | virtual void HashPassword(void *seed, int seedLen, void *password, int pwLen, void *hashResult) = 0; 30 | 31 | /* 32 | * The encryption/decryption block size in bytes (e.g. 16 bytes for AES128) 33 | */ 34 | virtual int GetEncryptionBlockSize(void) = 0; 35 | 36 | /* 37 | * The creation of a context allocates the memory needed, and initializes 38 | * its data (e.g. key and initial vector 'iv' for AES) 39 | */ 40 | virtual void *CreateEncryptionContext(void *key, int keyLen, void *seed = NULL, int seedlen = 0) = 0; 41 | 42 | /* 43 | * Release the context and its allocated memory from CreateEncryptionContext 44 | */ 45 | virtual void DestroyEncryptionContext(void *context) = 0; 46 | 47 | /* 48 | *Encrypts a cleartext input message into an encrypted output block 49 | */ 50 | virtual void EncryptMessage(void *context, const void *input, void *output, int len) = 0; 51 | 52 | /* 53 | * Decrypts an input block into an cleartext output message 54 | */ 55 | virtual void DecryptMessage(void *context, const void *input, void *output, int len) = 0; 56 | 57 | virtual void EncryptTest(void) = 0; 58 | }; 59 | 60 | #endif // __RADIOSECURITYINTERFACE_H__ 61 | -------------------------------------------------------------------------------- /src/RadioShuttle.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This is work copyright 3 | * (c) 2019 Helmut Tschemernjak 4 | * 30826 Garbsen (Hannover) Germany 5 | * 6 | * 7 | * Use is granted to registered RadioShuttle licensees only. 8 | * Licensees must own a valid serial number and product code. 9 | * Details see: www.radioshuttle.de 10 | */ 11 | 12 | #ifndef __RADIOSHUTTLE_H__ 13 | #define __RADIOSHUTTLE_H__ 14 | 15 | #ifdef ARDUINO 16 | #include "arduino-mbed.h" 17 | #include 18 | #include 19 | #undef min 20 | #undef max 21 | #undef map 22 | #define MyTimeout Timeout 23 | #define MyTimer Timer 24 | #define STATIC_ASSERT _Static_assert 25 | #define ASSERT assert 26 | using namespace std; 27 | #define FEATURE_LORA 1 28 | 29 | #else 30 | 31 | #if defined(DEVICE_LPTICKER) || defined(DEVICE_LOWPOWERTIMER) // LOWPOWERTIMER in older mbed versions 32 | #define MyTimeout LowPowerTimeout 33 | #define MyTimer LowPowerTimer 34 | #else 35 | #define MyTimeout Timeout 36 | #define MyTimer Timer 37 | #endif 38 | #define IRAM_ATTR 39 | 40 | #endif 41 | 42 | #include 43 | #include 44 | #include "radio.h" 45 | #include "RadioStatusInterface.h" 46 | #include "RadioSecurityInterface.h" 47 | 48 | #ifdef ARDUINO 49 | #define map std::map // map clashes with Arduino map() 50 | #endif 51 | #ifdef FEATURE_LORA 52 | 53 | 54 | typedef enum RSErrorCode { 55 | RS_NoErr = 0, 56 | RS_InvalidProductCode = 1, // license does not match 57 | RS_DuplicateAppID, 58 | RS_AppID_NotFound, 59 | RS_StationNotConnected, 60 | RS_NoPasswordSet, 61 | RS_PasswordSet, 62 | RS_NoSecurityInterface, 63 | RS_MsgID_NotFound, 64 | RS_NoRadioConfigured, // No radio added so far 65 | RS_NoRadioAvailable, // Radio could not be detected 66 | RS_RadioNotFound, // The specified radio is not available 67 | RS_UnknownModemType, // No FSK no LoRa modem type 68 | RS_MessageSizeExceeded, // Message size too long 69 | RS_InvalidParam, // invalid parameter. 70 | RS_OutOfMemory, // unable to allocate memory 71 | } RSCode; 72 | 73 | 74 | 75 | // Shuttle 76 | class RadioShuttle 77 | { 78 | public: 79 | typedef uint32_t devid_t; 80 | static const int DEV_ID_ANY = 0; 81 | static const int TX_POWER_AUTO = 9999; 82 | 83 | struct RadioProfile { 84 | int Frequency; // in Hz 85 | int Bandwidth; // in Hz 86 | int TXPower; // in dBm 87 | int SpreadingFaktor;// 7-12 88 | int FrequencyOffset;// +/- in Hz 89 | }; 90 | 91 | enum RadioType { 92 | RS_RadioType_Invalid = 0, 93 | RS_Node_Offline, // Sleep mode until sending, < 10k RAM 94 | RS_Node_Checking, // Sleep mode, checks for messages regulary, < 10k RAM 95 | RS_Node_Online, // Always powered-on, < 10k RAM 96 | RS_Station_Basic, // Always active for one or more apps < 15k RAM 97 | RS_Station_Server, // Always active, lots of memory, routing options, < 1 MB RAM 98 | }; 99 | 100 | typedef void (*AppRecvHandler)(int AppID, devid_t stationID, int msgID, int status, void *data, int length); 101 | typedef void (*AppStartupHandler)(void *context); 102 | 103 | enum MsgStatus { 104 | MS_SentCompleted, // A previous SendMsg has been sent 105 | MS_SentCompletedConfirmed, // A previous SendMsg has been sent and confirmed 106 | MS_SentTimeout, // A timeout occurred, number of retries exceeded 107 | 108 | MS_RecvData, // A simple input message 109 | MS_RecvDataConfirmed, // Received a confirmed message 110 | MS_NoStationFound, 111 | MS_NoStationSupportsApp, 112 | MS_AuthenicationRequired, 113 | 114 | MS_StationConnected, // A confirmation that the connection was accepted 115 | MS_StationDisconnected, // A confirmation that the disconnect was accepted 116 | }; 117 | 118 | enum MsgFlags { 119 | MF_Response = 0x01, // A response from a previous request, or request 120 | MF_NeedsConfirm = 0x02, // Station needs to acknloglage receivd 121 | MF_LowPriority = 0x04, // Transfer within one minute 122 | MF_HighPriority = 0x08, // ImmEdate transfer 123 | MF_Direct = 0x10, // Direct communication without request 124 | MF_Connect = 0x20, // Connect a node to the station with password 125 | MF_Encrypted = 0x40, // Message is encrypted 126 | MF_Authentication = 0x80, // Message requires prior authentication 127 | MF_SwitchOptions = 0x100,// Tell the node to switch the channel for this trans ID 128 | MF_FlagsMask = 0b111111111, // max flags for the RadioShuttle protocol, see msgFlags : 9 129 | /* 130 | * optional control flags are here. 131 | */ 132 | CF_FreeData = 0x200, // if a data buffer is provided, free it afterwords. 133 | CF_CopyData = 0x400, // create a copy of the data. 134 | }; 135 | 136 | struct RadioStats { 137 | int RxPackets; 138 | int TxPackets; 139 | int RxErrorCount; 140 | int channelBusyCount; 141 | long long rxBytes; 142 | long long txBytes; 143 | int noAuthMessageCount; 144 | int unkownMessageCount; 145 | int appNotSupported; 146 | int protocolError; 147 | int noMemoryError; 148 | int decryptError; 149 | int lastRSSI; 150 | int lastSNR; 151 | devid_t lastRXdeviceID; 152 | time_t startupTime; 153 | }; 154 | 155 | /* 156 | * Constructor requires a radio 157 | */ 158 | RadioShuttle(const char *deviceName); 159 | 160 | /* 161 | * Destructor, stop radios, free resources 162 | */ 163 | ~RadioShuttle(); 164 | 165 | /* 166 | * Adds a license to use the RadioShuttle, licenses are 167 | * may be bundled with the board or are available for 168 | * purchase at: www.radioshuttle.com 169 | * The license is bound to a special board ID and a fixed 170 | * node deviceID. 171 | */ 172 | RSCode AddLicense(devid_t deviceID, uint32_t productCode); 173 | 174 | /* 175 | * Adds a new radio with a given profile 176 | * Optional profile, must be called before startup 177 | * The retry delay allows to to specify a custom retry value (e.g. 4000 for 4 secs) 178 | * The default retry delay is the MAX MTU transfer time for a given spreading factor 179 | * plus some random time to avoid collisions. 180 | */ 181 | RSCode AddRadio(Radio *radio, RadioModems_t modem, const struct RadioProfile *profile = NULL, int customRetryDelay_ms = 0); 182 | 183 | /* 184 | * This allows to switch between RS_Node_Offline and RS_Node_Online 185 | * after the Startup() is already completed. 186 | */ 187 | RSCode UpdateNodeStartup(RadioType newRadioType); 188 | 189 | /* 190 | * The status interface allows custom status callbacks for 191 | * reporting send/receive/timeout activity e.g.: LED blinks 192 | */ 193 | RSCode AddRadioStatus(RadioStatusInterface *statusIntf); 194 | 195 | /* 196 | * Support function for password hash generation and encryption 197 | * The external API interface has the advantage that the security 198 | * can be upgraded independently of the RadioShuttle library. 199 | * AES hardware accelleration can be one reason for it. 200 | */ 201 | RSCode AddRadioSecurity(RadioSecurityInterface *securityIntf); 202 | 203 | /* 204 | * Starts the service with the specified RadioType 205 | * The optional deviceID allows to specify a custom ID, e.g. for failover. 206 | */ 207 | RSCode Startup(RadioType radioType, devid_t deviceID = 0); 208 | 209 | /* 210 | * get the current radio type 211 | */ 212 | RadioType GetRadioType(void); 213 | 214 | /* 215 | * Registers an application, the AppType is unique worldwide 216 | * and must be used for sending and receiving app data. 217 | * Multiple AppID registrations are supported. 218 | * The password can be a string to the password, 219 | * in case of binary passwd data the pwLen must be set. 220 | * If the password is set, clients must call Connect() prior 221 | * to any SendMsg. 222 | */ 223 | RSCode RegisterApplication(int AppID, AppRecvHandler, void *password = NULL, int pwLen = 0); 224 | /* 225 | * Removes the AppID 226 | */ 227 | RSCode DeRegisterApplication(int AppID); 228 | 229 | /* 230 | * Check if the password is specified for an app 231 | */ 232 | RSCode AppRequiresAuthentication(int AppID); 233 | 234 | /* 235 | * Start a pairing process to connect a node to a station 236 | */ 237 | RSCode StartPairing(char *name = NULL); 238 | /* 239 | * Connect the node against a station, it can be called multiple times 240 | * if communication with multiple station IDs is used. 241 | * The connect verifies the password against the app of remote station. 242 | */ 243 | RSCode Connect(int AppID, devid_t stationID = DEV_ID_ANY); 244 | 245 | /* 246 | * Inform the station that an app is discontinuing 247 | */ 248 | RSCode Disconnect(int AppID, devid_t stationID = ~0); 249 | 250 | /* 251 | * Prepare sending data to a remote application 252 | * The request is queued for sending. 253 | * valid flags see enum MsgFlags 254 | * txPower allows to overwrite the txPower in dBm. 255 | * The data is busy until the AppRecvHandler is called 256 | */ 257 | 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); 258 | /* 259 | * Removes a message from the queue 260 | */ 261 | RSCode KillMsg(int AppID, int msgID); 262 | 263 | /* 264 | * Sets a new profile for a given radio with a given profile 265 | * This can be called anytime after the RadioShuttle startup. 266 | */ 267 | RSCode UpdateRadioProfile(Radio *radio, RadioType radioType, const struct RadioProfile *profile); 268 | 269 | /* 270 | * Sets the size value to the largest messages available 271 | * for all configured radios 272 | * The flags are important because encrypted messages need more space 273 | */ 274 | RSCode MaxMessageSize(int *size, int msgFlags = 0); 275 | 276 | /* 277 | * Get statistics of all messages and errors 278 | * A pointer reference containing the RadioStats must be provided. 279 | * The statistics contain the information of the first radio, unless 280 | * the RadioEntry parameter is provided for a specified radio. 281 | */ 282 | RSCode GetStatistics(struct RadioStats **stats, Radio *radio = NULL); 283 | 284 | 285 | /* 286 | * Dump all received and sent packets 287 | */ 288 | void EnablePacketTrace(devid_t stationID = DEV_ID_ANY, bool sents = false, bool recvs = false, Radio *radio = 0); 289 | 290 | /* 291 | * The RadioShuttle is idle when there are no ongoing jobs, etc. 292 | */ 293 | bool Idle(bool forceBusyDuringTransmits = false); 294 | 295 | /* 296 | * Converts a RadioShuttle error code into a string. 297 | */ 298 | const char *StrError(RSErrorCode err); 299 | 300 | /* 301 | * Converts the radio type into a clear text name 302 | */ 303 | const char *GetRadioName(RadioType radioType); 304 | 305 | /* 306 | * Starts the main RadioShuttle loop, returns 0 when nothing needs to be done 307 | * Retuns > 0 when it should be called again 308 | * RunShuttle is called on user level (non-interrupt level) 309 | */ 310 | int RunShuttle(void); 311 | 312 | private: 313 | enum PacketStatus { 314 | PS_Queued, 315 | PS_Sent, 316 | PS_GotSendSlot, 317 | PS_WaitForConfirm, 318 | PS_SendRequestCompleted, 319 | PS_SendRequestConfirmed, 320 | PS_SendTimeout, 321 | }; 322 | 323 | struct RadioEntry; // forward decl. 324 | struct ReceivedMsgEntry { 325 | void *RxData; 326 | int RxSize; 327 | int rssi; 328 | int snr; 329 | struct RadioEntry *re; 330 | }; 331 | 332 | 333 | struct RadioEntry { 334 | Radio *radio; 335 | RadioEvents_t radioEvents; 336 | const RadioProfile *profile; 337 | RadioModems_t modem; 338 | volatile signed char _CADdetected; 339 | uint16_t lastTxSize; 340 | int lastTxPower; 341 | int timeOnAir12Bytes; 342 | struct ReceivedMsgEntry rxMsg; 343 | struct RadioStats rStats; 344 | int maxTimeOnAir; 345 | int retry_ms; 346 | int customRetryDelay_ms; 347 | uint32_t lastTxDone; 348 | volatile bool txDoneReceived; 349 | volatile const char *intrDelayedMsg; 350 | uint32_t random; 351 | uint32_t random2; 352 | }; 353 | 354 | struct AppEntry { 355 | int AppID; 356 | AppRecvHandler handler; 357 | int msgID; 358 | void *password; 359 | uint8_t pwLen; 360 | bool pwdConnected; 361 | }; 362 | 363 | struct ConnectEntry { 364 | devid_t stationID; 365 | int AppID; 366 | bool authorized; 367 | uint32_t random[2]; 368 | }; 369 | 370 | struct SendMsgEntry { 371 | int AppID; 372 | void *data; 373 | int len; 374 | int flags; 375 | devid_t stationID; 376 | int txPower; 377 | int msgID; 378 | int retryCount; 379 | bool releaseData; 380 | AppEntry *aep; 381 | ConnectEntry *cep; 382 | PacketStatus pStatus; 383 | int respWindow; 384 | uint32_t responseTime; 385 | uint32_t lastSentTime; 386 | uint32_t lastTimeOnAir; 387 | uint32_t confirmTimeout; 388 | int retry_ms; 389 | uint8_t channel; 390 | uint8_t factor; 391 | uint32_t securityData[8]; 392 | uint32_t tmpRandom[2]; 393 | }; 394 | 395 | struct SignalStrengthEntry { 396 | int rx_dBm; 397 | devid_t stationID; 398 | time_t lastUpdate; 399 | int rcnCnt; 400 | }; 401 | 402 | struct TimeOnAirSlotEntry { 403 | devid_t stationID; 404 | int AppID; 405 | uint8_t channel; 406 | uint8_t factor; 407 | uint32_t busy_time; 408 | int busy_ms; 409 | }; 410 | 411 | struct EncryptionHeader { 412 | uint32_t version : 3; // 3-bit encryption version 413 | uint32_t dataSum : 13; // Checksum of all packet data 414 | uint16_t msgSize : 11; // Same as in RadioHeader 415 | uint16_t msgID : 5; // Same as in RadioHeader 416 | uint32_t random; 417 | }; 418 | 419 | enum RadioHeaderVersions { 420 | RSMagic = 0b1011, 421 | RSHeaderFully_v1 = 0b001, 422 | RSHeaderPacked_v1 = 0b010, 423 | RSHeaderFullySize_v1 = 16, 424 | RSHeaderPackedSize_v1 = 12, 425 | msgIDv1Mask = 0b11111, 426 | Packedv1MaxApps = (1<<11)-1, 427 | Packedv1MaxRespWindow = (1<<11)-1, 428 | Fullyv1MaxRespWindow = (1<<16)-1, 429 | Packedv1MaxDeviceID = (1<<21)-1, 430 | MaxWinScale = (1<<4)-1, 431 | DataSumBits = 13, 432 | }; 433 | 434 | /* 435 | * The RadioShuttle communication header (similar to UDP), 436 | * but optimized for radios. 437 | * A packet crc checksum is done on the link layer, no field needed here 438 | * The source always sends this request to the other side 439 | * - Check immediately for a response after sending 440 | * - Check again for a response in responseWindow*2 milliseconds 441 | * 442 | */ 443 | struct RadioHeader { 444 | // 4 bytes 445 | uint16_t magic : 4; // 4-bit magic, 446 | uint16_t version : 3; // 3-bit version 447 | uint16_t msgFlags: 9; // e.g.: Request/Response NeedsConfirm, Priority, Encrypted 448 | union { 449 | struct { 450 | uint16_t msgSize : 11; // 11-bit message size allows a maximum of 2048 bytes 451 | uint16_t msgID : 5; // ID for the app communication, truncated to 5-bit 452 | } data; 453 | struct { 454 | uint16_t channel : 4; // Specify the channel to switch to for this transaction 455 | uint16_t factor : 3; // Specifies the spreading factor index (6-12) 456 | uint16_t winScale: 4; // Set the window scaling factor 457 | } option; 458 | } s; 459 | union { 460 | struct { // 8 bytes 461 | uint32_t appID : 11; // First 2048 application identifiers for the request 462 | devid_t destination : 21; // Device ID of the destination 2^11 first 2 million devices 463 | uint32_t respWindow : 11; // Wait for the respWindow (ms) max 2048 ms before sending data. 464 | devid_t source : 21; // Device ID of the destination 2^11 first 2 million devices 465 | } packedv1; 466 | struct { // 12 bytes 467 | uint16_t appID; // Application identifier for the request 468 | uint16_t respWindow; // We listen for a 2nd resonse in responseWindow (ms) 469 | devid_t destination; // Device ID of the destination 470 | devid_t source; // DeviceID of the source 471 | } fullyv1; 472 | } u; 473 | }; 474 | 475 | struct WireDumpSettings { 476 | devid_t stationID; 477 | bool sents; 478 | bool recvs; 479 | Radio *radio; 480 | }; 481 | 482 | /* 483 | * Radio specific callbacks are public to allow C wrapper function to call us. 484 | */ 485 | public: 486 | void RS_TxDone(Radio *radio, void *userData); 487 | void RS_RxDone(Radio *radio, void *userData, uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr); 488 | void RS_TxTimeout(Radio *radio, void *userData); 489 | void RS_RxTimeout(Radio *radio, void *userData); 490 | void RS_RxError(Radio *radio, void *userData); 491 | void RS_CadDone(Radio *radio, void *userData, bool channelActivityDetected); 492 | 493 | private: 494 | /* 495 | * The CadDetection will turn the radio into a Cad listening mode 496 | * to detect radio-modulated signals on the channel. 497 | * This takes a few ms (at SF7) and the interrupt RS_CadDone will report 498 | * the status. 499 | * The radio->_CADdetected contains the result (0 for idle, 1 for busy) 500 | */ 501 | bool CadDetection(RadioEntry *re); 502 | 503 | /* 504 | * init the RX/TX of the Radio. 505 | */ 506 | RSCode _initRadio(RadioEntry *re); 507 | 508 | /* 509 | * The processing function returns a status code if it has been able to process 510 | * this message. true' for messages processed, false for messages skipped. 511 | */ 512 | 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); 513 | 514 | 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); 515 | 516 | void MessageSecurityError(ReceivedMsgEntry *rme, AppEntry *aep, int msgID, devid_t source, uint8_t channel, uint8_t factor); 517 | 518 | void SaveTimeOnAirSlot(devid_t destination, int AppID, int msgFlags, int respWindow, uint8_t channel, uint8_t factor, int timeOnAir); 519 | 520 | /* 521 | * Our main send function is responsible for header packing, 522 | * compression and encryption, and finally sends a packet via the radio. 523 | * It returns true if we have been able to sent the message. 524 | */ 525 | 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); 526 | 527 | /* 528 | * We keep a little cache list of the power needed for different stations 529 | * This saves a lot of energy and reduces signal strength to keep the network 530 | * less busy. For example, other radio networks need not receive our signals. 531 | */ 532 | int CalculateTXPower(RadioEntry *re, devid_t stationID); 533 | bool UpdateSignalStrength(devid_t stationID, int dBm); 534 | bool DeleteSignalStrength(devid_t stationID); 535 | 536 | 537 | /* 538 | * Our main receive function is responsible for header unpacking, 539 | * de-compression and decryption, and finally provides the unpacked data. 540 | * It returns true if we have been able to detect and unpack the message. 541 | */ 542 | 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); 543 | 544 | /* 545 | * We need to process all input messages, the _recv list should be empty ASAP 546 | * because the data is only temporarily available until the next packet. 547 | */ 548 | void ProcessReceivedMessages(void); 549 | 550 | 551 | void PacketTrace(RadioEntry *re, const char *name, RadioHeader *rh, void *data, int len, bool sent, ReceivedMsgEntry *rme); 552 | 553 | /* 554 | * A dummy function which is called for every timeout, the timer wakes up 555 | * the sleep and therefore the RunShuttle starts processing. 556 | */ 557 | void TimeoutFunc(void); 558 | 559 | uint32_t GetDataSum(int maxbits, void *data, int len); 560 | 561 | 562 | private: 563 | const char *_name; 564 | devid_t _deviceID; 565 | RadioType _radioType; 566 | int _maxMTUSize; 567 | list _radios; 568 | map _apps; 569 | map, ConnectEntry> _connections; 570 | list _sends; 571 | list _recvs; 572 | map _signals; 573 | list _airtimes; 574 | MyTimeout *timer; 575 | MyTimer *ticker; 576 | volatile uint32_t prevWakeup; 577 | int SetTimerCount; 578 | 579 | static const RadioProfile defaultProfile[]; 580 | volatile bool busyInShuttle; 581 | WireDumpSettings _wireDumpSettings; 582 | const static int MAX_SENT_RETRIES = 3; // Defines the number of retries of sents (with confirm) 583 | const static int RX_TIMEOUT_30MIN = 30*60*1000; // Mbed OS timers do not allow more 2^31-1 us 584 | RadioStatusInterface *_statusIntf; 585 | RadioSecurityInterface *_securityIntf; 586 | AppStartupHandler _startupHandler; 587 | AppStartupHandler _receiveHandler; 588 | }; 589 | 590 | #endif // FEATURE_LORA 591 | #endif // __RADIOSHUTTLE_H__ 592 | -------------------------------------------------------------------------------- /src/RadioStatus.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The file is licensed under the Apache License, Version 2.0 3 | * (c) 2019 Helmut Tschemernjak 4 | * 30826 Garbsen (Hannover) Germany 5 | */ 6 | 7 | 8 | #ifdef ARDUINO 9 | #include 10 | #include "arduino-mbed.h" 11 | #include 12 | #define FEATURE_LORA 1 13 | #endif 14 | #ifdef __MBED__ 15 | #include "mbed.h" 16 | #include "xPinMap.h" 17 | #endif 18 | #include "RadioStatusInterface.h" 19 | #include "RadioStatus.h" 20 | #ifdef FEATURE_LORA 21 | 22 | 23 | MyRadioStatus::MyRadioStatus() 24 | { 25 | _totalTX = 0; 26 | _totalRX = 0; 27 | _totalError = 0; 28 | _totalTimeout = 0; 29 | 30 | ledTX = NULL; 31 | ledRX = NULL; 32 | ledTimeout = NULL; 33 | inverted = false; 34 | 35 | #ifdef TARGET_STM32L0 36 | ledTX = new DigitalOut(LED3); // blue 37 | *ledTX = 0; 38 | ledRX = new DigitalOut(LED4); // red 39 | *ledRX = 0; 40 | ledTimeout = new DigitalOut(LED1); // green 41 | *ledTimeout = 0; 42 | #endif 43 | #ifdef HELTECL432_REV1 44 | ledTX = new DigitalOut(STATUS_LED); // green 45 | *ledTX = 0; 46 | ledRX = new DigitalOut(LED2); // red 47 | *ledRX = 0; 48 | #endif 49 | #ifdef MyHOME_BOARD_REV4 50 | ledTX = new DigitalOut(LED2); // yellow 51 | *ledTX = 0; 52 | ledRX = new DigitalOut(STATUS_LED); // red 53 | *ledRX = 0; 54 | ledTimeout = new DigitalOut(LED3); // green 55 | *ledTimeout = 0; 56 | #endif 57 | #ifdef __SAMD21G18A__ 58 | inverted = true; 59 | #ifdef PIN_LED_TXL 60 | ledTX = new DigitalOut(PIN_LED_TXL); // yellow 61 | *ledTX = 1; 62 | #endif 63 | #ifdef PIN_LED_RXL 64 | ledRX = new DigitalOut(25); // red 65 | *ledRX = 1; 66 | #endif 67 | #endif 68 | #ifdef ARDUINO_ESP32_DEV // ESP32_ECO_POWER_REV_1 69 | ledTX = new DigitalOut(2); // green 70 | *ledTX = 0; 71 | ledRX = new DigitalOut(12); // red 72 | *ledRX = 0; 73 | #endif 74 | #if defined(ARDUINO_Heltec_WIFI_LoRa_32) || defined(ARDUINO_WIFI_LORA_32) \ 75 | || defined(ARDUINO_WIFI_LORA_32_V2) || defined(ARDUINO_WIRELESS_STICK) \ 76 | || defined(ARDUINO_WIRELESS_STICK_LITE) // the Heltec boards 77 | ledTX = new DigitalOut(25); // white 78 | *ledTX = 0; 79 | #endif 80 | 81 | #ifdef HAS_OLED_DISPLAY 82 | invertedDisplay = false; 83 | _line1[0] = 0; 84 | _line2[0] = 0; 85 | _line3[0] = 0; 86 | _line4[0] = 0; 87 | _line5[0] = 0; 88 | displayReset = NULL; 89 | #if defined(ARDUINO_Heltec_WIFI_LoRa_32) || defined(ARDUINO_WIFI_LORA_32) \ 90 | || defined(ARDUINO_WIFI_LORA_32_V2) || defined(ARDUINO_WIRELESS_STICK) 91 | #define DISPLAY_ADDRESS 0x3c 92 | #define DISPLAY_SDA 4 93 | #define DISPLAY_SCL 15 94 | #define DISPLAY_RESET 16 95 | displayReset = new DigitalOut(DISPLAY_RESET); 96 | #endif 97 | #ifdef ARDUINO_ESP32_DEV // ESP32_ECO_POWER_REV_1 98 | #define DISPLAY_ADDRESS 0x3c 99 | #define DISPLAY_SDA 21 100 | #define DISPLAY_SCL 22 101 | #endif 102 | if (displayReset) { 103 | *displayReset = 0; 104 | wait_us(50 * 1000); 105 | *displayReset = 1; 106 | } 107 | display = new SSD1306(DISPLAY_ADDRESS, DISPLAY_SDA, DISPLAY_SCL); 108 | display->init(); 109 | // display->flipScreenVertically(); 110 | display->setFont(ArialMT_Plain_16); // ArialMT_Plain_10); 111 | display->clear(); 112 | display->drawString(0, 0, "RadioShuttle 1.4"); 113 | display->setFont(ArialMT_Plain_10); 114 | int yoff = 17; 115 | display->drawString(0, yoff, "Peer-to-Peer LoRa Protcol"); 116 | yoff += 12; 117 | display->drawString(0, yoff, "Efficient, Fast, Secure"); 118 | yoff += 12; 119 | display->drawString(0, yoff, "www.radioshuttle.de"); 120 | display->display(); 121 | #endif 122 | } 123 | 124 | MyRadioStatus::~MyRadioStatus() 125 | { 126 | if (ledTX) { 127 | if (inverted) 128 | *ledTX = 1; 129 | else 130 | *ledTX = 0; 131 | delete ledTX; 132 | } 133 | if (ledRX) { 134 | if (inverted) 135 | *ledRX = 1; 136 | else 137 | *ledRX = 0; 138 | delete ledRX; 139 | } 140 | if (ledTimeout) { 141 | if (inverted) 142 | *ledTimeout = 1; 143 | else 144 | *ledTimeout = 0; 145 | delete ledTimeout; 146 | } 147 | } 148 | 149 | void 150 | MyRadioStatus::TXStart(int AppID, int toStation, int length, int dBm) 151 | { 152 | UNUSED(AppID); 153 | UNUSED(toStation); 154 | UNUSED(length); 155 | UNUSED(dBm); 156 | if (ledTX) { 157 | if (inverted) 158 | *ledTX = 0; 159 | else 160 | *ledTX = 1; 161 | } 162 | #ifdef HAS_OLED_DISPLAY 163 | snprintf(_line2, sizeof(_line2), "TX(%d) ID(%d) %d dBm", length, toStation, dBm); 164 | UpdateDisplay(true); 165 | #endif 166 | _totalTX++; 167 | } 168 | 169 | void 170 | MyRadioStatus::TXComplete(void) 171 | { 172 | if (ledTX) { 173 | if (inverted) 174 | *ledTX = 1; 175 | else 176 | *ledTX = 0; 177 | } 178 | #ifdef HAS_OLED_DISPLAY 179 | UpdateDisplay(false); 180 | #endif 181 | } 182 | 183 | void 184 | MyRadioStatus::RxDone(int size, int rssi, int snr) 185 | { 186 | UNUSED(size); 187 | UNUSED(rssi); 188 | UNUSED(snr); 189 | if (ledRX) { 190 | if (inverted) 191 | *ledRX = 0; 192 | else 193 | *ledRX = 1; 194 | } 195 | _totalRX++; 196 | #ifdef HAS_OLED_DISPLAY 197 | snprintf(_line3, sizeof(_line3), "RX(%d) RSSI(%d) SNR(%d)", size, rssi, snr); 198 | UpdateDisplay(true); 199 | #endif 200 | } 201 | 202 | void 203 | MyRadioStatus::RxCompleted(void) 204 | { 205 | if (ledRX) { 206 | if (inverted) 207 | *ledRX = 1; 208 | else 209 | *ledRX = 0; 210 | } 211 | #ifdef HAS_OLED_DISPLAY 212 | UpdateDisplay(false); 213 | #endif 214 | } 215 | 216 | void 217 | MyRadioStatus::MessageTimeout(int App, int toStation) 218 | { 219 | UNUSED(App); 220 | UNUSED(toStation); 221 | if (ledTimeout) 222 | *ledTimeout = 1; 223 | _totalTimeout++; 224 | #ifdef HAS_OLED_DISPLAY 225 | UpdateDisplay(false); 226 | #endif 227 | } 228 | 229 | 230 | void 231 | MyRadioStatus::UpdateDisplay(bool invertDisplay) 232 | { 233 | UNUSED(invertDisplay); 234 | #ifdef HAS_OLED_DISPLAY 235 | int yoff = 0; 236 | int hight = 12; 237 | time_t t = time(NULL); 238 | struct tm mytm; 239 | localtime_r(&t, &mytm); 240 | 241 | snprintf(_line1, sizeof(_line1), "%s (%d) %02d:%02d:%02d", _radioType, _stationID, 242 | mytm.tm_hour, mytm.tm_min, mytm.tm_sec); 243 | snprintf(_line4, sizeof(_line4), "Packets RX(%d) TX(%d)", _totalRX, _totalTX); 244 | snprintf(_line5, sizeof(_line5), "RXErr(%d) TOut(%d) %.2f %d", _totalError, _totalTimeout, (double)_frequency/1000000.0, _spreadingFactor); 245 | 246 | if (invertDisplay) 247 | display->invertDisplay(); 248 | else 249 | display->normalDisplay(); 250 | 251 | display->setFont(ArialMT_Plain_10); 252 | display->clear(); 253 | 254 | display->drawString(0, yoff, String(_line1)); 255 | yoff += hight; 256 | display->drawString(0, yoff, String(_line2)); 257 | yoff += hight; 258 | display->drawString(0, yoff, String(_line3)); 259 | yoff += hight; 260 | display->drawString(0, yoff, String(_line4)); 261 | yoff += hight; 262 | 263 | display->display(); 264 | #endif 265 | } 266 | 267 | #endif // FEATURE_LORA 268 | -------------------------------------------------------------------------------- /src/RadioStatus.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The file is licensed under the Apache License, Version 2.0 3 | * (c) 2019 Helmut Tschemernjak 4 | * 30826 Garbsen (Hannover) Germany 5 | */ 6 | 7 | #ifndef __RADIOSTATUS_H__ 8 | #define __RADIOSTATUS_H__ 9 | 10 | #if defined(ARDUINO_Heltec_WIFI_LoRa_32) || defined(ARDUINO_WIFI_LORA_32) \ 11 | || defined(ARDUINO_WIFI_LORA_32_V2) || defined(ARDUINO_WIRELESS_STICK) \ 12 | || defined(ARDUINO_ESP32_DEV) // the Heltec and ECO boards 13 | #define HAS_OLED_DISPLAY 14 | #include 15 | #include "SSD1306.h" 16 | #endif 17 | 18 | #ifdef ARDUINO_ESP32_DEV 19 | #undef HAS_OLED_DISPLAY // remove this line to enable the board 20 | #endif 21 | 22 | #ifdef FEATURE_SSD1306 23 | #include "SSD1306I2C.h" 24 | #define HAS_OLED_DISPLAY 25 | #define SSD1306 SSD1306I2C 26 | #endif 27 | 28 | #ifndef UNUSED 29 | #define UNUSED(x) (void)(x) 30 | #endif 31 | 32 | 33 | class MyRadioStatus : public RadioStatusInterface { 34 | public: 35 | MyRadioStatus(); 36 | virtual ~MyRadioStatus(); 37 | 38 | virtual void TXStart(int AppID, int toStation, int length, int dBm); 39 | virtual void TXComplete(void); 40 | virtual void RxDone(int size, int rssi, int snr); 41 | virtual void RxCompleted(void); 42 | virtual void MessageTimeout(int AppID, int toStation); 43 | 44 | void UpdateDisplay(bool invert); 45 | private: 46 | DigitalOut *ledTX; 47 | DigitalOut *ledRX; 48 | DigitalOut *ledTimeout; 49 | int _totalTX; 50 | int _totalRX; 51 | int _totalError; 52 | int _totalTimeout; 53 | bool inverted; 54 | 55 | #ifdef HAS_OLED_DISPLAY 56 | SSD1306 *display; 57 | DigitalOut *displayReset; 58 | char _line1[64]; 59 | char _line2[64]; 60 | char _line3[64]; 61 | char _line4[64]; 62 | char _line5[64]; 63 | bool invertedDisplay; 64 | #endif 65 | }; 66 | 67 | #endif // __RADIOSTATUS_H__ 68 | -------------------------------------------------------------------------------- /src/RadioStatusInterface.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The file is licensed under the Apache License, Version 2.0 3 | * (c) 2019 Helmut Tschemernjak 4 | * 30826 Garbsen (Hannover) Germany 5 | */ 6 | 7 | #ifndef __RADIOSTATUSINTERFACE_H__ 8 | #define __RADIOSTATUSINTERFACE_H__ 9 | 10 | class RadioStatusInterface { 11 | public: 12 | virtual ~RadioStatusInterface() { } 13 | /* 14 | * Signaling that a radio message send has been initiated 15 | */ 16 | virtual void TXStart(int AppID, int toStation, int length, int dBm) = 0; 17 | /* 18 | * Signaling that a radio message send has been completed 19 | */ 20 | virtual void TXComplete(void) = 0; 21 | /* 22 | * Signaling that a radio message input has been received 23 | * and queued for later processing 24 | */ 25 | virtual void RxDone(int size, int rssi, int snr) = 0; 26 | /* 27 | * Signaling that a radio message protocol processing has been completed 28 | */ 29 | virtual void RxCompleted(void) = 0; 30 | /* 31 | * Signaling that a higher-level message received a timeout 32 | * after the specified retry period 33 | */ 34 | virtual void MessageTimeout(int AppID, int toStation) = 0; 35 | 36 | void SetStationID(int stationID) { _stationID = stationID; }; 37 | 38 | void SetRadioType(const char *radioType) { _radioType = radioType; }; 39 | 40 | void SetRadioParams(int frequency, int spreadingFactor) { 41 | _frequency = frequency; _spreadingFactor = spreadingFactor; }; 42 | 43 | int _frequency; /* automaticlally set on RadioShuttle::Startup */ 44 | int _spreadingFactor; /* automaticlally set on RadioShuttle::Startup */ 45 | int _stationID; /* automaticlally set */ 46 | const char *_radioType; /* automaticlally set */ 47 | }; 48 | 49 | #endif // __RADIOSTATUSINTERFACE_H__ 50 | -------------------------------------------------------------------------------- /util/rs_aes.c: -------------------------------------------------------------------------------- 1 | /* 2 | Downloaded from here: https://github.com/kokke/tiny-AES128-C 3 | This is an implementation of the AES128 algorithm, specifically ECB and CBC mode. 4 | 5 | The implementation is verified against the test vectors in: 6 | National Institute of Standards and Technology Special Publication 800-38A 2001 ED 7 | 8 | ECB-AES128 9 | ---------- 10 | 11 | plain-text: 12 | 6bc1bee22e409f96e93d7e117393172a 13 | ae2d8a571e03ac9c9eb76fac45af8e51 14 | 30c81c46a35ce411e5fbc1191a0a52ef 15 | f69f2445df4f9b17ad2b417be66c3710 16 | 17 | key: 18 | 2b7e151628aed2a6abf7158809cf4f3c 19 | 20 | resulting cipher 21 | 3ad77bb40d7a3660a89ecaf32466ef97 22 | f5d3d58503b9699de785895a96fdbaaf 23 | 43b1cd7f598ece23881b00e3ed030688 24 | 7b0c785e27e8ad3f8223207104725dd4 25 | 26 | 27 | NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0) 28 | You should pad the end of the string with zeros if this is not the case. 29 | 30 | */ 31 | 32 | 33 | /*****************************************************************************/ 34 | /* Includes: */ 35 | /*****************************************************************************/ 36 | #include 37 | #include // CBC mode, for memset 38 | #include "rs_aes.h" 39 | 40 | /*****************************************************************************/ 41 | /* Defines: */ 42 | /*****************************************************************************/ 43 | // The number of columns comprising a state in AES. This is a constant in AES. Value=4 44 | #define Nb 4 45 | // The number of 32 bit words in a key. 46 | #define Nk 4 47 | 48 | // jcallan@github points out that declaring Multiply as a function 49 | // reduces code size considerably with the Keil ARM compiler. 50 | // See this link for more information: https://github.com/kokke/tiny-AES128-C/pull/3 51 | #ifndef MULTIPLY_AS_A_FUNCTION 52 | #define MULTIPLY_AS_A_FUNCTION 1 53 | #endif 54 | 55 | /* 56 | * simple re-defines do use the existing code with a context 57 | */ 58 | #define state ctx->state 59 | #define RoundKey ctx->RoundKey 60 | #define Iv ctx->InitialVector 61 | #define KEYLEN AES128_KEYLEN 62 | 63 | // The lookup-tables are marked const so they can be placed in read-only storage instead of RAM 64 | // The numbers below can be computed dynamically trading ROM for RAM - 65 | // This can be useful in (embedded) bootloader applications, where ROM is often limited. 66 | static const uint8_t sbox[256] = { 67 | //0 1 2 3 4 5 6 7 8 9 A B C D E F 68 | 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 69 | 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 70 | 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 71 | 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 72 | 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 73 | 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 74 | 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 75 | 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 76 | 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 77 | 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 78 | 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 79 | 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 80 | 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 81 | 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 82 | 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 83 | 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; 84 | 85 | static const uint8_t rsbox[256] = 86 | { 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 87 | 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 88 | 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 89 | 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 90 | 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 91 | 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 92 | 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 93 | 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 94 | 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 95 | 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 96 | 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 97 | 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 98 | 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 99 | 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 100 | 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 101 | 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }; 102 | 103 | 104 | // The round constant word array, Rcon[i], contains the values given by 105 | // x to th e power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) 106 | // Note that i starts at 1, not 0). 107 | static const uint8_t Rcon[10] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 }; 108 | 109 | 110 | /*****************************************************************************/ 111 | /* Private functions: */ 112 | /*****************************************************************************/ 113 | static uint8_t getSBoxValue(uint8_t num) 114 | { 115 | return sbox[num]; 116 | } 117 | 118 | static uint8_t getSBoxInvert(uint8_t num) 119 | { 120 | return rsbox[num]; 121 | } 122 | 123 | // This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. 124 | static void KeyExpansion(AES_CTX *ctx, const uint8_t* Key) 125 | { 126 | uint32_t i, j, k; 127 | uint8_t tempa[4]; // Used for the column/row operations 128 | 129 | // The first round key is the key itself. 130 | for(i = 0; i < Nk; ++i) 131 | { 132 | RoundKey[(i * 4) + 0] = Key[(i * 4) + 0]; 133 | RoundKey[(i * 4) + 1] = Key[(i * 4) + 1]; 134 | RoundKey[(i * 4) + 2] = Key[(i * 4) + 2]; 135 | RoundKey[(i * 4) + 3] = Key[(i * 4) + 3]; 136 | } 137 | 138 | // All other round keys are found from the previous round keys. 139 | for(; (i < (Nb * (Nr + 1))); ++i) 140 | { 141 | for(j = 0; j < 4; ++j) 142 | { 143 | tempa[j]=RoundKey[(i-1) * 4 + j]; 144 | } 145 | if (i % Nk == 0) 146 | { 147 | // This function rotates the 4 bytes in a word to the left once. 148 | // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] 149 | 150 | // Function RotWord() 151 | { 152 | k = tempa[0]; 153 | tempa[0] = tempa[1]; 154 | tempa[1] = tempa[2]; 155 | tempa[2] = tempa[3]; 156 | tempa[3] = k; 157 | } 158 | 159 | // SubWord() is a function that takes a four-byte input word and 160 | // applies the S-box to each of the four bytes to produce an output word. 161 | 162 | // Function Subword() 163 | { 164 | tempa[0] = getSBoxValue(tempa[0]); 165 | tempa[1] = getSBoxValue(tempa[1]); 166 | tempa[2] = getSBoxValue(tempa[2]); 167 | tempa[3] = getSBoxValue(tempa[3]); 168 | } 169 | 170 | tempa[0] = tempa[0] ^ Rcon[(i/Nk)-1]; 171 | } 172 | else if (Nk > 6 && i % Nk == 4) 173 | { 174 | // Function Subword() 175 | { 176 | tempa[0] = getSBoxValue(tempa[0]); 177 | tempa[1] = getSBoxValue(tempa[1]); 178 | tempa[2] = getSBoxValue(tempa[2]); 179 | tempa[3] = getSBoxValue(tempa[3]); 180 | } 181 | } 182 | RoundKey[i * 4 + 0] = RoundKey[(i - Nk) * 4 + 0] ^ tempa[0]; 183 | RoundKey[i * 4 + 1] = RoundKey[(i - Nk) * 4 + 1] ^ tempa[1]; 184 | RoundKey[i * 4 + 2] = RoundKey[(i - Nk) * 4 + 2] ^ tempa[2]; 185 | RoundKey[i * 4 + 3] = RoundKey[(i - Nk) * 4 + 3] ^ tempa[3]; 186 | } 187 | } 188 | 189 | // This function adds the round key to state. 190 | // The round key is added to the state by an XOR function. 191 | static void AddRoundKey(AES_CTX *ctx, uint8_t round) 192 | { 193 | uint8_t i,j; 194 | for(i=0;i<4;++i) 195 | { 196 | for(j = 0; j < 4; ++j) 197 | { 198 | (*state)[i][j] ^= RoundKey[round * Nb * 4 + i * Nb + j]; 199 | } 200 | } 201 | } 202 | 203 | // The SubBytes Function Substitutes the values in the 204 | // state matrix with values in an S-box. 205 | static void SubBytes(AES_CTX *ctx) 206 | { 207 | uint8_t i, j; 208 | for(i = 0; i < 4; ++i) 209 | { 210 | for(j = 0; j < 4; ++j) 211 | { 212 | (*state)[j][i] = getSBoxValue((*state)[j][i]); 213 | } 214 | } 215 | } 216 | 217 | // The ShiftRows() function shifts the rows in the state to the left. 218 | // Each row is shifted with different offset. 219 | // Offset = Row number. So the first row is not shifted. 220 | static void ShiftRows(AES_CTX *ctx) 221 | { 222 | uint8_t temp; 223 | 224 | // Rotate first row 1 columns to left 225 | temp = (*state)[0][1]; 226 | (*state)[0][1] = (*state)[1][1]; 227 | (*state)[1][1] = (*state)[2][1]; 228 | (*state)[2][1] = (*state)[3][1]; 229 | (*state)[3][1] = temp; 230 | 231 | // Rotate second row 2 columns to left 232 | temp = (*state)[0][2]; 233 | (*state)[0][2] = (*state)[2][2]; 234 | (*state)[2][2] = temp; 235 | 236 | temp = (*state)[1][2]; 237 | (*state)[1][2] = (*state)[3][2]; 238 | (*state)[3][2] = temp; 239 | 240 | // Rotate third row 3 columns to left 241 | temp = (*state)[0][3]; 242 | (*state)[0][3] = (*state)[3][3]; 243 | (*state)[3][3] = (*state)[2][3]; 244 | (*state)[2][3] = (*state)[1][3]; 245 | (*state)[1][3] = temp; 246 | } 247 | 248 | static uint8_t xtime(uint8_t x) 249 | { 250 | return ((x<<1) ^ (((x>>7) & 1) * 0x1b)); 251 | } 252 | 253 | // MixColumns function mixes the columns of the state matrix 254 | static void MixColumns(AES_CTX *ctx) 255 | { 256 | uint8_t i; 257 | uint8_t Tmp,Tm,t; 258 | for(i = 0; i < 4; ++i) 259 | { 260 | t = (*state)[i][0]; 261 | Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ; 262 | Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ; 263 | Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ; 264 | Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ; 265 | Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ; 266 | } 267 | } 268 | 269 | // Multiply is used to multiply numbers in the field GF(2^8) 270 | #if MULTIPLY_AS_A_FUNCTION 271 | static uint8_t Multiply(uint8_t x, uint8_t y) 272 | { 273 | return (((y & 1) * x) ^ 274 | ((y>>1 & 1) * xtime(x)) ^ 275 | ((y>>2 & 1) * xtime(xtime(x))) ^ 276 | ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ 277 | ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); 278 | } 279 | #else 280 | #define Multiply(x, y) \ 281 | ( ((y & 1) * x) ^ \ 282 | ((y>>1 & 1) * xtime(x)) ^ \ 283 | ((y>>2 & 1) * xtime(xtime(x))) ^ \ 284 | ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \ 285 | ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \ 286 | 287 | #endif 288 | 289 | // MixColumns function mixes the columns of the state matrix. 290 | // The method used to multiply may be difficult to understand for the inexperienced. 291 | // Please use the references to gain more information. 292 | static void InvMixColumns(AES_CTX *ctx) 293 | { 294 | int i; 295 | uint8_t a,b,c,d; 296 | for(i=0;i<4;++i) 297 | { 298 | a = (*state)[i][0]; 299 | b = (*state)[i][1]; 300 | c = (*state)[i][2]; 301 | d = (*state)[i][3]; 302 | 303 | (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09); 304 | (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d); 305 | (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b); 306 | (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e); 307 | } 308 | } 309 | 310 | 311 | // The SubBytes Function Substitutes the values in the 312 | // state matrix with values in an S-box. 313 | static void InvSubBytes(AES_CTX *ctx) 314 | { 315 | uint8_t i,j; 316 | for(i=0;i<4;++i) 317 | { 318 | for(j=0;j<4;++j) 319 | { 320 | (*state)[j][i] = getSBoxInvert((*state)[j][i]); 321 | } 322 | } 323 | } 324 | 325 | static void InvShiftRows(AES_CTX *ctx) 326 | { 327 | uint8_t temp; 328 | 329 | // Rotate first row 1 columns to right 330 | temp=(*state)[3][1]; 331 | (*state)[3][1]=(*state)[2][1]; 332 | (*state)[2][1]=(*state)[1][1]; 333 | (*state)[1][1]=(*state)[0][1]; 334 | (*state)[0][1]=temp; 335 | 336 | // Rotate second row 2 columns to right 337 | temp=(*state)[0][2]; 338 | (*state)[0][2]=(*state)[2][2]; 339 | (*state)[2][2]=temp; 340 | 341 | temp=(*state)[1][2]; 342 | (*state)[1][2]=(*state)[3][2]; 343 | (*state)[3][2]=temp; 344 | 345 | // Rotate third row 3 columns to right 346 | temp=(*state)[0][3]; 347 | (*state)[0][3]=(*state)[1][3]; 348 | (*state)[1][3]=(*state)[2][3]; 349 | (*state)[2][3]=(*state)[3][3]; 350 | (*state)[3][3]=temp; 351 | } 352 | 353 | 354 | // Cipher is the main function that encrypts the PlainText. 355 | static void Cipher(AES_CTX *ctx) 356 | { 357 | uint8_t round = 0; 358 | 359 | // Add the First round key to the state before starting the rounds. 360 | AddRoundKey(ctx, 0); 361 | 362 | // There will be Nr rounds. 363 | // The first Nr-1 rounds are identical. 364 | // These Nr-1 rounds are executed in the loop below. 365 | for(round = 1; round < Nr; ++round) 366 | { 367 | SubBytes(ctx); 368 | ShiftRows(ctx); 369 | MixColumns(ctx); 370 | AddRoundKey(ctx, round); 371 | } 372 | 373 | // The last round is given below. 374 | // The MixColumns function is not here in the last round. 375 | SubBytes(ctx); 376 | ShiftRows(ctx); 377 | AddRoundKey(ctx, Nr); 378 | } 379 | 380 | static void InvCipher(AES_CTX *ctx) 381 | { 382 | uint8_t round=0; 383 | 384 | // Add the First round key to the state before starting the rounds. 385 | AddRoundKey(ctx, Nr); 386 | 387 | // There will be Nr rounds. 388 | // The first Nr-1 rounds are identical. 389 | // These Nr-1 rounds are executed in the loop below. 390 | for(round=Nr-1;round>0;round--) 391 | { 392 | InvShiftRows(ctx); 393 | InvSubBytes(ctx); 394 | AddRoundKey(ctx, round); 395 | InvMixColumns(ctx); 396 | } 397 | 398 | // The last round is given below. 399 | // The MixColumns function is not here in the last round. 400 | InvShiftRows(ctx); 401 | InvSubBytes(ctx); 402 | AddRoundKey(ctx, 0); 403 | } 404 | 405 | 406 | /*****************************************************************************/ 407 | /* Public functions: */ 408 | /*****************************************************************************/ 409 | 410 | 411 | void AES128_InitContext(AES_CTX *ctx, const uint8_t* key, const uint8_t* initialVector) 412 | { 413 | // The KeyExpansion routine must be called before encryption. 414 | KeyExpansion(ctx, key); 415 | if (initialVector) 416 | memcpy(ctx->InitialVector, initialVector, KEYLEN); 417 | 418 | } 419 | #ifdef ECB_MODE 420 | 421 | 422 | void AES128_ECB_encrypt(AES_CTX *ctx, const uint8_t* input, uint8_t* output) 423 | { 424 | // Copy input to output, and work in-memory on output 425 | memcpy(output, input, KEYLEN); 426 | state = (state_t*)output; 427 | 428 | // The next function call encrypts the PlainText with the Key using AES algorithm. 429 | Cipher(ctx); 430 | } 431 | 432 | void AES128_ECB_decrypt(AES_CTX *ctx, const uint8_t* input, uint8_t *output) 433 | { 434 | // Copy input to output, and work in-memory on output 435 | memcpy(output, input, KEYLEN); 436 | state = (state_t*)output; 437 | 438 | InvCipher(ctx); 439 | } 440 | 441 | 442 | #endif // ECB_MODE 443 | 444 | 445 | 446 | 447 | #ifdef CBC_MODE 448 | 449 | 450 | static void XorWithIv(AES_CTX *ctx, uint8_t* buf) 451 | { 452 | if (sizeof(int) == 4) { 453 | uint32_t *data = (uint32_t *)buf; 454 | uint32_t *iVec = (uint32_t *)Iv; 455 | 456 | *data++ ^= *iVec++; 457 | *data++ ^= *iVec++; 458 | *data++ ^= *iVec++; 459 | *data++ ^= *iVec++; 460 | } else { 461 | uint8_t i; 462 | for(i = 0; i < KEYLEN; ++i) 463 | { 464 | buf[i] ^= Iv[i]; 465 | } 466 | } 467 | } 468 | 469 | void AES128_CBC_encrypt_buffer(AES_CTX *ctx, uint8_t* output, const uint8_t* input, uint32_t length) 470 | { 471 | uintptr_t i; 472 | uint8_t remainders = length % KEYLEN; /* Remaining bytes in the last non-full block */ 473 | 474 | 475 | for(i = 0; i < length; i += KEYLEN) 476 | { 477 | memcpy(output, input, KEYLEN); 478 | XorWithIv(ctx, output); 479 | state = (state_t*)output; 480 | Cipher(ctx); 481 | memcpy(ctx->InitialVector, output, KEYLEN); 482 | input += KEYLEN; 483 | output += KEYLEN; 484 | } 485 | 486 | if(remainders) 487 | { 488 | memcpy(output, input, KEYLEN); 489 | memset(output + remainders, 0, KEYLEN - remainders); /* add 0-padding */ 490 | state = (state_t*)output; 491 | Cipher(ctx); 492 | } 493 | } 494 | 495 | void AES128_CBC_decrypt_buffer(AES_CTX *ctx, uint8_t* output, const uint8_t* input, uint32_t length) 496 | { 497 | uintptr_t i; 498 | uint8_t remainders = length % KEYLEN; /* Remaining bytes in the last non-full block */ 499 | 500 | 501 | for(i = 0; i < length; i += KEYLEN) 502 | { 503 | memcpy(output, input, KEYLEN); 504 | state = (state_t*)output; 505 | InvCipher(ctx); 506 | XorWithIv(ctx, output); 507 | memcpy(ctx->InitialVector, input, KEYLEN); 508 | input += KEYLEN; 509 | output += KEYLEN; 510 | } 511 | 512 | if(remainders) 513 | { 514 | memcpy(output, input, KEYLEN); 515 | memset(output+remainders, 0, KEYLEN - remainders); /* add 0-padding */ 516 | state = (state_t*)output; 517 | InvCipher(ctx); 518 | } 519 | } 520 | 521 | 522 | #endif // CBC_MODE 523 | 524 | 525 | -------------------------------------------------------------------------------- /util/rs_aes.h: -------------------------------------------------------------------------------- 1 | #ifndef _AES128_H_ 2 | #define _AES128_H_ 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | // #define the macros below to 1/0 to enable/disable the mode of operation. 11 | // 12 | // CBC enables AES128 encryption in CBC-mode of operation and handles 0-padding. 13 | // ECB enables the basic ECB 16-byte block algorithm. Both can be enabled simultaneously. 14 | 15 | // The #ifndef-guard allows it to be configured before #include'ing or at compile time. 16 | #ifndef DISABLE_CBC_MODE 17 | #define CBC_MODE 18 | #endif 19 | 20 | #ifndef DISABLE_ECB_MODE 21 | #define ECB_MODE 22 | #endif 23 | 24 | /*****************************************************************************/ 25 | /* Private variables: */ 26 | /*****************************************************************************/ 27 | // state - array holding the intermediate results during decryption. 28 | typedef uint8_t state_t[4][4]; 29 | 30 | // Key length in bytes [128 bit] 31 | #define AES128_KEYLEN 16 32 | // The number of rounds in AES Cipher. 33 | #define Nr 10 34 | 35 | 36 | typedef struct { 37 | state_t* state; 38 | uint8_t RoundKey[176]; // The array that stores the round keys 39 | #ifdef CBC_MODE 40 | // uint8_t* Iv; 41 | uint8_t InitialVector[AES128_KEYLEN]; // Initial Vector used only for CBC mode 42 | #endif 43 | } AES_CTX; 44 | 45 | void AES128_InitContext(AES_CTX *ctx, const uint8_t* key, const uint8_t* initialVector); 46 | 47 | #ifdef ECB_MODE 48 | 49 | void AES128_ECB_encrypt(AES_CTX *ctx, const uint8_t* input, uint8_t *output); 50 | void AES128_ECB_decrypt(AES_CTX *ctx, const uint8_t* input, uint8_t *output); 51 | 52 | #endif // ECB_MODE 53 | 54 | 55 | #ifdef CBC_MODE 56 | 57 | void AES128_CBC_encrypt_buffer(AES_CTX *ctx, uint8_t* output, const uint8_t* input, uint32_t length); 58 | void AES128_CBC_decrypt_buffer(AES_CTX *ctx, uint8_t* output, const uint8_t* input, uint32_t length); 59 | 60 | #endif // CBC_MODE 61 | 62 | #ifdef __cplusplus 63 | } 64 | #endif 65 | 66 | #endif //_AES128_H_ 67 | -------------------------------------------------------------------------------- /util/rs_sha256.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Filename: sha256.c 3 | * Author: Brad Conte (brad AT bradconte.com) 4 | * Copyright: 5 | * Disclaimer: This code is presented "as is" without any guarantees. 6 | * Details: Implementation of the SHA-256 hashing algorithm. 7 | SHA-256 is one of the three algorithms in the SHA2 8 | specification. The others, SHA-384 and SHA-512, are not 9 | offered in this implementation. 10 | Algorithm specification can be found here: 11 | * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf 12 | This implementation uses little endian byte order. 13 | *********************************************************************/ 14 | 15 | /*************************** HEADER FILES ***************************/ 16 | #include 17 | #include 18 | #include "rs_sha256.h" 19 | 20 | /****************************** MACROS ******************************/ 21 | #define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) 22 | #define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) 23 | 24 | #define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) 25 | #define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) 26 | #define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) 27 | #define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) 28 | #define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) 29 | #define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) 30 | 31 | /**************************** VARIABLES *****************************/ 32 | static const WORD k[64] = { 33 | 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, 34 | 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, 35 | 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, 36 | 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, 37 | 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, 38 | 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, 39 | 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, 40 | 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 41 | }; 42 | 43 | /*********************** FUNCTION DEFINITIONS ***********************/ 44 | void sha256_transform(SHA256_CTX *ctx, const BYTE data[]) 45 | { 46 | WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; 47 | 48 | for (i = 0, j = 0; i < 16; ++i, j += 4) 49 | m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); 50 | for ( ; i < 64; ++i) 51 | m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; 52 | 53 | a = ctx->state[0]; 54 | b = ctx->state[1]; 55 | c = ctx->state[2]; 56 | d = ctx->state[3]; 57 | e = ctx->state[4]; 58 | f = ctx->state[5]; 59 | g = ctx->state[6]; 60 | h = ctx->state[7]; 61 | 62 | for (i = 0; i < 64; ++i) { 63 | t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i]; 64 | t2 = EP0(a) + MAJ(a,b,c); 65 | h = g; 66 | g = f; 67 | f = e; 68 | e = d + t1; 69 | d = c; 70 | c = b; 71 | b = a; 72 | a = t1 + t2; 73 | } 74 | 75 | ctx->state[0] += a; 76 | ctx->state[1] += b; 77 | ctx->state[2] += c; 78 | ctx->state[3] += d; 79 | ctx->state[4] += e; 80 | ctx->state[5] += f; 81 | ctx->state[6] += g; 82 | ctx->state[7] += h; 83 | } 84 | 85 | void sha256_init(SHA256_CTX *ctx) 86 | { 87 | ctx->datalen = 0; 88 | ctx->bitlen = 0; 89 | ctx->state[0] = 0x6a09e667; 90 | ctx->state[1] = 0xbb67ae85; 91 | ctx->state[2] = 0x3c6ef372; 92 | ctx->state[3] = 0xa54ff53a; 93 | ctx->state[4] = 0x510e527f; 94 | ctx->state[5] = 0x9b05688c; 95 | ctx->state[6] = 0x1f83d9ab; 96 | ctx->state[7] = 0x5be0cd19; 97 | } 98 | 99 | void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) 100 | { 101 | WORD i; 102 | 103 | for (i = 0; i < len; ++i) { 104 | ctx->data[ctx->datalen] = data[i]; 105 | ctx->datalen++; 106 | if (ctx->datalen == 64) { 107 | sha256_transform(ctx, ctx->data); 108 | ctx->bitlen += 512; 109 | ctx->datalen = 0; 110 | } 111 | } 112 | } 113 | 114 | void sha256_final(SHA256_CTX *ctx, BYTE hash[]) 115 | { 116 | WORD i; 117 | 118 | i = ctx->datalen; 119 | 120 | // Pad whatever data is left in the buffer. 121 | if (ctx->datalen < 56) { 122 | ctx->data[i++] = 0x80; 123 | while (i < 56) 124 | ctx->data[i++] = 0x00; 125 | } 126 | else { 127 | ctx->data[i++] = 0x80; 128 | while (i < 64) 129 | ctx->data[i++] = 0x00; 130 | sha256_transform(ctx, ctx->data); 131 | memset(ctx->data, 0, 56); 132 | } 133 | 134 | // Append to the padding the total message's length in bits and transform. 135 | ctx->bitlen += ctx->datalen * 8; 136 | ctx->data[63] = ctx->bitlen; 137 | ctx->data[62] = ctx->bitlen >> 8; 138 | ctx->data[61] = ctx->bitlen >> 16; 139 | ctx->data[60] = ctx->bitlen >> 24; 140 | ctx->data[59] = ctx->bitlen >> 32; 141 | ctx->data[58] = ctx->bitlen >> 40; 142 | ctx->data[57] = ctx->bitlen >> 48; 143 | ctx->data[56] = ctx->bitlen >> 56; 144 | sha256_transform(ctx, ctx->data); 145 | 146 | // Since this implementation uses little endian byte ordering and SHA uses big endian, 147 | // reverse all the bytes when copying the final state to the output hash. 148 | for (i = 0; i < 4; ++i) { 149 | hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; 150 | hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; 151 | hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; 152 | hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; 153 | hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; 154 | hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; 155 | hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; 156 | hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /util/rs_sha256.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Filename: sha256.h 3 | * Author: Brad Conte (brad AT bradconte.com) 4 | * Copyright: 5 | * Disclaimer: This code is presented "as is" without any guarantees. 6 | * Details: Defines the API for the corresponding SHA1 implementation. 7 | *********************************************************************/ 8 | 9 | #ifndef SHA256_H 10 | #define SHA256_H 11 | 12 | /*************************** HEADER FILES ***************************/ 13 | #include 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | /****************************** MACROS ******************************/ 20 | #define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest 21 | 22 | /**************************** DATA TYPES ****************************/ 23 | typedef unsigned char BYTE; // 8-bit byte 24 | typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines 25 | 26 | typedef struct { 27 | BYTE data[64]; 28 | WORD datalen; 29 | unsigned long long bitlen; 30 | WORD state[8]; 31 | } SHA256_CTX; 32 | 33 | /*********************** FUNCTION DECLARATIONS **********************/ 34 | void sha256_init(SHA256_CTX *ctx); 35 | void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len); 36 | void sha256_final(SHA256_CTX *ctx, BYTE hash[]); 37 | 38 | #ifdef __cplusplus 39 | } 40 | #endif 41 | 42 | #endif // SHA256_H 43 | --------------------------------------------------------------------------------