├── .travis.yml ├── API.md ├── LICENSE ├── README.md ├── antenna types.jpg ├── examples ├── LoRaDumpRegisters │ └── LoRaDumpRegisters.ino ├── LoRaDuplex │ └── LoRaDuplex.ino ├── LoRaDuplexCallback │ └── LoRaDuplexCallback.ino ├── LoRaReceiver │ └── LoRaReceiver.ino ├── LoRaReceiverCallback │ └── LoRaReceiverCallback.ino ├── LoRaReceiver_modified │ └── LoRaReceiver_modified.ino ├── LoRaSender │ └── LoRaSender.ino ├── LoRaSender_modified │ └── LoRaSender_modified.ino ├── LoRaSetSpread │ └── LoRaSetSpread.ino └── LoRaSetSyncWord │ └── LoRaSetSyncWord.ino ├── keywords.txt ├── library.properties └── src ├── LoRa_STM32.cpp └── LoRa_STM32.h /.travis.yml: -------------------------------------------------------------------------------- 1 | language: generic 2 | env: 3 | global: 4 | - IDE_VERSION=1.8.2 5 | matrix: 6 | - BOARD="arduino:avr:uno" 7 | - BOARD="arduino:avr:micro" 8 | - BOARD="arduino:avr:mega:cpu=atmega2560" 9 | - BOARD="arduino:samd:arduino_zero_edbg" 10 | - BOARD="arduino:samd:mkr1000" 11 | - BOARD="arduino:samd:mkrzero" 12 | before_install: 13 | - wget http://downloads.arduino.cc/arduino-$IDE_VERSION-linux64.tar.xz 14 | - tar xf arduino-$IDE_VERSION-linux64.tar.xz 15 | - mv arduino-$IDE_VERSION $HOME/arduino-ide 16 | - export PATH=$PATH:$HOME/arduino-ide 17 | - if [[ "$BOARD" =~ "arduino:samd:" ]]; then 18 | arduino --install-boards arduino:samd &> /dev/null; 19 | fi 20 | - buildExampleSketch() { arduino --verbose-build --verify --board $BOARD $PWD/examples/$1/$1.ino; } 21 | install: 22 | - mkdir -p $HOME/Arduino/libraries 23 | - ln -s $PWD $HOME/Arduino/libraries/LoRa_STM32 24 | script: 25 | - buildExampleSketch LoRaDumpRegisters 26 | - buildExampleSketch LoRaDuplex 27 | - buildExampleSketch LoRaDuplexCallback 28 | - buildExampleSketch LoRaReceiver 29 | - buildExampleSketch LoRaReceiverCallback 30 | - buildExampleSketch LoRaSender 31 | - buildExampleSketch LoRaSetSpread 32 | - buildExampleSketch LoRaSetSyncWord 33 | -------------------------------------------------------------------------------- /API.md: -------------------------------------------------------------------------------- 1 | # LoRa API 2 | 3 | ## Include Library 4 | 5 | ```arduino 6 | #include 7 | ``` 8 | 9 | ## Setup 10 | 11 | ### Begin 12 | 13 | Initialize the library with the specified frequency. 14 | 15 | ```arduino 16 | LoRa.begin(frequency); 17 | ``` 18 | * `frequency` - frequency in Hz (`433E6`, `866E6`, `915E6`) 19 | 20 | Returns `1` on success, `0` on failure. 21 | 22 | ### Set pins 23 | 24 | Override the default `NSS`, `NRESET`, and `DIO0` pins used by the library. **Must** be called before `LoRa.begin()`. 25 | 26 | ```arduino 27 | LoRa.setPins(ss, reset, dio0); 28 | ``` 29 | * `ss` - new slave select pin to use, defaults to `10` 30 | * `reset` - new reset pin to use, defaults to `9` 31 | * `dio0` - new DIO0 pin to use, defaults to `2`. **Must** be interrupt capable via [attachInterrupt(...)](https://www.arduino.cc/en/Reference/AttachInterrupt). 32 | 33 | This call is optional and only needs to be used if you need to change the default pins used. 34 | 35 | #### No MCU controlled reset pin 36 | 37 | To save further pins one could connect the reset pin of the MCU with reset pin of the radio thus resetting only during startup. 38 | 39 | * `reset` - set to `-1` to omit this pin 40 | 41 | ### Set SPI Frequency 42 | 43 | Override the default SPI frequency of 10 MHz used by the library. **Must** be called before `LoRa.begin()`. 44 | 45 | ```arduino 46 | LoRa.setSPIFrequency(frequency); 47 | ``` 48 | * `frequency` - new SPI frequency to use, defaults to `8E6` 49 | 50 | This call is optional and only needs to be used if you need to change the default SPI frequency used. Some logic level converters cannot support high speeds such as 8 MHz, so a lower SPI frequency can be selected with `LoRa.setSPIFrequency(frequency)`. 51 | 52 | ### End 53 | 54 | Stop the library 55 | 56 | ```arduino 57 | LoRa.end() 58 | ``` 59 | 60 | ## Sending data 61 | 62 | ### Begin packet 63 | 64 | Start the sequence of sending a packet. 65 | 66 | ```arduino 67 | LoRa.beginPacket(); 68 | 69 | LoRa.beginPacket(implicitHeader); 70 | ``` 71 | 72 | * `implicitHeader` - (optional) `true` enables implicit header mode, `false` enables explicit header mode (default) 73 | 74 | Returns `1` on success, `0` on failure. 75 | 76 | ### Writing 77 | 78 | Write data to the packet. Each packet can contain up to 255 bytes. 79 | 80 | ```arduino 81 | LoRa.write(byte); 82 | 83 | LoRa.write(buffer, length); 84 | ``` 85 | * `byte` - single byte to write to packet 86 | 87 | or 88 | 89 | * `buffer` - data to write to packet 90 | * `length` - size of data to write 91 | 92 | Returns the number of bytes written. 93 | 94 | **Note:** Other Arduino `Print` API's can also be used to write data into the packet 95 | 96 | ### End packet 97 | 98 | End the sequence of sending a packet. 99 | 100 | ```arduino 101 | LoRa.endPacket() 102 | ``` 103 | 104 | Returns `1` on success, `0` on failure. 105 | 106 | ## Receiving data 107 | 108 | ### Parsing packet 109 | 110 | Check if a packet has been received. 111 | 112 | ```arduino 113 | int packetSize = LoRa.parsePacket(); 114 | 115 | int packetSize = LoRa.parsePacket(size); 116 | ``` 117 | 118 | * `size` - (optional) if `> 0` implicit header mode is enabled with the expected a packet of `size` bytes, default mode is explicit header mode 119 | 120 | 121 | Returns the packet size in bytes or `0` if no packet was received. 122 | 123 | ### Continuous receive mode 124 | 125 | #### Register callback 126 | 127 | Register a callback function for when a packet is received. 128 | 129 | ```arduino 130 | LoRa.onReceive(onReceive); 131 | 132 | void onReceive(int packetSize) { 133 | // ... 134 | } 135 | ``` 136 | 137 | * `onReceive` - function to call when a packet is received. 138 | 139 | #### Receive mode 140 | 141 | Puts the radio in continuous receive mode. 142 | 143 | ```arduino 144 | LoRa.receive(); 145 | 146 | LoRa.receive(int size); 147 | ``` 148 | 149 | * `size` - (optional) if `> 0` implicit header mode is enabled with the expected a packet of `size` bytes, default mode is explicit header mode 150 | 151 | The `onReceive` callback will be called when a packet is received. 152 | 153 | ### Packet RSSI 154 | 155 | ```arduino 156 | int rssi = LoRa.packetRssi(); 157 | ``` 158 | 159 | Returns the RSSI of the received packet. 160 | 161 | ### Packet SNR 162 | 163 | ```arduino 164 | float snr = LoRa.packetSnr(); 165 | ``` 166 | 167 | Returns the estimated SNR of the received packet in dB. 168 | 169 | ### Available 170 | 171 | ```arduino 172 | int availableBytes = LoRa.available() 173 | ``` 174 | 175 | Returns number of bytes available for reading. 176 | 177 | ### Peeking 178 | 179 | Peek at the next byte in the packet. 180 | 181 | ```arduino 182 | byte b = LoRa.peek(); 183 | ``` 184 | 185 | Returns the next byte in the packet or `-1` if no bytes are available. 186 | 187 | ### Reading 188 | 189 | Read the next byte from the packet. 190 | 191 | ```arduino 192 | byte b = LoRa.read(); 193 | ``` 194 | 195 | Returns the next byte in the packet or `-1` if no bytes are available. 196 | 197 | **Note:** Other Arduino [`Stream` API's](https://www.arduino.cc/en/Reference/Stream) can also be used to read data from the packet 198 | 199 | ## Other radio modes 200 | 201 | ### Idle mode 202 | 203 | Put the radio in idle (standby) mode. 204 | 205 | ```arduino 206 | LoRa.idle(); 207 | ``` 208 | 209 | ### Sleep mode 210 | 211 | Put the radio in sleep mode. 212 | 213 | ```arduino 214 | LoRa.sleep(); 215 | ``` 216 | 217 | ## Radio parameters 218 | 219 | ### TX Power 220 | 221 | Change the TX power of the radio. 222 | 223 | ```arduino 224 | LoRa.setTxPower(txPower); 225 | 226 | LoRa.setTxPower(txPower, outputPin); 227 | ``` 228 | * `txPower` - TX power in dB, defaults to `17` 229 | * `outputPin` - (optional) PA output pin, supported values are `PA_OUTPUT_RFO_PIN` and `PA_OUTPUT_PA_BOOST_PIN`, defaults to `PA_OUTPUT_PA_BOOST_PIN`. 230 | 231 | Supported values are between `2` and `17` for `PA_OUTPUT_PA_BOOST_PIN`, `0` and `14` for `PA_OUTPUT_RFO_PIN`. 232 | 233 | Most modules have the PA output pin connected to PA BOOST, 234 | 235 | ### Frequency 236 | 237 | Change the frequency of the radio. 238 | 239 | ```arduino 240 | LoRa.setFrequency(frequency); 241 | ``` 242 | * `frequency` - frequency in Hz (`433E6`, `866E6`, `915E6`) 243 | 244 | ### Spreading Factor 245 | 246 | Change the spreading factor of the radio. 247 | 248 | ```arduino 249 | LoRa.setSpreadingFactor(spreadingFactor); 250 | ``` 251 | * `spreadingFactor` - spreading factor, defaults to `7` 252 | 253 | Supported values are between `6` and `12`. If a spreading factor of `6` is set, implicit header mode must be used to transmit and receive packets. 254 | 255 | ### Signal Bandwidth 256 | 257 | Change the signal bandwidth of the radio. 258 | 259 | ```arduino 260 | LoRa.setSignalBandwidth(signalBandwidth); 261 | ``` 262 | 263 | * `signalBandwidth` - signal bandwidth in Hz, defaults to `125E3`. 264 | 265 | Supported values are `7.8E3`, `10.4E3`, `15.6E3`, `20.8E3`, `31.25E3`, `41.7E3`, `62.5E3`, `125E3`, and `250E3`. 266 | 267 | ### Coding Rate 268 | 269 | Change the coding rate of the radio. 270 | 271 | ```arduino 272 | LoRa.setCodingRate4(codingRateDenominator); 273 | ``` 274 | 275 | * `codingRateDenominator` - denominator of the coding rate, defaults to `5` 276 | 277 | Supported values are between `5` and `8`, these correspond to coding rates of `4/5` and `4/8`. The coding rate numerator is fixed at `4`. 278 | 279 | ### Preamble Length 280 | 281 | Change the preamble length of the radio. 282 | 283 | ```arduino 284 | LoRa.setPreambleLength(preambleLength); 285 | ``` 286 | 287 | * `preambleLength` - preamble length in symbols, defaults to `8` 288 | 289 | Supported values are between `6` and `65535`. 290 | 291 | ### Sync Word 292 | 293 | Change the sync word of the radio. 294 | 295 | ```arduino 296 | LoRa.setSyncWord(syncWord); 297 | ``` 298 | 299 | * `syncWord` - byte value to use as the sync word, defaults to `0x34` 300 | 301 | ### CRC 302 | 303 | Enable or disable CRC usage, by default a CRC is not used. 304 | 305 | ```arduino 306 | LoRa.enableCrc(); 307 | 308 | LoRa.disableCrc(); 309 | ``` 310 | 311 | ## Other functions 312 | 313 | ### Random 314 | 315 | Generate a random byte, based on the Wideband RSSI measurement. 316 | 317 | ``` 318 | byte b = LoRa.random(); 319 | ``` 320 | 321 | Returns random byte. 322 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Sandeep Mistry 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Arduino LoRa For STM32F103CB 2 | 3 | [![Build Status](https://travis-ci.org/sandeepmistry/arduino-LoRa.svg?branch=master)](https://travis-ci.org/sandeepmistry/arduino-LoRa) 4 | 5 | An [Arduino](http://arduino.cc/) library for sending and receiving data using [LoRa](https://www.lora-alliance.org/) radios. 6 | 7 | ### Semtech SX1276/77/78/79 wiring 8 | 9 | | Semtech SX1276/77/78/79 | Arduino(STM32F103CB) | 10 | | :---------------------: | :------:| 11 | | VCC | 3.3V | 12 | | GND | GND | 13 | | SCK | SCK | 14 | | MISO | MISO | 15 | | MOSI | MOSI | 16 | | NSS | PA4 | 17 | | NRESET | PC13 | 18 | | DIO0 | PA1 | 19 | | DIO1 | PB13 | 20 | | DIO2 | PB12 | 21 | 22 | | Charger Header J4 | Battery 3.7V Li | 23 | | :---------------------: | :------:| 24 | | 1 | + | 25 | | 2 | - | 26 | 27 | | Charge Value| Arduino(STM32F103CB) | 28 | | :---------------------: | :------:| 29 | | Divinder 430K/1.5M | PA0 | 30 | 31 | 32 | ### Board Header Details 33 | 34 | | Header J1 Pins | Arduino(STM32F103CB) | 35 | | :---------------------: | :------:| 36 | | 1 | 3.3V | 37 | | 2 | PB9 | 38 | | 3 | PB8 | 39 | | 4 | GND | 40 | | 5 | GND | 41 | | 6 | PB1 | 42 | | 7 | PB0 | 43 | | 8 | GND | 44 | | 9 | PA2 | 45 | | 10 | PA3| 46 | | 11 | PB10 | 47 | | 12 | PB11 | 48 | | 13 | GND | 49 | 50 | | Header J2 Pins | Arduino(STM32F103CB) | 51 | | :---------------------: | :------:| 52 | | 1 | 5V | 53 | | 2 | GND | 54 | | 3 | PB5 | 55 | | 4 | PB4 | 56 | | 5 | PB3 | 57 | | 6 | PA15 | 58 | | 7 | PA12| 59 | | 8 | PA11| 60 | | 9 | PA8| 61 | | 10 | PB15| 62 | | 11 | PB14 | 63 | | 12 | GND | 64 | | 13 | GND | 65 | 66 | | Header J3 Pins | Arduino(STM32F103CB) | 67 | | :---------------------: | :------:| 68 | | 1 | 3.3V | 69 | | 2 | SWIO | 70 | | 3 | SWCLK| 71 | | 4 | GND | 72 | 73 | 74 | | Header J5 Pins | Arduino(STM32F103CB) | 75 | | :---------------------: | :------:| 76 | | 1 | 3.3V | 77 | | 2 | GND | 78 | | 3 | PB6/SCL | 79 | | 4 | PB7/SDA | 80 | 81 | | Header J7 Row One Pins | Arduino(STM32F103CB) | Header J7 Row Two Pins | Arduino(STM32F103CB) | 82 | | :---------------------: | :------:|:---------------------:|:------:| 83 | | 1 | 3.3V | 2 |3.3V | 84 | | 3 | BOOT0 | 4 | BOOT1 85 | | 5 | GND | 6 | GND | 86 | 87 | **NOTES**: 88 | While Programming through on board USB (CH340 USB to UART Driver required in windows) connected to STM32F103CB's PA10/USART1_RX and PA9/USART1_TX select the following in Arduino IDE 89 | 90 | | Board :Generic STM32F103C Series | 91 | 92 | | Variant : STM32F103CB(20k RAM,128k Flash) | 93 | 94 | | CPU Speed:72MHz Normal | 95 | 96 | | Upload Meathod: Serial | 97 | 98 | | Port should be picked as per windows (Com 1,2,3 etc) | 99 | 100 | 101 | To add STM32 board to arduino ide use 102 | https://github.com/rogerclarkmelbourne/Arduino_STM32 103 | 104 | ## Header J7 should be set as following for programming 105 | 106 | Pin 1 --- Pin 3 Short using Jumper 107 | 108 | Pin 4 --- Pin 6 Short using Jumper 109 | 110 | Press reset pin S3 before pressing upload button 111 | 112 | ## Header J7 should be set as following to run application program 113 | 114 | Pin 3 --- Pin 5 Short using Jumper 115 | 116 | Pin 4 --- Pin 6 Short using Jumper 117 | 118 | Press reset pin S3 after this 119 | 120 | 121 | `NSS`, `NRESET`, and `DIO0` pins can be changed by using `LoRa.setPins(ss, reset, dio0)`. `DIO0` pin is optional, it is only needed for receive callback mode. If `DIO0` pin is used, it **must** be interrupt capable via [`attachInterrupt(...)`](https://www.arduino.cc/en/Reference/AttachInterrupt). 122 | 123 | 124 | ## Installation 125 | 126 | ### Using the Arduino IDE Library Manager 127 | 128 | 1. Choose `Sketch` -> `Include Library` -> `Manage Libraries...` 129 | 2. Type `LoRa` into the search box. 130 | 3. Click the row to select the library. 131 | 4. Click the `Install` button to install the library. 132 | 133 | ### Using Git 134 | 135 | ```sh 136 | cd ~/Documents/Arduino/libraries/ 137 | git clone https://github.com/sandeepmistry/arduino-LoRa LoRa 138 | ``` 139 | 140 | ## API 141 | 142 | See [API.md](API.md). 143 | 144 | ## Examples 145 | 146 | See [examples](examples) folder. 147 | 148 | ## FAQ 149 | 150 | **1) Initilizating the LoRa radio is failing** 151 | 152 | Please check the wiring you are using matches what's listed in [Semtech SX1276/77/78/79 wiring](#semtech-sx1276777879-wiring). You can also use `LoRa.setPins(ss, reset, dio0)` to change the default pins used. Some logic level converters cannot operate at 8 MHz, you can call `LoRa.setSPIFrequency(frequency)` to lower the SPI frequency used by the library. Both API's must be called before `LoRa.begin(...)`. 153 | 154 | **2) Can other radios see the packets I'm sending?** 155 | 156 | Yes, any LoRa radio that are configured with the same radio parameters and in range can see the packets you send. 157 | 158 | **3) Is the data I'm sending encrypted?** 159 | 160 | No, all data is sent unencrypted. If want your packet data to be encrypted, you must encrypt it before passing it into this library, followed by decrypting on the receiving end. 161 | 162 | **4) How does this library differ from LoRaWAN libraries?** 163 | 164 | This library exposes the LoRa radio directly, and allows you to send data to any radios in range with same radio parameters. All data is broadcasted and there is no addressing. LoRaWAN builds on top of LoRA, but adds addressing, encryption, and additional layers. It also requires a LoRaWAN gateway and LoRaWAN network and application server. 165 | 166 | 167 | 168 | ## License 169 | 170 | This libary is [licensed](LICENSE) under the [MIT Licence](http://en.wikipedia.org/wiki/MIT_License). 171 | # arduino-LoRa-STM32 172 | -------------------------------------------------------------------------------- /antenna types.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armtronix/arduino-LoRa-STM32/ab215477d13c641b4defa04594b6eed10a719465/antenna types.jpg -------------------------------------------------------------------------------- /examples/LoRaDumpRegisters/LoRaDumpRegisters.ino: -------------------------------------------------------------------------------- 1 | /* 2 | LoRa register dump 3 | 4 | This examples shows how to inspect and output the LoRa radio's 5 | registers on the Serial interface 6 | */ 7 | #include // include libraries 8 | #include 9 | 10 | void setup() { 11 | Serial.begin(115200); // initialize serial 12 | while (!Serial); 13 | 14 | Serial.println("LoRa Dump Registers"); 15 | 16 | // override the default CS, reset, and IRQ pins (optional) 17 | // LoRa.setPins(7, 6, 1); // set CS, reset, IRQ pin 18 | 19 | if (!LoRa.begin(868E6)) { // initialize ratio at 915 MHz 20 | Serial.println("LoRa init failed. Check your connections."); 21 | while (true); // if failed, do nothing 22 | } 23 | 24 | LoRa.dumpRegisters(Serial); 25 | } 26 | 27 | 28 | void loop() { 29 | } 30 | 31 | -------------------------------------------------------------------------------- /examples/LoRaDuplex/LoRaDuplex.ino: -------------------------------------------------------------------------------- 1 | /* 2 | LoRa Duplex communication 3 | 4 | Sends a message every half second, and polls continually 5 | for new incoming messages. Implements a one-byte addressing scheme, 6 | with 0xFF as the broadcast address. 7 | 8 | Uses readString() from Stream class to read payload. The Stream class' 9 | timeout may affect other functuons, like the radio's callback. For an 10 | 11 | created 28 April 2017 12 | by Tom Igoe 13 | */ 14 | #include // include libraries 15 | #include 16 | 17 | const int csPin = 7; // LoRa radio chip select 18 | const int resetPin = 6; // LoRa radio reset 19 | const int irqPin = 1; // change for your board; must be a hardware interrupt pin 20 | 21 | String outgoing; // outgoing message 22 | 23 | byte msgCount = 0; // count of outgoing messages 24 | byte localAddress = 0xBB; // address of this device 25 | byte destination = 0xFF; // destination to send to 26 | long lastSendTime = 0; // last send time 27 | int interval = 2000; // interval between sends 28 | 29 | void setup() { 30 | Serial.begin(115200); // initialize serial 31 | while (!Serial); 32 | 33 | Serial.println("LoRa Duplex"); 34 | 35 | // override the default CS, reset, and IRQ pins (optional) 36 | LoRa.setPins(csPin, resetPin, irqPin);// set CS, reset, IRQ pin 37 | 38 | if (!LoRa.begin(868E6)) { // initialize ratio at 915 MHz 39 | Serial.println("LoRa init failed. Check your connections."); 40 | while (true); // if failed, do nothing 41 | } 42 | 43 | Serial.println("LoRa init succeeded."); 44 | } 45 | 46 | void loop() { 47 | if (millis() - lastSendTime > interval) { 48 | String message = "HeLoRa World!"; // send a message 49 | sendMessage(message); 50 | Serial.println("Sending " + message); 51 | lastSendTime = millis(); // timestamp the message 52 | interval = random(2000) + 1000; // 2-3 seconds 53 | } 54 | 55 | // parse for a packet, and call onReceive with the result: 56 | onReceive(LoRa.parsePacket()); 57 | } 58 | 59 | void sendMessage(String outgoing) { 60 | LoRa.beginPacket(); // start packet 61 | LoRa.write(destination); // add destination address 62 | LoRa.write(localAddress); // add sender address 63 | LoRa.write(msgCount); // add message ID 64 | LoRa.write(outgoing.length()); // add payload length 65 | LoRa.print(outgoing); // add payload 66 | LoRa.endPacket(); // finish packet and send it 67 | msgCount++; // increment message ID 68 | } 69 | 70 | void onReceive(int packetSize) { 71 | if (packetSize == 0) return; // if there's no packet, return 72 | 73 | // read packet header bytes: 74 | int recipient = LoRa.read(); // recipient address 75 | byte sender = LoRa.read(); // sender address 76 | byte incomingMsgId = LoRa.read(); // incoming msg ID 77 | byte incomingLength = LoRa.read(); // incoming msg length 78 | 79 | String incoming = ""; 80 | 81 | while (LoRa.available()) { 82 | incoming += (char)LoRa.read(); 83 | } 84 | 85 | if (incomingLength != incoming.length()) { // check length for error 86 | Serial.println("error: message length does not match length"); 87 | return; // skip rest of function 88 | } 89 | 90 | // if the recipient isn't this device or broadcast, 91 | if (recipient != localAddress && recipient != 0xFF) { 92 | Serial.println("This message is not for me."); 93 | return; // skip rest of function 94 | } 95 | 96 | // if message is for this device, or broadcast, print details: 97 | Serial.println("Received from: 0x" + String(sender, HEX)); 98 | Serial.println("Sent to: 0x" + String(recipient, HEX)); 99 | Serial.println("Message ID: " + String(incomingMsgId)); 100 | Serial.println("Message length: " + String(incomingLength)); 101 | Serial.println("Message: " + incoming); 102 | Serial.println("RSSI: " + String(LoRa.packetRssi())); 103 | Serial.println("Snr: " + String(LoRa.packetSnr())); 104 | Serial.println(); 105 | } 106 | 107 | -------------------------------------------------------------------------------- /examples/LoRaDuplexCallback/LoRaDuplexCallback.ino: -------------------------------------------------------------------------------- 1 | /* 2 | LoRa Duplex communication wth callback 3 | 4 | Sends a message every half second, and uses callback 5 | for new incoming messages. Implements a one-byte addressing scheme, 6 | with 0xFF as the broadcast address. 7 | 8 | Note: while sending, LoRa radio is not listening for incoming messages. 9 | Note2: when using the callback method, you can't use any of the Stream 10 | functions that rely on the timeout, such as readString, parseInt(), etc. 11 | 12 | created 28 April 2017 13 | by Tom Igoe 14 | */ 15 | #include // include libraries 16 | #include 17 | 18 | const int csPin = 7; // LoRa radio chip select 19 | const int resetPin = 6; // LoRa radio reset 20 | const int irqPin = 1; // change for your board; must be a hardware interrupt pin 21 | 22 | String outgoing; // outgoing message 23 | byte msgCount = 0; // count of outgoing messages 24 | byte localAddress = 0xBB; // address of this device 25 | byte destination = 0xFF; // destination to send to 26 | long lastSendTime = 0; // last send time 27 | int interval = 2000; // interval between sends 28 | 29 | void setup() { 30 | Serial.begin(115200); // initialize serial 31 | while (!Serial); 32 | 33 | Serial.println("LoRa Duplex with callback"); 34 | 35 | // override the default CS, reset, and IRQ pins (optional) 36 | LoRa.setPins(csPin, resetPin, irqPin);// set CS, reset, IRQ pin 37 | 38 | if (!LoRa.begin(868E6)) { // initialize ratio at 915 MHz 39 | Serial.println("LoRa init failed. Check your connections."); 40 | while (true); // if failed, do nothing 41 | } 42 | 43 | LoRa.onReceive(onReceive); 44 | LoRa.receive(); 45 | Serial.println("LoRa init succeeded."); 46 | } 47 | 48 | void loop() { 49 | if (millis() - lastSendTime > interval) { 50 | String message = "HeLoRa World!"; // send a message 51 | sendMessage(message); 52 | Serial.println("Sending " + message); 53 | lastSendTime = millis(); // timestamp the message 54 | interval = random(2000) + 1000; // 2-3 seconds 55 | LoRa.receive(); // go back into receive mode 56 | } 57 | } 58 | 59 | void sendMessage(String outgoing) { 60 | LoRa.beginPacket(); // start packet 61 | LoRa.write(destination); // add destination address 62 | LoRa.write(localAddress); // add sender address 63 | LoRa.write(msgCount); // add message ID 64 | LoRa.write(outgoing.length()); // add payload length 65 | LoRa.print(outgoing); // add payload 66 | LoRa.endPacket(); // finish packet and send it 67 | msgCount++; // increment message ID 68 | } 69 | 70 | void onReceive(int packetSize) { 71 | if (packetSize == 0) return; // if there's no packet, return 72 | 73 | // read packet header bytes: 74 | int recipient = LoRa.read(); // recipient address 75 | byte sender = LoRa.read(); // sender address 76 | byte incomingMsgId = LoRa.read(); // incoming msg ID 77 | byte incomingLength = LoRa.read(); // incoming msg length 78 | 79 | String incoming = ""; // payload of packet 80 | 81 | while (LoRa.available()) { // can't use readString() in callback, so 82 | incoming += (char)LoRa.read(); // add bytes one by one 83 | } 84 | 85 | if (incomingLength != incoming.length()) { // check length for error 86 | Serial.println("error: message length does not match length"); 87 | return; // skip rest of function 88 | } 89 | 90 | // if the recipient isn't this device or broadcast, 91 | if (recipient != localAddress && recipient != 0xFF) { 92 | Serial.println("This message is not for me."); 93 | return; // skip rest of function 94 | } 95 | 96 | // if message is for this device, or broadcast, print details: 97 | Serial.println("Received from: 0x" + String(sender, HEX)); 98 | Serial.println("Sent to: 0x" + String(recipient, HEX)); 99 | Serial.println("Message ID: " + String(incomingMsgId)); 100 | Serial.println("Message length: " + String(incomingLength)); 101 | Serial.println("Message: " + incoming); 102 | Serial.println("RSSI: " + String(LoRa.packetRssi())); 103 | Serial.println("Snr: " + String(LoRa.packetSnr())); 104 | Serial.println(); 105 | } 106 | 107 | -------------------------------------------------------------------------------- /examples/LoRaReceiver/LoRaReceiver.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void setup() { 5 | Serial.begin(115200); 6 | while (!Serial); 7 | 8 | Serial.println("LoRa Receiver"); 9 | 10 | if (!LoRa.begin(868E6)) { 11 | Serial.println("Starting LoRa failed!"); 12 | while (1); 13 | } 14 | } 15 | 16 | void loop() { 17 | // try to parse packet 18 | int packetSize = LoRa.parsePacket(); 19 | if (packetSize) { 20 | // received a packet 21 | Serial.print("Received packet '"); 22 | 23 | // read packet 24 | while (LoRa.available()) { 25 | Serial.print((char)LoRa.read()); 26 | } 27 | 28 | // print RSSI of packet 29 | Serial.print("' with RSSI "); 30 | Serial.println(LoRa.packetRssi()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/LoRaReceiverCallback/LoRaReceiverCallback.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void setup() { 5 | Serial.begin(115200); 6 | while (!Serial); 7 | 8 | Serial.println("LoRa Receiver Callback"); 9 | 10 | if (!LoRa.begin(868E6)) { 11 | Serial.println("Starting LoRa failed!"); 12 | while (1); 13 | } 14 | 15 | // register the receive callback 16 | LoRa.onReceive(onReceive); 17 | 18 | // put the radio into receive mode 19 | LoRa.receive(); 20 | } 21 | 22 | void loop() { 23 | // do nothing 24 | } 25 | 26 | void onReceive(int packetSize) { 27 | // received a packet 28 | Serial.print("Received packet '"); 29 | 30 | // read packet 31 | for (int i = 0; i < packetSize; i++) { 32 | Serial.print((char)LoRa.read()); 33 | } 34 | 35 | // print RSSI of packet 36 | Serial.print("' with RSSI "); 37 | Serial.println(LoRa.packetRssi()); 38 | } 39 | 40 | -------------------------------------------------------------------------------- /examples/LoRaReceiver_modified/LoRaReceiver_modified.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void setup() { 5 | Serial.begin(115200); 6 | while (!Serial); 7 | 8 | Serial.println("LoRa Receiver"); 9 | //LoRa.setTxPower(17, PA_OUTPUT_PA_BOOST_PIN); 10 | //LoRa.setSignalBandwidth(31.25E3); 11 | //LoRa.setSpreadingFactor(7); 12 | //LoRa.setCodingRate4(5); 13 | if (!LoRa.begin(866E6)) { 14 | Serial.println("Stating LoRa failed!"); 15 | while (1); 16 | } 17 | } 18 | 19 | void loop() { 20 | // try to parse packet 21 | int packetSize = LoRa.parsePacket(); 22 | if (packetSize) { 23 | // received a packet 24 | Serial.print("Received packet '"); 25 | 26 | // read packet 27 | while (LoRa.available()) { 28 | Serial.print((char)LoRa.read()); 29 | } 30 | 31 | // print RSSI of packet 32 | Serial.print("' with RSSI "); 33 | Serial.println(LoRa.packetRssi()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /examples/LoRaSender/LoRaSender.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int counter = 0; 5 | 6 | void setup() { 7 | Serial.begin(115200); 8 | while (!Serial); 9 | 10 | Serial.println("LoRa Sender"); 11 | 12 | if (!LoRa.begin(868E6)) { 13 | Serial.println("Starting LoRa failed!"); 14 | while (1); 15 | } 16 | } 17 | 18 | void loop() { 19 | Serial.print("Sending packet: "); 20 | Serial.println(counter); 21 | 22 | // send packet 23 | LoRa.beginPacket(); 24 | LoRa.print("hello "); 25 | LoRa.print(counter); 26 | LoRa.endPacket(); 27 | 28 | counter++; 29 | 30 | delay(5000); 31 | } 32 | -------------------------------------------------------------------------------- /examples/LoRaSender_modified/LoRaSender_modified.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int counter = 0; 5 | 6 | void setup() { 7 | Serial.begin(115200); 8 | while (!Serial); 9 | 10 | Serial.println("LoRa Sender"); 11 | LoRa.setTxPower(17, PA_OUTPUT_PA_BOOST_PIN); 12 | LoRa.setSignalBandwidth(31.25E3); 13 | LoRa.setSpreadingFactor(12); 14 | LoRa.setCodingRate4(5); 15 | if (!LoRa.begin(866E6)) { 16 | Serial.println("Starting LoRa failed!"); 17 | while (1); 18 | } 19 | } 20 | 21 | void loop() { 22 | Serial.print("Sending packet: "); 23 | Serial.println(counter); 24 | 25 | // send packet 26 | LoRa.beginPacket(); 27 | LoRa.print("hello "); 28 | LoRa.print(counter); 29 | LoRa.endPacket(); 30 | 31 | counter++; 32 | 33 | delay(5000); 34 | } 35 | -------------------------------------------------------------------------------- /examples/LoRaSetSpread/LoRaSetSpread.ino: -------------------------------------------------------------------------------- 1 | /* 2 | LoRa Duplex communication with Spreading Factor 3 | 4 | Sends a message every half second, and polls continually 5 | for new incoming messages. Sets the LoRa radio's spreading factor. 6 | 7 | Spreading factor affects how far apart the radio's transmissions 8 | are, across the available bandwidth. Radios with different spreading 9 | factors will not receive each other's transmissions. This is one way you 10 | can filter out radios you want to ignore, without making an addressing scheme. 11 | 12 | Spreading factor affects reliability of transmission at high rates, however, 13 | so avoid a hugh spreading factor when you're sending continually. 14 | 15 | See the Semtech datasheet, http://www.semtech.com/images/datasheet/sx1276.pdf 16 | for more on Spreading Factor. 17 | 18 | created 28 April 2017 19 | by Tom Igoe 20 | */ 21 | #include // include libraries 22 | #include 23 | 24 | const int csPin = 7; // LoRa radio chip select 25 | const int resetPin = 6; // LoRa radio reset 26 | const int irqPin = 1; // change for your board; must be a hardware interrupt pin 27 | 28 | byte msgCount = 0; // count of outgoing messages 29 | int interval = 2000; // interval between sends 30 | long lastSendTime = 0; // time of last packet send 31 | 32 | void setup() { 33 | Serial.begin(115200); // initialize serial 34 | while (!Serial); 35 | 36 | Serial.println("LoRa Duplex - Set spreading factor"); 37 | 38 | // override the default CS, reset, and IRQ pins (optional) 39 | LoRa.setPins(csPin, resetPin, irqPin); // set CS, reset, IRQ pin 40 | 41 | if (!LoRa.begin(868E6)) { // initialize ratio at 915 MHz 42 | Serial.println("LoRa init failed. Check your connections."); 43 | while (true); // if failed, do nothing 44 | } 45 | 46 | LoRa.setSpreadingFactor(8); // ranges from 6-12,default 7 see API docs 47 | Serial.println("LoRa init succeeded."); 48 | } 49 | 50 | void loop() { 51 | if (millis() - lastSendTime > interval) { 52 | String message = "HeLoRa World! "; // send a message 53 | message += msgCount; 54 | sendMessage(message); 55 | Serial.println("Sending " + message); 56 | lastSendTime = millis(); // timestamp the message 57 | interval = random(2000) + 1000; // 2-3 seconds 58 | msgCount++; 59 | } 60 | 61 | // parse for a packet, and call onReceive with the result: 62 | onReceive(LoRa.parsePacket()); 63 | } 64 | 65 | void sendMessage(String outgoing) { 66 | LoRa.beginPacket(); // start packet 67 | LoRa.print(outgoing); // add payload 68 | LoRa.endPacket(); // finish packet and send it 69 | msgCount++; // increment message ID 70 | } 71 | 72 | void onReceive(int packetSize) { 73 | if (packetSize == 0) return; // if there's no packet, return 74 | 75 | // read packet header bytes: 76 | String incoming = ""; 77 | 78 | while (LoRa.available()) { 79 | incoming += (char)LoRa.read(); 80 | } 81 | 82 | Serial.println("Message: " + incoming); 83 | Serial.println("RSSI: " + String(LoRa.packetRssi())); 84 | Serial.println("Snr: " + String(LoRa.packetSnr())); 85 | Serial.println(); 86 | } 87 | 88 | -------------------------------------------------------------------------------- /examples/LoRaSetSyncWord/LoRaSetSyncWord.ino: -------------------------------------------------------------------------------- 1 | /* 2 | LoRa Duplex communication with Sync Word 3 | 4 | Sends a message every half second, and polls continually 5 | for new incoming messages. Sets the LoRa radio's Sync Word. 6 | 7 | Spreading factor is basically the radio's network ID. Radios with different 8 | Sync Words will not receive each other's transmissions. This is one way you 9 | can filter out radios you want to ignore, without making an addressing scheme. 10 | 11 | See the Semtech datasheet, http://www.semtech.com/images/datasheet/sx1276.pdf 12 | for more on Sync Word. 13 | 14 | created 28 April 2017 15 | by Tom Igoe 16 | */ 17 | #include // include libraries 18 | #include 19 | const int csPin = 7; // LoRa radio chip select 20 | const int resetPin = 6; // LoRa radio reset 21 | const int irqPin = 1; // change for your board; must be a hardware interrupt pin 22 | 23 | byte msgCount = 0; // count of outgoing messages 24 | int interval = 2000; // interval between sends 25 | long lastSendTime = 0; // time of last packet send 26 | 27 | void setup() { 28 | Serial.begin(115200); // initialize serial 29 | while (!Serial); 30 | 31 | Serial.println("LoRa Duplex - Set sync word"); 32 | 33 | // override the default CS, reset, and IRQ pins (optional) 34 | LoRa.setPins(csPin, resetPin, irqPin);// set CS, reset, IRQ pin 35 | 36 | if (!LoRa.begin(868E6)) { // initialize ratio at 915 MHz 37 | Serial.println("LoRa init failed. Check your connections."); 38 | while (true); // if failed, do nothing 39 | } 40 | 41 | LoRa.setSyncWord(0xF3); // ranges from 0-0xFF, default 0x34, see API docs 42 | Serial.println("LoRa init succeeded."); 43 | } 44 | 45 | void loop() { 46 | if (millis() - lastSendTime > interval) { 47 | String message = "HeLoRa World! "; // send a message 48 | message += msgCount; 49 | sendMessage(message); 50 | Serial.println("Sending " + message); 51 | lastSendTime = millis(); // timestamp the message 52 | interval = random(2000) + 1000; // 2-3 seconds 53 | msgCount++; 54 | } 55 | 56 | // parse for a packet, and call onReceive with the result: 57 | onReceive(LoRa.parsePacket()); 58 | } 59 | 60 | void sendMessage(String outgoing) { 61 | LoRa.beginPacket(); // start packet 62 | LoRa.print(outgoing); // add payload 63 | LoRa.endPacket(); // finish packet and send it 64 | msgCount++; // increment message ID 65 | } 66 | 67 | void onReceive(int packetSize) { 68 | if (packetSize == 0) return; // if there's no packet, return 69 | 70 | // read packet header bytes: 71 | String incoming = ""; 72 | 73 | while (LoRa.available()) { 74 | incoming += (char)LoRa.read(); 75 | } 76 | 77 | Serial.println("Message: " + incoming); 78 | Serial.println("RSSI: " + String(LoRa.packetRssi())); 79 | Serial.println("Snr: " + String(LoRa.packetSnr())); 80 | Serial.println(); 81 | } 82 | 83 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For LoRa 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | LoRa KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | begin KEYWORD2 16 | end KEYWORD2 17 | 18 | beginPacket KEYWORD2 19 | endPacket KEYWORD2 20 | 21 | parsePacket KEYWORD2 22 | packetRssi KEYWORD2 23 | packetSnr KEYWORD2 24 | 25 | write KEYWORD2 26 | 27 | available KEYWORD2 28 | read KEYWORD2 29 | peek KEYWORD2 30 | flush KEYWORD2 31 | 32 | onReceive KEYWORD2 33 | receive KEYWORD2 34 | idle KEYWORD2 35 | sleep KEYWORD2 36 | 37 | setTxPower KEYWORD2 38 | setFrequency KEYWORD2 39 | setSpreadingFactor KEYWORD2 40 | setSignalBandwidth KEYWORD2 41 | setCodingRate4 KEYWORD2 42 | setPreambleLength KEYWORD2 43 | setSyncWord KEYWORD2 44 | enableCrc KEYWORD2 45 | disableCrc KEYWORD2 46 | 47 | random KEYWORD2 48 | setPins KEYWORD2 49 | setSPIFrequency KEYWORD2 50 | dumpRegisters KEYWORD2 51 | 52 | ####################################### 53 | # Constants (LITERAL1) 54 | ####################################### 55 | 56 | PA_OUTPUT_RFO_PIN LITERAL1 57 | PA_OUTPUT_PA_BOOST_PIN LITERAL1 58 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=LoRa_STM32 2 | version=0.3.0 3 | author=Sandeep Mistry 4 | maintainer=Sandeep Mistry 5 | sentence=An Arduino library for sending and receiving data using LoRa radios. 6 | paragraph=Supports Semtech SX1276/77/78/79 based boards/shields. 7 | category=Communication 8 | url=https://github.com/sandeepmistry/arduino-LoRa 9 | architectures=* 10 | includes=LoRa_STM32.h 11 | -------------------------------------------------------------------------------- /src/LoRa_STM32.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Sandeep Mistry. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #include 5 | 6 | // registers 7 | #define REG_FIFO 0x00 8 | #define REG_OP_MODE 0x01 9 | #define REG_FRF_MSB 0x06 10 | #define REG_FRF_MID 0x07 11 | #define REG_FRF_LSB 0x08 12 | #define REG_PA_CONFIG 0x09 13 | #define REG_LNA 0x0c 14 | #define REG_FIFO_ADDR_PTR 0x0d 15 | #define REG_FIFO_TX_BASE_ADDR 0x0e 16 | #define REG_FIFO_RX_BASE_ADDR 0x0f 17 | #define REG_FIFO_RX_CURRENT_ADDR 0x10 18 | #define REG_IRQ_FLAGS 0x12 19 | #define REG_RX_NB_BYTES 0x13 20 | #define REG_PKT_SNR_VALUE 0x19 21 | #define REG_PKT_RSSI_VALUE 0x1a 22 | #define REG_MODEM_CONFIG_1 0x1d 23 | #define REG_MODEM_CONFIG_2 0x1e 24 | #define REG_PREAMBLE_MSB 0x20 25 | #define REG_PREAMBLE_LSB 0x21 26 | #define REG_PAYLOAD_LENGTH 0x22 27 | #define REG_MODEM_CONFIG_3 0x26 28 | #define REG_RSSI_WIDEBAND 0x2c 29 | #define REG_DETECTION_OPTIMIZE 0x31 30 | #define REG_DETECTION_THRESHOLD 0x37 31 | #define REG_SYNC_WORD 0x39 32 | #define REG_DIO_MAPPING_1 0x40 33 | #define REG_VERSION 0x42 34 | 35 | // modes 36 | #define MODE_LONG_RANGE_MODE 0x80 37 | #define MODE_SLEEP 0x00 38 | #define MODE_STDBY 0x01 39 | #define MODE_TX 0x03 40 | #define MODE_RX_CONTINUOUS 0x05 41 | #define MODE_RX_SINGLE 0x06 42 | 43 | // PA config 44 | #define PA_BOOST 0x80 45 | 46 | // IRQ masks 47 | #define IRQ_TX_DONE_MASK 0x08 48 | #define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20 49 | #define IRQ_RX_DONE_MASK 0x40 50 | 51 | #define MAX_PKT_LENGTH 255 52 | 53 | LoRaClass::LoRaClass() : 54 | _spiSettings(8E6, MSBFIRST, SPI_MODE0), 55 | _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), 56 | _frequency(0), 57 | _packetIndex(0), 58 | _implicitHeaderMode(0), 59 | _onReceive(NULL) 60 | { 61 | // overide Stream timeout value 62 | setTimeout(0); 63 | } 64 | 65 | int LoRaClass::begin(long frequency) 66 | { 67 | // setup pins 68 | pinMode(_ss, OUTPUT); 69 | // set SS high 70 | digitalWrite(_ss, HIGH); 71 | 72 | if (_reset != -1) { 73 | pinMode(_reset, OUTPUT); 74 | 75 | // perform reset 76 | digitalWrite(_reset, LOW); 77 | delay(10); 78 | digitalWrite(_reset, HIGH); 79 | delay(10); 80 | } 81 | 82 | // start SPI 83 | SPI.begin(); 84 | 85 | // check version 86 | uint8_t version = readRegister(REG_VERSION); 87 | if (version != 0x12) { 88 | return 0; 89 | } 90 | 91 | // put in sleep mode 92 | sleep(); 93 | 94 | // set frequency 95 | setFrequency(frequency); 96 | 97 | // set base addresses 98 | writeRegister(REG_FIFO_TX_BASE_ADDR, 0); 99 | writeRegister(REG_FIFO_RX_BASE_ADDR, 0); 100 | 101 | // set LNA boost 102 | writeRegister(REG_LNA, readRegister(REG_LNA) | 0x03); 103 | 104 | // set auto AGC 105 | writeRegister(REG_MODEM_CONFIG_3, 0x04); 106 | 107 | // set output power to 17 dBm 108 | setTxPower(20); 109 | 110 | // put in standby mode 111 | idle(); 112 | 113 | return 1; 114 | } 115 | 116 | void LoRaClass::end() 117 | { 118 | // put in sleep mode 119 | sleep(); 120 | 121 | // stop SPI 122 | SPI.end(); 123 | } 124 | 125 | int LoRaClass::beginPacket(int implicitHeader) 126 | { 127 | // put in standby mode 128 | idle(); 129 | 130 | if (implicitHeader) { 131 | implicitHeaderMode(); 132 | } else { 133 | explicitHeaderMode(); 134 | } 135 | 136 | // reset FIFO address and paload length 137 | writeRegister(REG_FIFO_ADDR_PTR, 0); 138 | writeRegister(REG_PAYLOAD_LENGTH, 0); 139 | 140 | return 1; 141 | } 142 | 143 | int LoRaClass::endPacket() 144 | { 145 | // put in TX mode 146 | writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX); 147 | 148 | // wait for TX done 149 | while ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0) { 150 | yield(); 151 | } 152 | 153 | // clear IRQ's 154 | writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK); 155 | 156 | return 1; 157 | } 158 | 159 | int LoRaClass::parsePacket(int size) 160 | { 161 | int packetLength = 0; 162 | int irqFlags = readRegister(REG_IRQ_FLAGS); 163 | 164 | if (size > 0) { 165 | implicitHeaderMode(); 166 | 167 | writeRegister(REG_PAYLOAD_LENGTH, size & 0xff); 168 | } else { 169 | explicitHeaderMode(); 170 | } 171 | 172 | // clear IRQ's 173 | writeRegister(REG_IRQ_FLAGS, irqFlags); 174 | 175 | if ((irqFlags & IRQ_RX_DONE_MASK) && (irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { 176 | // received a packet 177 | _packetIndex = 0; 178 | 179 | // read packet length 180 | if (_implicitHeaderMode) { 181 | packetLength = readRegister(REG_PAYLOAD_LENGTH); 182 | } else { 183 | packetLength = readRegister(REG_RX_NB_BYTES); 184 | } 185 | 186 | // set FIFO address to current RX address 187 | writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR)); 188 | 189 | // put in standby mode 190 | idle(); 191 | } else if (readRegister(REG_OP_MODE) != (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE)) { 192 | // not currently in RX mode 193 | 194 | // reset FIFO address 195 | writeRegister(REG_FIFO_ADDR_PTR, 0); 196 | 197 | // put in single RX mode 198 | writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE); 199 | } 200 | 201 | return packetLength; 202 | } 203 | 204 | int LoRaClass::packetRssi() 205 | { 206 | return (readRegister(REG_PKT_RSSI_VALUE) - (_frequency < 868E6 ? 164 : 157)); 207 | } 208 | 209 | float LoRaClass::packetSnr() 210 | { 211 | return ((int8_t)readRegister(REG_PKT_SNR_VALUE)) * 0.25; 212 | } 213 | 214 | size_t LoRaClass::write(uint8_t byte) 215 | { 216 | return write(&byte, sizeof(byte)); 217 | } 218 | 219 | size_t LoRaClass::write(const uint8_t *buffer, size_t size) 220 | { 221 | int currentLength = readRegister(REG_PAYLOAD_LENGTH); 222 | 223 | // check size 224 | if ((currentLength + size) > MAX_PKT_LENGTH) { 225 | size = MAX_PKT_LENGTH - currentLength; 226 | } 227 | 228 | // write data 229 | for (size_t i = 0; i < size; i++) { 230 | writeRegister(REG_FIFO, buffer[i]); 231 | } 232 | 233 | // update length 234 | writeRegister(REG_PAYLOAD_LENGTH, currentLength + size); 235 | 236 | return size; 237 | } 238 | 239 | int LoRaClass::available() 240 | { 241 | return (readRegister(REG_RX_NB_BYTES) - _packetIndex); 242 | } 243 | 244 | int LoRaClass::read() 245 | { 246 | if (!available()) { 247 | return -1; 248 | } 249 | 250 | _packetIndex++; 251 | 252 | return readRegister(REG_FIFO); 253 | } 254 | 255 | int LoRaClass::peek() 256 | { 257 | if (!available()) { 258 | return -1; 259 | } 260 | 261 | // store current FIFO address 262 | int currentAddress = readRegister(REG_FIFO_ADDR_PTR); 263 | 264 | // read 265 | uint8_t b = readRegister(REG_FIFO); 266 | 267 | // restore FIFO address 268 | writeRegister(REG_FIFO_ADDR_PTR, currentAddress); 269 | 270 | return b; 271 | } 272 | 273 | void LoRaClass::flush() 274 | { 275 | } 276 | 277 | void LoRaClass::onReceive(void(*callback)(int)) 278 | { 279 | _onReceive = callback; 280 | 281 | if (callback) { 282 | writeRegister(REG_DIO_MAPPING_1, 0x00); 283 | 284 | attachInterrupt(_dio0, LoRaClass::onDio0Rise, RISING); 285 | } else { 286 | detachInterrupt(_dio0); 287 | } 288 | } 289 | 290 | void LoRaClass::receive(int size) 291 | { 292 | if (size > 0) { 293 | implicitHeaderMode(); 294 | 295 | writeRegister(REG_PAYLOAD_LENGTH, size & 0xff); 296 | } else { 297 | explicitHeaderMode(); 298 | } 299 | 300 | writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS); 301 | } 302 | 303 | void LoRaClass::idle() 304 | { 305 | writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY); 306 | } 307 | 308 | void LoRaClass::sleep() 309 | { 310 | writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP); 311 | } 312 | 313 | void LoRaClass::setTxPower(int level, int outputPin) 314 | { 315 | if (PA_OUTPUT_RFO_PIN == outputPin) { 316 | // RFO 317 | if (level < 0) { 318 | level = 0; 319 | } else if (level > 14) { 320 | level = 14; 321 | } 322 | 323 | writeRegister(REG_PA_CONFIG, 0x70 | level); 324 | } else { 325 | // PA BOOST 326 | if (level < 2) { 327 | level = 2; 328 | } else if (level > 17) { 329 | level = 17; 330 | } 331 | 332 | writeRegister(REG_PA_CONFIG, PA_BOOST | (level - 2)); 333 | } 334 | } 335 | 336 | void LoRaClass::setFrequency(long frequency) 337 | { 338 | _frequency = frequency; 339 | 340 | uint64_t frf = ((uint64_t)frequency << 19) / 32000000; 341 | 342 | writeRegister(REG_FRF_MSB, (uint8_t)(frf >> 16)); 343 | writeRegister(REG_FRF_MID, (uint8_t)(frf >> 8)); 344 | writeRegister(REG_FRF_LSB, (uint8_t)(frf >> 0)); 345 | } 346 | 347 | void LoRaClass::setSpreadingFactor(int sf) 348 | { 349 | if (sf < 6) { 350 | sf = 6; 351 | } else if (sf > 12) { 352 | sf = 12; 353 | } 354 | 355 | if (sf == 6) { 356 | writeRegister(REG_DETECTION_OPTIMIZE, 0xc5); 357 | writeRegister(REG_DETECTION_THRESHOLD, 0x0c); 358 | } else { 359 | writeRegister(REG_DETECTION_OPTIMIZE, 0xc3); 360 | writeRegister(REG_DETECTION_THRESHOLD, 0x0a); 361 | } 362 | 363 | writeRegister(REG_MODEM_CONFIG_2, (readRegister(REG_MODEM_CONFIG_2) & 0x0f) | ((sf << 4) & 0xf0)); 364 | } 365 | 366 | void LoRaClass::setSignalBandwidth(long sbw) 367 | { 368 | int bw; 369 | 370 | if (sbw <= 7.8E3) { 371 | bw = 0; 372 | } else if (sbw <= 10.4E3) { 373 | bw = 1; 374 | } else if (sbw <= 15.6E3) { 375 | bw = 2; 376 | } else if (sbw <= 20.8E3) { 377 | bw = 3; 378 | } else if (sbw <= 31.25E3) { 379 | bw = 4; 380 | } else if (sbw <= 41.7E3) { 381 | bw = 5; 382 | } else if (sbw <= 62.5E3) { 383 | bw = 6; 384 | } else if (sbw <= 125E3) { 385 | bw = 7; 386 | } else if (sbw <= 250E3) { 387 | bw = 8; 388 | } else /*if (sbw <= 250E3)*/ { 389 | bw = 9; 390 | } 391 | 392 | writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0x0f) | (bw << 4)); 393 | } 394 | 395 | void LoRaClass::setCodingRate4(int denominator) 396 | { 397 | if (denominator < 5) { 398 | denominator = 5; 399 | } else if (denominator > 8) { 400 | denominator = 8; 401 | } 402 | 403 | int cr = denominator - 4; 404 | 405 | writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0xf1) | (cr << 1)); 406 | } 407 | 408 | void LoRaClass::setPreambleLength(long length) 409 | { 410 | writeRegister(REG_PREAMBLE_MSB, (uint8_t)(length >> 8)); 411 | writeRegister(REG_PREAMBLE_LSB, (uint8_t)(length >> 0)); 412 | } 413 | 414 | void LoRaClass::setSyncWord(int sw) 415 | { 416 | writeRegister(REG_SYNC_WORD, sw); 417 | } 418 | 419 | void LoRaClass::enableCrc() 420 | { 421 | writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) | 0x04); 422 | } 423 | 424 | void LoRaClass::disableCrc() 425 | { 426 | writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) & 0xfb); 427 | } 428 | 429 | byte LoRaClass::random() 430 | { 431 | return readRegister(REG_RSSI_WIDEBAND); 432 | } 433 | 434 | void LoRaClass::setPins(int ss, int reset, int dio0) 435 | { 436 | _ss = ss; 437 | _reset = reset; 438 | _dio0 = dio0; 439 | } 440 | 441 | void LoRaClass::setSPIFrequency(uint32_t frequency) 442 | { 443 | _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); 444 | } 445 | 446 | void LoRaClass::dumpRegisters(Stream& out) 447 | { 448 | for (int i = 0; i < 128; i++) { 449 | out.print("0x"); 450 | out.print(i, HEX); 451 | out.print(": 0x"); 452 | out.println(readRegister(i), HEX); 453 | } 454 | } 455 | 456 | void LoRaClass::explicitHeaderMode() 457 | { 458 | _implicitHeaderMode = 0; 459 | 460 | writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) & 0xfe); 461 | } 462 | 463 | void LoRaClass::implicitHeaderMode() 464 | { 465 | _implicitHeaderMode = 1; 466 | 467 | writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) | 0x01); 468 | } 469 | 470 | void LoRaClass::handleDio0Rise() 471 | { 472 | int irqFlags = readRegister(REG_IRQ_FLAGS); 473 | 474 | // clear IRQ's 475 | writeRegister(REG_IRQ_FLAGS, irqFlags); 476 | 477 | if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { 478 | // received a packet 479 | _packetIndex = 0; 480 | 481 | // read packet length 482 | int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH) : readRegister(REG_RX_NB_BYTES); 483 | 484 | // set FIFO address to current RX address 485 | writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR)); 486 | 487 | if (_onReceive) { 488 | _onReceive(packetLength); 489 | } 490 | 491 | // reset FIFO address 492 | writeRegister(REG_FIFO_ADDR_PTR, 0); 493 | } 494 | } 495 | 496 | uint8_t LoRaClass::readRegister(uint8_t address) 497 | { 498 | return singleTransfer(address & 0x7f, 0x00); 499 | } 500 | 501 | void LoRaClass::writeRegister(uint8_t address, uint8_t value) 502 | { 503 | singleTransfer(address | 0x80, value); 504 | } 505 | 506 | uint8_t LoRaClass::singleTransfer(uint8_t address, uint8_t value) 507 | { 508 | uint8_t response; 509 | 510 | digitalWrite(_ss, LOW); 511 | 512 | SPI.beginTransaction(_spiSettings); 513 | SPI.transfer(address); 514 | response = SPI.transfer(value); 515 | SPI.endTransaction(); 516 | 517 | digitalWrite(_ss, HIGH); 518 | 519 | return response; 520 | } 521 | 522 | void LoRaClass::onDio0Rise() 523 | { 524 | LoRa.handleDio0Rise(); 525 | } 526 | 527 | LoRaClass LoRa; 528 | -------------------------------------------------------------------------------- /src/LoRa_STM32.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Sandeep Mistry. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #ifndef LORA_H 5 | #define LORA_H 6 | 7 | #include 8 | #include 9 | 10 | #define LORA_DEFAULT_SS_PIN PA4 11 | #define LORA_DEFAULT_RESET_PIN PC13 12 | #define LORA_DEFAULT_DIO0_PIN PA1 13 | 14 | #define PA_OUTPUT_RFO_PIN 0 15 | #define PA_OUTPUT_PA_BOOST_PIN 1 16 | 17 | class LoRaClass : public Stream { 18 | public: 19 | LoRaClass(); 20 | 21 | int begin(long frequency); 22 | void end(); 23 | 24 | int beginPacket(int implicitHeader = false); 25 | int endPacket(); 26 | 27 | int parsePacket(int size = 0); 28 | int packetRssi(); 29 | float packetSnr(); 30 | 31 | // from Print 32 | virtual size_t write(uint8_t byte); 33 | virtual size_t write(const uint8_t *buffer, size_t size); 34 | 35 | // from Stream 36 | virtual int available(); 37 | virtual int read(); 38 | virtual int peek(); 39 | virtual void flush(); 40 | 41 | void onReceive(void(*callback)(int)); 42 | 43 | void receive(int size = 0); 44 | void idle(); 45 | void sleep(); 46 | 47 | void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); 48 | void setFrequency(long frequency); 49 | void setSpreadingFactor(int sf); 50 | void setSignalBandwidth(long sbw); 51 | void setCodingRate4(int denominator); 52 | void setPreambleLength(long length); 53 | void setSyncWord(int sw); 54 | void enableCrc(); 55 | void disableCrc(); 56 | 57 | // deprecated 58 | void crc() { enableCrc(); } 59 | void noCrc() { disableCrc(); } 60 | 61 | byte random(); 62 | 63 | void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN); 64 | void setSPIFrequency(uint32_t frequency); 65 | 66 | void dumpRegisters(Stream& out); 67 | 68 | private: 69 | void explicitHeaderMode(); 70 | void implicitHeaderMode(); 71 | 72 | void handleDio0Rise(); 73 | 74 | uint8_t readRegister(uint8_t address); 75 | void writeRegister(uint8_t address, uint8_t value); 76 | uint8_t singleTransfer(uint8_t address, uint8_t value); 77 | 78 | static void onDio0Rise(); 79 | 80 | private: 81 | SPISettings _spiSettings; 82 | int _ss; 83 | int _reset; 84 | int _dio0; 85 | int _frequency; 86 | int _packetIndex; 87 | int _implicitHeaderMode; 88 | void (*_onReceive)(int); 89 | }; 90 | 91 | extern LoRaClass LoRa; 92 | 93 | #endif 94 | --------------------------------------------------------------------------------