diff --git a/Examples/LoRaTests/Client/Client.ino b/Examples/LoRaTests/Client/Client.ino new file mode 100644 index 0000000..e72c72e --- /dev/null +++ b/Examples/LoRaTests/Client/Client.ino @@ -0,0 +1,52 @@ +#include +#include + +// The dragino shield +#define PIN_SS 10 +#define PIN_RESET 9 +#define PIN_DIO0 2 + +// Wemos +//#define PIN_SS 15 +//#define PIN_RESET 4 +//#define PIN_DIO0 5 + +// Custom: The smart greenhouse device +//#define PIN_SS 10 +//#define PIN_RESET -1 +//#define PIN_DIO0 2 + + +// Channel 0 +#define FREQUENCY 868100000 + + +int counter = 0; + +void setup() { + Serial.begin(9600); + while (!Serial); + + Serial.println("LoRa Sender"); + + LoRa.setPins(PIN_SS, PIN_RESET, PIN_DIO0); + if (!LoRa.begin(FREQUENCY)) { + Serial.println("Starting LoRa failed!"); + while (1); + } +} + +void loop() { + Serial.print("Sending packet: "); + Serial.println(counter); + + // send packet + LoRa.beginPacket(); + LoRa.print("hello "); + LoRa.print(counter); + LoRa.endPacket(); + + counter++; + + delay(5000); +} diff --git a/Examples/LoRaTests/README.md b/Examples/LoRaTests/README.md new file mode 100644 index 0000000..d1add90 --- /dev/null +++ b/Examples/LoRaTests/README.md @@ -0,0 +1,8 @@ +# RFM95 LoRa Test scripts + +> Tests a pair fo RFM95 chips to ensure they work. + +## Getting started +To use, copy the directories in the `lib` directory to `~/Arduino/libraries`. + +Then, open the sketch in the Arduino IDE. diff --git a/Examples/LoRaTests/Server/Server.ino b/Examples/LoRaTests/Server/Server.ino new file mode 100644 index 0000000..1a29f2a --- /dev/null +++ b/Examples/LoRaTests/Server/Server.ino @@ -0,0 +1,57 @@ +#include +#include + +// The dragino shield +//#define PIN_SS 10 +//#define PIN_RESET 9 +//#define PIN_DIO0 2 + +// Custom: The smart greenhouse device +//#define PIN_SS 10 +//#define PIN_RESET -1 +//#define PIN_DIO0 2 + +// Wemos +#define PIN_SS 15 +#define PIN_RESET 4 +#define PIN_DIO0 5 + +// Channel 0 +#define FREQUENCY 868100000 + + + +void setup() { + Serial.begin(9600); + while (!Serial); + + Serial.println("LoRa Receiver"); + //LoRa.enableInvertIQ(); + LoRa.setPins(PIN_SS, PIN_RESET, PIN_DIO0); + if (!LoRa.begin(FREQUENCY)) { + Serial.println("Starting LoRa failed!"); + while (1); + } + LoRa.onReceive(onReceive); + LoRa.receive(); + Serial.println("Listening for transmissions."); +} + +void loop() { +} + + + +void onReceive(int packetSize) { + // received a packet + Serial.print("Received packet '"); + + // read packet + while (LoRa.available()) { + Serial.print((char)LoRa.read()); + } + + // print RSSI of packet + Serial.print("' with RSSI "); + Serial.println(LoRa.packetRssi()); +} diff --git a/Examples/LoRaTests/lib/arduino-LoRa/.travis.yml b/Examples/LoRaTests/lib/arduino-LoRa/.travis.yml new file mode 100644 index 0000000..ae7a782 --- /dev/null +++ b/Examples/LoRaTests/lib/arduino-LoRa/.travis.yml @@ -0,0 +1,38 @@ +language: generic +env: + global: + - IDE_VERSION=1.8.2 + matrix: + - BOARD="arduino:avr:uno" + - BOARD="arduino:avr:micro" + - BOARD="arduino:avr:mega:cpu=atmega2560" + - BOARD="arduino:samd:arduino_zero_edbg" + - BOARD="arduino:samd:mkr1000" + - BOARD="arduino:samd:mkrzero" + - BOARD="arduino:samd:mkrwan1300" +before_install: + - wget http://downloads.arduino.cc/arduino-$IDE_VERSION-linux64.tar.xz + - tar xf arduino-$IDE_VERSION-linux64.tar.xz + - mv arduino-$IDE_VERSION $HOME/arduino-ide + - export PATH=$PATH:$HOME/arduino-ide + - if [[ "$BOARD" =~ "arduino:samd:" ]]; then + arduino --install-boards arduino:samd &> /dev/null; + fi + - buildExampleSketch() { arduino --verbose-build --verify --board $BOARD $PWD/examples/$1/$1.ino; } +install: + - mkdir -p $HOME/Arduino/libraries + - ln -s $PWD $HOME/Arduino/libraries/LoRa +script: + - buildExampleSketch LoRaDumpRegisters + - buildExampleSketch LoRaDuplex + - if [[ "$BOARD" != "arduino:samd:mkrwan1300" ]]; then + buildExampleSketch LoRaDuplexCallback; + fi + - buildExampleSketch LoRaReceiver + - if [[ "$BOARD" != "arduino:samd:mkrwan1300" ]]; then + buildExampleSketch LoRaReceiverCallback; + fi + - buildExampleSketch LoRaSender + - buildExampleSketch LoRaSenderNonBlocking + - buildExampleSketch LoRaSetSpread + - buildExampleSketch LoRaSetSyncWord diff --git a/Examples/LoRaTests/lib/arduino-LoRa/API.md b/Examples/LoRaTests/lib/arduino-LoRa/API.md new file mode 100644 index 0000000..ded3b9c --- /dev/null +++ b/Examples/LoRaTests/lib/arduino-LoRa/API.md @@ -0,0 +1,355 @@ +# LoRa API + +## Include Library + +```arduino +#include +``` + +## Setup + +### Begin + +Initialize the library with the specified frequency. + +```arduino +LoRa.begin(frequency); +``` + * `frequency` - frequency in Hz (`433E6`, `866E6`, `915E6`) + +Returns `1` on success, `0` on failure. + +### Set pins + +Override the default `NSS`, `NRESET`, and `DIO0` pins used by the library. **Must** be called before `LoRa.begin()`. + +```arduino +LoRa.setPins(ss, reset, dio0); +``` + * `ss` - new slave select pin to use, defaults to `10` + * `reset` - new reset pin to use, defaults to `9` + * `dio0` - new DIO0 pin to use, defaults to `2`. **Must** be interrupt capable via [attachInterrupt(...)](https://www.arduino.cc/en/Reference/AttachInterrupt). + +This call is optional and only needs to be used if you need to change the default pins used. + +#### No MCU controlled reset pin + +To save further pins one could connect the reset pin of the MCU with reset pin of the radio thus resetting only during startup. + +* `reset` - set to `-1` to omit this pin + +### Set SPI interface + +Override the default SPI interface used by the library. **Must** be called before `LoRa.begin()`. + +```arduino +LoRa.setSPI(spi); +``` + * `spi` - new SPI interface to use, defaults to `SPI` + +This call is optional and only needs to be used if you need to change the default SPI interface used, in the case your Arduino (or compatible) board has more than one SPI interface present. + +### Set SPI Frequency + +Override the default SPI frequency of 10 MHz used by the library. **Must** be called before `LoRa.begin()`. + +```arduino +LoRa.setSPIFrequency(frequency); +``` + * `frequency` - new SPI frequency to use, defaults to `8E6` + +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)`. + +### End + +Stop the library + +```arduino +LoRa.end() +``` + +## Sending data + +### Begin packet + +Start the sequence of sending a packet. + +```arduino +LoRa.beginPacket(); + +LoRa.beginPacket(implicitHeader); +``` + + * `implicitHeader` - (optional) `true` enables implicit header mode, `false` enables explicit header mode (default) + +Returns `1` if radio is ready to transmit, `0` if busy or on failure. + +### Writing + +Write data to the packet. Each packet can contain up to 255 bytes. + +```arduino +LoRa.write(byte); + +LoRa.write(buffer, length); +``` +* `byte` - single byte to write to packet + +or + +* `buffer` - data to write to packet +* `length` - size of data to write + +Returns the number of bytes written. + +**Note:** Other Arduino `Print` API's can also be used to write data into the packet + +### End packet + +End the sequence of sending a packet. + +```arduino +LoRa.endPacket(); + +LoRa.endPacket(async); +``` + * `async` - (optional) `true` enables non-blocking mode, `false` waits for transmission to be completed (default) + +Returns `1` on success, `0` on failure. + +## Receiving data + +### Parsing packet + +Check if a packet has been received. + +```arduino +int packetSize = LoRa.parsePacket(); + +int packetSize = LoRa.parsePacket(size); +``` + + * `size` - (optional) if `> 0` implicit header mode is enabled with the expected a packet of `size` bytes, default mode is explicit header mode + + +Returns the packet size in bytes or `0` if no packet was received. + +### Continuous receive mode + +**WARNING**: Not supported on the Arduino MKR WAN 1300 board! + +#### Register callback + +Register a callback function for when a packet is received. + +```arduino +LoRa.onReceive(onReceive); + +void onReceive(int packetSize) { + // ... +} +``` + + * `onReceive` - function to call when a packet is received. + +#### Receive mode + +Puts the radio in continuous receive mode. + +```arduino +LoRa.receive(); + +LoRa.receive(int size); +``` + + * `size` - (optional) if `> 0` implicit header mode is enabled with the expected a packet of `size` bytes, default mode is explicit header mode + +The `onReceive` callback will be called when a packet is received. + +### Packet RSSI + +```arduino +int rssi = LoRa.packetRssi(); +``` + +Returns the RSSI of the received packet. + +### Packet SNR + +```arduino +float snr = LoRa.packetSnr(); +``` + +Returns the estimated SNR of the received packet in dB. + +### Packet Frequency Error + +```arduino +long freqErr = LoRa.packetFrequencyError(); +``` + +Returns the frequency error of the received packet in Hz. The frequency error is the frequency offset between the receiver centre frequency and that of an incoming LoRa signal. + +### Available + +```arduino +int availableBytes = LoRa.available() +``` + +Returns number of bytes available for reading. + +### Peeking + +Peek at the next byte in the packet. + +```arduino +byte b = LoRa.peek(); +``` + +Returns the next byte in the packet or `-1` if no bytes are available. + +### Reading + +Read the next byte from the packet. + +```arduino +byte b = LoRa.read(); +``` + +Returns the next byte in the packet or `-1` if no bytes are available. + +**Note:** Other Arduino [`Stream` API's](https://www.arduino.cc/en/Reference/Stream) can also be used to read data from the packet + +## Other radio modes + +### Idle mode + +Put the radio in idle (standby) mode. + +```arduino +LoRa.idle(); +``` + +### Sleep mode + +Put the radio in sleep mode. + +```arduino +LoRa.sleep(); +``` + +## Radio parameters + +### TX Power + +Change the TX power of the radio. + +```arduino +LoRa.setTxPower(txPower); + +LoRa.setTxPower(txPower, outputPin); +``` + * `txPower` - TX power in dB, defaults to `17` + * `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`. + +Supported values are `2` to `20` for `PA_OUTPUT_PA_BOOST_PIN`, and `0` to `14` for `PA_OUTPUT_RFO_PIN`. + +Most modules have the PA output pin connected to PA BOOST, + +### Frequency + +Change the frequency of the radio. + +```arduino +LoRa.setFrequency(frequency); +``` + * `frequency` - frequency in Hz (`433E6`, `866E6`, `915E6`) + +### Spreading Factor + +Change the spreading factor of the radio. + +```arduino +LoRa.setSpreadingFactor(spreadingFactor); +``` + * `spreadingFactor` - spreading factor, defaults to `7` + +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. + +### Signal Bandwidth + +Change the signal bandwidth of the radio. + +```arduino +LoRa.setSignalBandwidth(signalBandwidth); +``` + + * `signalBandwidth` - signal bandwidth in Hz, defaults to `125E3`. + +Supported values are `7.8E3`, `10.4E3`, `15.6E3`, `20.8E3`, `31.25E3`, `41.7E3`, `62.5E3`, `125E3`, and `250E3`. + +### Coding Rate + +Change the coding rate of the radio. + +```arduino +LoRa.setCodingRate4(codingRateDenominator); +``` + + * `codingRateDenominator` - denominator of the coding rate, defaults to `5` + +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`. + +### Preamble Length + +Change the preamble length of the radio. + +```arduino +LoRa.setPreambleLength(preambleLength); +``` + + * `preambleLength` - preamble length in symbols, defaults to `8` + +Supported values are between `6` and `65535`. + +### Sync Word + +Change the sync word of the radio. + +```arduino +LoRa.setSyncWord(syncWord); +``` + + * `syncWord` - byte value to use as the sync word, defaults to `0x12` + +### CRC + +Enable or disable CRC usage, by default a CRC is not used. + +```arduino +LoRa.enableCrc(); + +LoRa.disableCrc(); +``` + +### Invert IQ Signals + +Enable or disable Invert the LoRa I and Q signals, by default a invertIQ is not used. + +```arduino +LoRa.enableInvertIQ(); + +LoRa.disableInvertIQ(); +``` + +## Other functions + +### Random + +Generate a random byte, based on the Wideband RSSI measurement. + +``` +byte b = LoRa.random(); +``` + +Returns random byte. diff --git a/Examples/LoRaTests/lib/arduino-LoRa/LICENSE b/Examples/LoRaTests/lib/arduino-LoRa/LICENSE new file mode 100644 index 0000000..1e85b95 --- /dev/null +++ b/Examples/LoRaTests/lib/arduino-LoRa/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Sandeep Mistry + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Examples/LoRaTests/lib/arduino-LoRa/README.md b/Examples/LoRaTests/lib/arduino-LoRa/README.md new file mode 100644 index 0000000..c89956a --- /dev/null +++ b/Examples/LoRaTests/lib/arduino-LoRa/README.md @@ -0,0 +1,91 @@ +# Arduino LoRa + +[![Build Status](https://travis-ci.org/sandeepmistry/arduino-LoRa.svg?branch=master)](https://travis-ci.org/sandeepmistry/arduino-LoRa) + +An [Arduino](https://arduino.cc/) library for sending and receiving data using [LoRa](https://www.lora-alliance.org/) radios. + +## Compatible Hardware + + * [Semtech SX1276/77/78/79](http://www.semtech.com/apps/product.php?pn=SX1276) based boards including: + * [Dragino Lora Shield](http://www.dragino.com/products/module/item/102-lora-shield.html) + * [HopeRF](http://www.hoperf.com/rf_transceiver/lora/) [RFM95W](http://www.hoperf.com/rf_transceiver/lora/RFM95W.html), [RFM96W](http://www.hoperf.com/rf_transceiver/lora/RFM96W.html), and [RFM98W](http://www.hoperf.com/rf_transceiver/lora/RFM98W.html) + * [Modtronix](http://modtronix.com/) [inAir4](http://modtronix.com/inair4.html), [inAir9](http://modtronix.com/inair9.html), and [inAir9B](http://modtronix.com/inair9b.html) + * [Arduino MKR WAN 1300](https://store.arduino.cc/usa/mkr-wan-1300) + * **NOTE:** Requires firmware v1.1.6 or later on the on-board Murata module. Please use the [MKRWANFWUpdate_standalone example](https://github.com/arduino-libraries/MKRWAN/blob/master/examples/MKRWANFWUpdate_standalone/MKRWANFWUpdate_standalone.ino) from latest [MKRWAN library](https://github.com/arduino-libraries/MKRWAN) release to update the firmware. + * **WARNING**: [LoRa.onReceive(...)](https://github.com/sandeepmistry/arduino-LoRa/blob/master/API.md#register-callback) and [LoRa.recieve()](https://github.com/sandeepmistry/arduino-LoRa/blob/master/API.md#receive-mode) is not compatible with this board! + +### Semtech SX1276/77/78/79 wiring + +| Semtech SX1276/77/78/79 | Arduino | +| :---------------------: | :------:| +| VCC | 3.3V | +| GND | GND | +| SCK | SCK | +| MISO | MISO | +| MOSI | MOSI | +| NSS | 10 | +| NRESET | 9 | +| DIO0 | 2 | + + +`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). + +**NOTES**: + * Some boards (like the Arduino Nano), cannot supply enough current for the SX127x in TX mode. This will cause lockups when sending, be sure to use an external 3.3V supply that can provide at least 120mA's when using these boards. + * If your Arduino board operates at 5V, like the Arduino Uno, Leonardo or Mega, you will need to use a level converter for the wiring to the Semtech SX127x module. Most Semtech SX127x breakout boards do not have logic level converters built-in. + +## Installation + +### Using the Arduino IDE Library Manager + +1. Choose `Sketch` -> `Include Library` -> `Manage Libraries...` +2. Type `LoRa` into the search box. +3. Click the row to select the library. +4. Click the `Install` button to install the library. + +### Using Git + +```sh +cd ~/Documents/Arduino/libraries/ +git clone https://github.com/sandeepmistry/arduino-LoRa LoRa +``` + +## API + +See [API.md](API.md). + +## Examples + +See [examples](examples) folder. + +## FAQ + +**1) Initilizating the LoRa radio is failing** + +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(...)`. + +**2) Can other radios see the packets I'm sending?** + +Yes, any LoRa radio that are configured with the same radio parameters and in range can see the packets you send. + +**3) Is the data I'm sending encrypted?** + +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. + +**4) How does this library differ from LoRaWAN libraries?** + +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. + +**5) Does this library honor duty cycles?** + +No, you have to manage it by your self. + +**6) Which frequencies can I use?** + +You can use [this table](https://www.thethingsnetwork.org/wiki/LoRaWAN/Frequencies/By-Country) to lookup the available frequencies by your country. The selectable frequency also depends on your hardware. You can lookup the data sheet or ask your supplier. + +Please also notice the frequency dependent duty cycles for legal reasons! + +## License + +This libary is [licensed](LICENSE) under the [MIT Licence](https://en.wikipedia.org/wiki/MIT_License). diff --git a/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaDumpRegisters/LoRaDumpRegisters.ino b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaDumpRegisters/LoRaDumpRegisters.ino new file mode 100644 index 0000000..5a23d2e --- /dev/null +++ b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaDumpRegisters/LoRaDumpRegisters.ino @@ -0,0 +1,30 @@ +/* + LoRa register dump + + This examples shows how to inspect and output the LoRa radio's + registers on the Serial interface +*/ +#include // include libraries +#include + +void setup() { + Serial.begin(9600); // initialize serial + while (!Serial); + + Serial.println("LoRa Dump Registers"); + + // override the default CS, reset, and IRQ pins (optional) + // LoRa.setPins(7, 6, 1); // set CS, reset, IRQ pin + + if (!LoRa.begin(915E6)) { // initialize ratio at 915 MHz + Serial.println("LoRa init failed. Check your connections."); + while (true); // if failed, do nothing + } + + LoRa.dumpRegisters(Serial); +} + + +void loop() { +} + diff --git a/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaDuplex/LoRaDuplex.ino b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaDuplex/LoRaDuplex.ino new file mode 100644 index 0000000..c914254 --- /dev/null +++ b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaDuplex/LoRaDuplex.ino @@ -0,0 +1,106 @@ +/* + LoRa Duplex communication + + Sends a message every half second, and polls continually + for new incoming messages. Implements a one-byte addressing scheme, + with 0xFF as the broadcast address. + + Uses readString() from Stream class to read payload. The Stream class' + timeout may affect other functuons, like the radio's callback. For an + + created 28 April 2017 + by Tom Igoe +*/ +#include // include libraries +#include + +const int csPin = 7; // LoRa radio chip select +const int resetPin = 6; // LoRa radio reset +const int irqPin = 1; // change for your board; must be a hardware interrupt pin + +String outgoing; // outgoing message + +byte msgCount = 0; // count of outgoing messages +byte localAddress = 0xBB; // address of this device +byte destination = 0xFF; // destination to send to +long lastSendTime = 0; // last send time +int interval = 2000; // interval between sends + +void setup() { + Serial.begin(9600); // initialize serial + while (!Serial); + + Serial.println("LoRa Duplex"); + + // override the default CS, reset, and IRQ pins (optional) + LoRa.setPins(csPin, resetPin, irqPin);// set CS, reset, IRQ pin + + if (!LoRa.begin(915E6)) { // initialize ratio at 915 MHz + Serial.println("LoRa init failed. Check your connections."); + while (true); // if failed, do nothing + } + + Serial.println("LoRa init succeeded."); +} + +void loop() { + if (millis() - lastSendTime > interval) { + String message = "HeLoRa World!"; // send a message + sendMessage(message); + Serial.println("Sending " + message); + lastSendTime = millis(); // timestamp the message + interval = random(2000) + 1000; // 2-3 seconds + } + + // parse for a packet, and call onReceive with the result: + onReceive(LoRa.parsePacket()); +} + +void sendMessage(String outgoing) { + LoRa.beginPacket(); // start packet + LoRa.write(destination); // add destination address + LoRa.write(localAddress); // add sender address + LoRa.write(msgCount); // add message ID + LoRa.write(outgoing.length()); // add payload length + LoRa.print(outgoing); // add payload + LoRa.endPacket(); // finish packet and send it + msgCount++; // increment message ID +} + +void onReceive(int packetSize) { + if (packetSize == 0) return; // if there's no packet, return + + // read packet header bytes: + int recipient = LoRa.read(); // recipient address + byte sender = LoRa.read(); // sender address + byte incomingMsgId = LoRa.read(); // incoming msg ID + byte incomingLength = LoRa.read(); // incoming msg length + + String incoming = ""; + + while (LoRa.available()) { + incoming += (char)LoRa.read(); + } + + if (incomingLength != incoming.length()) { // check length for error + Serial.println("error: message length does not match length"); + return; // skip rest of function + } + + // if the recipient isn't this device or broadcast, + if (recipient != localAddress && recipient != 0xFF) { + Serial.println("This message is not for me."); + return; // skip rest of function + } + + // if message is for this device, or broadcast, print details: + Serial.println("Received from: 0x" + String(sender, HEX)); + Serial.println("Sent to: 0x" + String(recipient, HEX)); + Serial.println("Message ID: " + String(incomingMsgId)); + Serial.println("Message length: " + String(incomingLength)); + Serial.println("Message: " + incoming); + Serial.println("RSSI: " + String(LoRa.packetRssi())); + Serial.println("Snr: " + String(LoRa.packetSnr())); + Serial.println(); +} + diff --git a/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaDuplexCallback/LoRaDuplexCallback.ino b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaDuplexCallback/LoRaDuplexCallback.ino new file mode 100644 index 0000000..0511f3e --- /dev/null +++ b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaDuplexCallback/LoRaDuplexCallback.ino @@ -0,0 +1,110 @@ +/* + LoRa Duplex communication wth callback + + Sends a message every half second, and uses callback + for new incoming messages. Implements a one-byte addressing scheme, + with 0xFF as the broadcast address. + + Note: while sending, LoRa radio is not listening for incoming messages. + Note2: when using the callback method, you can't use any of the Stream + functions that rely on the timeout, such as readString, parseInt(), etc. + + created 28 April 2017 + by Tom Igoe +*/ +#include // include libraries +#include + +#ifdef ARDUINO_SAMD_MKRWAN1300 +#error "This example is not compatible with the Arduino MKR WAN 1300 board!" +#endif + +const int csPin = 7; // LoRa radio chip select +const int resetPin = 6; // LoRa radio reset +const int irqPin = 1; // change for your board; must be a hardware interrupt pin + +String outgoing; // outgoing message +byte msgCount = 0; // count of outgoing messages +byte localAddress = 0xBB; // address of this device +byte destination = 0xFF; // destination to send to +long lastSendTime = 0; // last send time +int interval = 2000; // interval between sends + +void setup() { + Serial.begin(9600); // initialize serial + while (!Serial); + + Serial.println("LoRa Duplex with callback"); + + // override the default CS, reset, and IRQ pins (optional) + LoRa.setPins(csPin, resetPin, irqPin);// set CS, reset, IRQ pin + + if (!LoRa.begin(915E6)) { // initialize ratio at 915 MHz + Serial.println("LoRa init failed. Check your connections."); + while (true); // if failed, do nothing + } + + LoRa.onReceive(onReceive); + LoRa.receive(); + Serial.println("LoRa init succeeded."); +} + +void loop() { + if (millis() - lastSendTime > interval) { + String message = "HeLoRa World!"; // send a message + sendMessage(message); + Serial.println("Sending " + message); + lastSendTime = millis(); // timestamp the message + interval = random(2000) + 1000; // 2-3 seconds + LoRa.receive(); // go back into receive mode + } +} + +void sendMessage(String outgoing) { + LoRa.beginPacket(); // start packet + LoRa.write(destination); // add destination address + LoRa.write(localAddress); // add sender address + LoRa.write(msgCount); // add message ID + LoRa.write(outgoing.length()); // add payload length + LoRa.print(outgoing); // add payload + LoRa.endPacket(); // finish packet and send it + msgCount++; // increment message ID +} + +void onReceive(int packetSize) { + if (packetSize == 0) return; // if there's no packet, return + + // read packet header bytes: + int recipient = LoRa.read(); // recipient address + byte sender = LoRa.read(); // sender address + byte incomingMsgId = LoRa.read(); // incoming msg ID + byte incomingLength = LoRa.read(); // incoming msg length + + String incoming = ""; // payload of packet + + while (LoRa.available()) { // can't use readString() in callback, so + incoming += (char)LoRa.read(); // add bytes one by one + } + + if (incomingLength != incoming.length()) { // check length for error + Serial.println("error: message length does not match length"); + return; // skip rest of function + } + + // if the recipient isn't this device or broadcast, + if (recipient != localAddress && recipient != 0xFF) { + Serial.println("This message is not for me."); + return; // skip rest of function + } + + // if message is for this device, or broadcast, print details: + Serial.println("Received from: 0x" + String(sender, HEX)); + Serial.println("Sent to: 0x" + String(recipient, HEX)); + Serial.println("Message ID: " + String(incomingMsgId)); + Serial.println("Message length: " + String(incomingLength)); + Serial.println("Message: " + incoming); + Serial.println("RSSI: " + String(LoRa.packetRssi())); + Serial.println("Snr: " + String(LoRa.packetSnr())); + Serial.println(); +} + diff --git a/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaReceiver/LoRaReceiver.ino b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaReceiver/LoRaReceiver.ino new file mode 100644 index 0000000..ccbb453 --- /dev/null +++ b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaReceiver/LoRaReceiver.ino @@ -0,0 +1,32 @@ +#include +#include + +void setup() { + Serial.begin(9600); + while (!Serial); + + Serial.println("LoRa Receiver"); + + if (!LoRa.begin(915E6)) { + Serial.println("Starting LoRa failed!"); + while (1); + } +} + +void loop() { + // try to parse packet + int packetSize = LoRa.parsePacket(); + if (packetSize) { + // received a packet + Serial.print("Received packet '"); + + // read packet + while (LoRa.available()) { + Serial.print((char)LoRa.read()); + } + + // print RSSI of packet + Serial.print("' with RSSI "); + Serial.println(LoRa.packetRssi()); + } +} diff --git a/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaReceiverCallback/LoRaReceiverCallback.ino b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaReceiverCallback/LoRaReceiverCallback.ino new file mode 100644 index 0000000..e227e23 --- /dev/null +++ b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaReceiverCallback/LoRaReceiverCallback.ino @@ -0,0 +1,43 @@ +#include +#include + +#ifdef ARDUINO_SAMD_MKRWAN1300 +#error "This example is not compatible with the Arduino MKR WAN 1300 board!" +#endif + +void setup() { + Serial.begin(9600); + while (!Serial); + + Serial.println("LoRa Receiver Callback"); + + if (!LoRa.begin(915E6)) { + Serial.println("Starting LoRa failed!"); + while (1); + } + + // register the receive callback + LoRa.onReceive(onReceive); + + // put the radio into receive mode + LoRa.receive(); +} + +void loop() { + // do nothing +} + +void onReceive(int packetSize) { + // received a packet + Serial.print("Received packet '"); + + // read packet + for (int i = 0; i < packetSize; i++) { + Serial.print((char)LoRa.read()); + } + + // print RSSI of packet + Serial.print("' with RSSI "); + Serial.println(LoRa.packetRssi()); +} + diff --git a/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaSender/LoRaSender.ino b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaSender/LoRaSender.ino new file mode 100644 index 0000000..a252ee5 --- /dev/null +++ b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaSender/LoRaSender.ino @@ -0,0 +1,31 @@ +#include +#include + +int counter = 0; + +void setup() { + Serial.begin(9600); + while (!Serial); + + Serial.println("LoRa Sender"); + + if (!LoRa.begin(915E6)) { + Serial.println("Starting LoRa failed!"); + while (1); + } +} + +void loop() { + Serial.print("Sending packet: "); + Serial.println(counter); + + // send packet + LoRa.beginPacket(); + LoRa.print("hello "); + LoRa.print(counter); + LoRa.endPacket(); + + counter++; + + delay(5000); +} diff --git a/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaSenderNonBlocking/LoRaSenderNonBlocking.ino b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaSenderNonBlocking/LoRaSenderNonBlocking.ino new file mode 100644 index 0000000..0f39077 --- /dev/null +++ b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaSenderNonBlocking/LoRaSenderNonBlocking.ino @@ -0,0 +1,35 @@ +#include +#include + +int counter = 0; + +void setup() { + Serial.begin(9600); + while (!Serial); + + Serial.println("LoRa Sender non-blocking"); + + if (!LoRa.begin(915E6)) { + Serial.println("Starting LoRa failed!"); + while (1); + } +} + +void loop() { + // wait until the radio is ready to send a packet + while (LoRa.beginPacket() == 0) { + Serial.print("waiting for radio ... "); + delay(100); + } + + Serial.print("Sending packet non-blocking: "); + Serial.println(counter); + + // send in async / non-blocking mode + LoRa.beginPacket(); + LoRa.print("hello "); + LoRa.print(counter); + LoRa.endPacket(true); // true = async / non-blocking mode + + counter++; +} diff --git a/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaSetSpread/LoRaSetSpread.ino b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaSetSpread/LoRaSetSpread.ino new file mode 100644 index 0000000..99d0e8d --- /dev/null +++ b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaSetSpread/LoRaSetSpread.ino @@ -0,0 +1,87 @@ +/* + LoRa Duplex communication with Spreading Factor + + Sends a message every half second, and polls continually + for new incoming messages. Sets the LoRa radio's spreading factor. + + Spreading factor affects how far apart the radio's transmissions + are, across the available bandwidth. Radios with different spreading + factors will not receive each other's transmissions. This is one way you + can filter out radios you want to ignore, without making an addressing scheme. + + Spreading factor affects reliability of transmission at high rates, however, + so avoid a hugh spreading factor when you're sending continually. + + See the Semtech datasheet, http://www.semtech.com/images/datasheet/sx1276.pdf + for more on Spreading Factor. + + created 28 April 2017 + by Tom Igoe +*/ +#include // include libraries +#include + +const int csPin = 7; // LoRa radio chip select +const int resetPin = 6; // LoRa radio reset +const int irqPin = 1; // change for your board; must be a hardware interrupt pin + +byte msgCount = 0; // count of outgoing messages +int interval = 2000; // interval between sends +long lastSendTime = 0; // time of last packet send + +void setup() { + Serial.begin(9600); // initialize serial + while (!Serial); + + Serial.println("LoRa Duplex - Set spreading factor"); + + // override the default CS, reset, and IRQ pins (optional) + LoRa.setPins(csPin, resetPin, irqPin); // set CS, reset, IRQ pin + + if (!LoRa.begin(915E6)) { // initialize ratio at 915 MHz + Serial.println("LoRa init failed. Check your connections."); + while (true); // if failed, do nothing + } + + LoRa.setSpreadingFactor(8); // ranges from 6-12,default 7 see API docs + Serial.println("LoRa init succeeded."); +} + +void loop() { + if (millis() - lastSendTime > interval) { + String message = "HeLoRa World! "; // send a message + message += msgCount; + sendMessage(message); + Serial.println("Sending " + message); + lastSendTime = millis(); // timestamp the message + interval = random(2000) + 1000; // 2-3 seconds + msgCount++; + } + + // parse for a packet, and call onReceive with the result: + onReceive(LoRa.parsePacket()); +} + +void sendMessage(String outgoing) { + LoRa.beginPacket(); // start packet + LoRa.print(outgoing); // add payload + LoRa.endPacket(); // finish packet and send it + msgCount++; // increment message ID +} + +void onReceive(int packetSize) { + if (packetSize == 0) return; // if there's no packet, return + + // read packet header bytes: + String incoming = ""; + + while (LoRa.available()) { + incoming += (char)LoRa.read(); + } + + Serial.println("Message: " + incoming); + Serial.println("RSSI: " + String(LoRa.packetRssi())); + Serial.println("Snr: " + String(LoRa.packetSnr())); + Serial.println(); +} + diff --git a/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaSetSyncWord/LoRaSetSyncWord.ino b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaSetSyncWord/LoRaSetSyncWord.ino new file mode 100644 index 0000000..69af87d --- /dev/null +++ b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaSetSyncWord/LoRaSetSyncWord.ino @@ -0,0 +1,82 @@ +/* + LoRa Duplex communication with Sync Word + + Sends a message every half second, and polls continually + for new incoming messages. Sets the LoRa radio's Sync Word. + + Spreading factor is basically the radio's network ID. Radios with different + Sync Words will not receive each other's transmissions. This is one way you + can filter out radios you want to ignore, without making an addressing scheme. + + See the Semtech datasheet, http://www.semtech.com/images/datasheet/sx1276.pdf + for more on Sync Word. + + created 28 April 2017 + by Tom Igoe +*/ +#include // include libraries +#include +const int csPin = 7; // LoRa radio chip select +const int resetPin = 6; // LoRa radio reset +const int irqPin = 1; // change for your board; must be a hardware interrupt pin + +byte msgCount = 0; // count of outgoing messages +int interval = 2000; // interval between sends +long lastSendTime = 0; // time of last packet send + +void setup() { + Serial.begin(9600); // initialize serial + while (!Serial); + + Serial.println("LoRa Duplex - Set sync word"); + + // override the default CS, reset, and IRQ pins (optional) + LoRa.setPins(csPin, resetPin, irqPin);// set CS, reset, IRQ pin + + if (!LoRa.begin(915E6)) { // initialize ratio at 915 MHz + Serial.println("LoRa init failed. Check your connections."); + while (true); // if failed, do nothing + } + + LoRa.setSyncWord(0xF3); // ranges from 0-0xFF, default 0x34, see API docs + Serial.println("LoRa init succeeded."); +} + +void loop() { + if (millis() - lastSendTime > interval) { + String message = "HeLoRa World! "; // send a message + message += msgCount; + sendMessage(message); + Serial.println("Sending " + message); + lastSendTime = millis(); // timestamp the message + interval = random(2000) + 1000; // 2-3 seconds + msgCount++; + } + + // parse for a packet, and call onReceive with the result: + onReceive(LoRa.parsePacket()); +} + +void sendMessage(String outgoing) { + LoRa.beginPacket(); // start packet + LoRa.print(outgoing); // add payload + LoRa.endPacket(); // finish packet and send it + msgCount++; // increment message ID +} + +void onReceive(int packetSize) { + if (packetSize == 0) return; // if there's no packet, return + + // read packet header bytes: + String incoming = ""; + + while (LoRa.available()) { + incoming += (char)LoRa.read(); + } + + Serial.println("Message: " + incoming); + Serial.println("RSSI: " + String(LoRa.packetRssi())); + Serial.println("Snr: " + String(LoRa.packetSnr())); + Serial.println(); +} + diff --git a/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaSimpleGateway/LoRaSimpleGateway.ino b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaSimpleGateway/LoRaSimpleGateway.ino new file mode 100644 index 0000000..745bf1f --- /dev/null +++ b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaSimpleGateway/LoRaSimpleGateway.ino @@ -0,0 +1,113 @@ +/* + LoRa Simple Gateway/Node Exemple + + This code uses InvertIQ function to create a simple Gateway/Node logic. + + Gateway - Sends messages with enableInvertIQ() + - Receives messages with disableInvertIQ() + + Node - Sends messages with disableInvertIQ() + - Receives messages with enableInvertIQ() + + With this arrangement a Gateway never receive messages from another Gateway + and a Node never receive message from another Node. + Only Gateway to Node and vice versa. + + This code receives messages and sends a message every second. + + InvertIQ function basically invert the LoRa I and Q signals. + + See the Semtech datasheet, http://www.semtech.com/images/datasheet/sx1276.pdf + for more on InvertIQ register 0x33. + + created 05 August 2018 + by Luiz H. Cassettari +*/ + +#include // include libraries +#include + +const long frequency = 915E6; // LoRa Frequency + +const int csPin = 10; // LoRa radio chip select +const int resetPin = 9; // LoRa radio reset +const int irqPin = 2; // change for your board; must be a hardware interrupt pin + +void setup() { + Serial.begin(9600); // initialize serial + while (!Serial); + + LoRa.setPins(csPin, resetPin, irqPin); + + if (!LoRa.begin(frequency)) { + Serial.println("LoRa init failed. Check your connections."); + while (true); // if failed, do nothing + } + + Serial.println("LoRa init succeeded."); + Serial.println(); + Serial.println("LoRa Simple Gateway"); + Serial.println("Only receive messages from nodes"); + Serial.println("Tx: invertIQ enable"); + Serial.println("Rx: invertIQ disable"); + Serial.println(); + + LoRa.onReceive(onReceive); + LoRa_rxMode(); +} + +void loop() { + if (runEvery(5000)) { // repeat every 5000 millis + + String message = "HeLoRa World! "; + message += "I'm a Gateway! "; + message += millis(); + + LoRa_sendMessage(message); // send a message + + Serial.println("Send Message!"); + } +} + +void LoRa_rxMode(){ + LoRa.disableInvertIQ(); // normal mode + LoRa.receive(); // set receive mode +} + +void LoRa_txMode(){ + LoRa.idle(); // set standby mode + LoRa.enableInvertIQ(); // active invert I and Q signals +} + +void LoRa_sendMessage(String message) { + LoRa_txMode(); // set tx mode + LoRa.beginPacket(); // start packet + LoRa.print(message); // add payload + LoRa.endPacket(); // finish packet and send it + LoRa_rxMode(); // set rx mode +} + +void onReceive(int packetSize) { + String message = ""; + + while (LoRa.available()) { + message += (char)LoRa.read(); + } + + Serial.print("Gateway Receive: "); + Serial.println(message); + +} + +boolean runEvery(unsigned long interval) +{ + static unsigned long previousMillis = 0; + unsigned long currentMillis = millis(); + if (currentMillis - previousMillis >= interval) + { + previousMillis = currentMillis; + return true; + } + return false; +} + diff --git a/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaSimpleNode/LoRaSimpleNode.ino b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaSimpleNode/LoRaSimpleNode.ino new file mode 100644 index 0000000..a6cb0c0 --- /dev/null +++ b/Examples/LoRaTests/lib/arduino-LoRa/examples/LoRaSimpleNode/LoRaSimpleNode.ino @@ -0,0 +1,113 @@ +/* + LoRa Simple Gateway/Node Exemple + + This code uses InvertIQ function to create a simple Gateway/Node logic. + + Gateway - Sends messages with enableInvertIQ() + - Receives messages with disableInvertIQ() + + Node - Sends messages with disableInvertIQ() + - Receives messages with enableInvertIQ() + + With this arrangement a Gateway never receive messages from another Gateway + and a Node never receive message from another Node. + Only Gateway to Node and vice versa. + + This code receives messages and sends a message every second. + + InvertIQ function basically invert the LoRa I and Q signals. + + See the Semtech datasheet, http://www.semtech.com/images/datasheet/sx1276.pdf + for more on InvertIQ register 0x33. + + created 05 August 2018 + by Luiz H. Cassettari +*/ + +#include // include libraries +#include + +const long frequency = 915E6; // LoRa Frequency + +const int csPin = 10; // LoRa radio chip select +const int resetPin = 9; // LoRa radio reset +const int irqPin = 2; // change for your board; must be a hardware interrupt pin + +void setup() { + Serial.begin(9600); // initialize serial + while (!Serial); + + LoRa.setPins(csPin, resetPin, irqPin); + + if (!LoRa.begin(frequency)) { + Serial.println("LoRa init failed. Check your connections."); + while (true); // if failed, do nothing + } + + Serial.println("LoRa init succeeded."); + Serial.println(); + Serial.println("LoRa Simple Node"); + Serial.println("Only receive messages from gateways"); + Serial.println("Tx: invertIQ disable"); + Serial.println("Rx: invertIQ enable"); + Serial.println(); + + LoRa.onReceive(onReceive); + LoRa_rxMode(); +} + +void loop() { + if (runEvery(1000)) { // repeat every 1000 millis + + String message = "HeLoRa World! "; + message += "I'm a Node! "; + message += millis(); + + LoRa_sendMessage(message); // send a message + + Serial.println("Send Message!"); + } +} + +void LoRa_rxMode(){ + LoRa.enableInvertIQ(); // active invert I and Q signals + LoRa.receive(); // set receive mode +} + +void LoRa_txMode(){ + LoRa.idle(); // set standby mode + LoRa.disableInvertIQ(); // normal mode +} + +void LoRa_sendMessage(String message) { + LoRa_txMode(); // set tx mode + LoRa.beginPacket(); // start packet + LoRa.print(message); // add payload + LoRa.endPacket(); // finish packet and send it + LoRa_rxMode(); // set rx mode +} + +void onReceive(int packetSize) { + String message = ""; + + while (LoRa.available()) { + message += (char)LoRa.read(); + } + + Serial.print("Node Receive: "); + Serial.println(message); + +} + +boolean runEvery(unsigned long interval) +{ + static unsigned long previousMillis = 0; + unsigned long currentMillis = millis(); + if (currentMillis - previousMillis >= interval) + { + previousMillis = currentMillis; + return true; + } + return false; +} + diff --git a/Examples/LoRaTests/lib/arduino-LoRa/keywords.txt b/Examples/LoRaTests/lib/arduino-LoRa/keywords.txt new file mode 100644 index 0000000..2cf0351 --- /dev/null +++ b/Examples/LoRaTests/lib/arduino-LoRa/keywords.txt @@ -0,0 +1,60 @@ +####################################### +# Syntax Coloring Map For LoRa +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +LoRa KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +end KEYWORD2 + +beginPacket KEYWORD2 +endPacket KEYWORD2 + +parsePacket KEYWORD2 +packetRssi KEYWORD2 +packetSnr KEYWORD2 +packetFrequencyError KEYWORD2 + +write KEYWORD2 + +available KEYWORD2 +read KEYWORD2 +peek KEYWORD2 +flush KEYWORD2 + +onReceive KEYWORD2 +receive KEYWORD2 +idle KEYWORD2 +sleep KEYWORD2 + +setTxPower KEYWORD2 +setFrequency KEYWORD2 +setSpreadingFactor KEYWORD2 +setSignalBandwidth KEYWORD2 +setCodingRate4 KEYWORD2 +setPreambleLength KEYWORD2 +setSyncWord KEYWORD2 +enableCrc KEYWORD2 +disableCrc KEYWORD2 +enableInvertIQ KEYWORD2 +disableInvertIQ KEYWORD2 + +random KEYWORD2 +setPins KEYWORD2 +setSPIFrequency KEYWORD2 +dumpRegisters KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + +PA_OUTPUT_RFO_PIN LITERAL1 +PA_OUTPUT_PA_BOOST_PIN LITERAL1 diff --git a/Examples/LoRaTests/lib/arduino-LoRa/library.properties b/Examples/LoRaTests/lib/arduino-LoRa/library.properties new file mode 100644 index 0000000..af09c7a --- /dev/null +++ b/Examples/LoRaTests/lib/arduino-LoRa/library.properties @@ -0,0 +1,10 @@ +name=LoRa +version=0.5.0 +author=Sandeep Mistry +maintainer=Sandeep Mistry +sentence=An Arduino library for sending and receiving data using LoRa radios. +paragraph=Supports Semtech SX1276/77/78/79 based boards/shields. +category=Communication +url=https://github.com/sandeepmistry/arduino-LoRa +architectures=* +includes=LoRa.h diff --git a/Examples/LoRaTests/lib/arduino-LoRa/src/LoRa.cpp b/Examples/LoRaTests/lib/arduino-LoRa/src/LoRa.cpp new file mode 100644 index 0000000..a37e3b0 --- /dev/null +++ b/Examples/LoRaTests/lib/arduino-LoRa/src/LoRa.cpp @@ -0,0 +1,685 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +// registers +#define REG_FIFO 0x00 +#define REG_OP_MODE 0x01 +#define REG_FRF_MSB 0x06 +#define REG_FRF_MID 0x07 +#define REG_FRF_LSB 0x08 +#define REG_PA_CONFIG 0x09 +#define REG_OCP 0x0b +#define REG_LNA 0x0c +#define REG_FIFO_ADDR_PTR 0x0d +#define REG_FIFO_TX_BASE_ADDR 0x0e +#define REG_FIFO_RX_BASE_ADDR 0x0f +#define REG_FIFO_RX_CURRENT_ADDR 0x10 +#define REG_IRQ_FLAGS 0x12 +#define REG_RX_NB_BYTES 0x13 +#define REG_PKT_SNR_VALUE 0x19 +#define REG_PKT_RSSI_VALUE 0x1a +#define REG_MODEM_CONFIG_1 0x1d +#define REG_MODEM_CONFIG_2 0x1e +#define REG_PREAMBLE_MSB 0x20 +#define REG_PREAMBLE_LSB 0x21 +#define REG_PAYLOAD_LENGTH 0x22 +#define REG_MODEM_CONFIG_3 0x26 +#define REG_FREQ_ERROR_MSB 0x28 +#define REG_FREQ_ERROR_MID 0x29 +#define REG_FREQ_ERROR_LSB 0x2a +#define REG_RSSI_WIDEBAND 0x2c +#define REG_DETECTION_OPTIMIZE 0x31 +#define REG_INVERTIQ 0x33 +#define REG_DETECTION_THRESHOLD 0x37 +#define REG_SYNC_WORD 0x39 +#define REG_INVERTIQ2 0x3b +#define REG_DIO_MAPPING_1 0x40 +#define REG_VERSION 0x42 +#define REG_PA_DAC 0x4d + +// modes +#define MODE_LONG_RANGE_MODE 0x80 +#define MODE_SLEEP 0x00 +#define MODE_STDBY 0x01 +#define MODE_TX 0x03 +#define MODE_RX_CONTINUOUS 0x05 +#define MODE_RX_SINGLE 0x06 + +// PA config +#define PA_BOOST 0x80 + +// IRQ masks +#define IRQ_TX_DONE_MASK 0x08 +#define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20 +#define IRQ_RX_DONE_MASK 0x40 + +#define MAX_PKT_LENGTH 255 + +LoRaClass::LoRaClass() : + _spiSettings(LORA_DEFAULT_SPI_FREQUENCY, MSBFIRST, SPI_MODE0), + _spi(&LORA_DEFAULT_SPI), + _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), + _frequency(0), + _packetIndex(0), + _implicitHeaderMode(0), + _onReceive(NULL) +{ + // overide Stream timeout value + setTimeout(0); +} + +int LoRaClass::begin(long frequency) +{ +#ifdef ARDUINO_SAMD_MKRWAN1300 + pinMode(LORA_IRQ_DUMB, OUTPUT); + digitalWrite(LORA_IRQ_DUMB, LOW); + + // Hardware reset + pinMode(LORA_BOOT0, OUTPUT); + digitalWrite(LORA_BOOT0, LOW); + + pinMode(LORA_RESET, OUTPUT); + digitalWrite(LORA_RESET, HIGH); + delay(200); + digitalWrite(LORA_RESET, LOW); + delay(200); + digitalWrite(LORA_RESET, HIGH); + delay(50); +#endif + + // setup pins + pinMode(_ss, OUTPUT); + // set SS high + digitalWrite(_ss, HIGH); + + if (_reset != -1) { + pinMode(_reset, OUTPUT); + + // perform reset + digitalWrite(_reset, LOW); + delay(10); + digitalWrite(_reset, HIGH); + delay(10); + } + + // start SPI + _spi->begin(); + + // check version + uint8_t version = readRegister(REG_VERSION); + if (version != 0x12) { + return 0; + } + + // put in sleep mode + sleep(); + + // set frequency + setFrequency(frequency); + + // set base addresses + writeRegister(REG_FIFO_TX_BASE_ADDR, 0); + writeRegister(REG_FIFO_RX_BASE_ADDR, 0); + + // set LNA boost + writeRegister(REG_LNA, readRegister(REG_LNA) | 0x03); + + // set auto AGC + writeRegister(REG_MODEM_CONFIG_3, 0x04); + + // set output power to 17 dBm + setTxPower(17); + + // put in standby mode + idle(); + + return 1; +} + +void LoRaClass::end() +{ + // put in sleep mode + sleep(); + + // stop SPI + _spi->end(); +} + +int LoRaClass::beginPacket(int implicitHeader) +{ + if (isTransmitting()) { + return 0; + } + + // put in standby mode + idle(); + + if (implicitHeader) { + implicitHeaderMode(); + } else { + explicitHeaderMode(); + } + + // reset FIFO address and paload length + writeRegister(REG_FIFO_ADDR_PTR, 0); + writeRegister(REG_PAYLOAD_LENGTH, 0); + + return 1; +} + +int LoRaClass::endPacket(bool async) +{ + // put in TX mode + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX); + + if (async) { + // grace time is required for the radio + delayMicroseconds(150); + } else { + // wait for TX done + while ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0) { + yield(); + } + // clear IRQ's + writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK); + } + + return 1; +} + +bool LoRaClass::isTransmitting() +{ + if ((readRegister(REG_OP_MODE) & MODE_TX) == MODE_TX) { + return true; + } + + if (readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) { + // clear IRQ's + writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK); + } + + return false; +} + +int LoRaClass::parsePacket(int size) +{ + int packetLength = 0; + int irqFlags = readRegister(REG_IRQ_FLAGS); + + if (size > 0) { + implicitHeaderMode(); + + writeRegister(REG_PAYLOAD_LENGTH, size & 0xff); + } else { + explicitHeaderMode(); + } + + // clear IRQ's + writeRegister(REG_IRQ_FLAGS, irqFlags); + + if ((irqFlags & IRQ_RX_DONE_MASK) && (irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { + // received a packet + _packetIndex = 0; + + // read packet length + if (_implicitHeaderMode) { + packetLength = readRegister(REG_PAYLOAD_LENGTH); + } else { + packetLength = readRegister(REG_RX_NB_BYTES); + } + + // set FIFO address to current RX address + writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR)); + + // put in standby mode + idle(); + } else if (readRegister(REG_OP_MODE) != (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE)) { + // not currently in RX mode + + // reset FIFO address + writeRegister(REG_FIFO_ADDR_PTR, 0); + + // put in single RX mode + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE); + } + + return packetLength; +} + +int LoRaClass::packetRssi() +{ + return (readRegister(REG_PKT_RSSI_VALUE) - (_frequency < 868E6 ? 164 : 157)); +} + +float LoRaClass::packetSnr() +{ + return ((int8_t)readRegister(REG_PKT_SNR_VALUE)) * 0.25; +} + +long LoRaClass::packetFrequencyError() +{ + int32_t freqError = 0; + freqError = static_cast(readRegister(REG_FREQ_ERROR_MSB) & B111); + freqError <<= 8L; + freqError += static_cast(readRegister(REG_FREQ_ERROR_MID)); + freqError <<= 8L; + freqError += static_cast(readRegister(REG_FREQ_ERROR_LSB)); + + if (readRegister(REG_FREQ_ERROR_MSB) & B1000) { // Sign bit is on + freqError -= 524288; // B1000'0000'0000'0000'0000 + } + + const float fXtal = 32E6; // FXOSC: crystal oscillator (XTAL) frequency (2.5. Chip Specification, p. 14) + const float fError = ((static_cast(freqError) * (1L << 24)) / fXtal) * (getSignalBandwidth() / 500000.0f); // p. 37 + + return static_cast(fError); +} + +size_t LoRaClass::write(uint8_t byte) +{ + return write(&byte, sizeof(byte)); +} + +size_t LoRaClass::write(const uint8_t *buffer, size_t size) +{ + int currentLength = readRegister(REG_PAYLOAD_LENGTH); + + // check size + if ((currentLength + size) > MAX_PKT_LENGTH) { + size = MAX_PKT_LENGTH - currentLength; + } + + // write data + for (size_t i = 0; i < size; i++) { + writeRegister(REG_FIFO, buffer[i]); + } + + // update length + writeRegister(REG_PAYLOAD_LENGTH, currentLength + size); + + return size; +} + +int LoRaClass::available() +{ + return (readRegister(REG_RX_NB_BYTES) - _packetIndex); +} + +int LoRaClass::read() +{ + if (!available()) { + return -1; + } + + _packetIndex++; + + return readRegister(REG_FIFO); +} + +int LoRaClass::peek() +{ + if (!available()) { + return -1; + } + + // store current FIFO address + int currentAddress = readRegister(REG_FIFO_ADDR_PTR); + + // read + uint8_t b = readRegister(REG_FIFO); + + // restore FIFO address + writeRegister(REG_FIFO_ADDR_PTR, currentAddress); + + return b; +} + +void LoRaClass::flush() +{ +} + +#ifndef ARDUINO_SAMD_MKRWAN1300 +void LoRaClass::onReceive(void(*callback)(int)) +{ + _onReceive = callback; + + if (callback) { + pinMode(_dio0, INPUT); + + writeRegister(REG_DIO_MAPPING_1, 0x00); +#ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); +#endif + attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING); + } else { + detachInterrupt(digitalPinToInterrupt(_dio0)); +#ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); +#endif + } +} + +void LoRaClass::receive(int size) +{ + if (size > 0) { + implicitHeaderMode(); + + writeRegister(REG_PAYLOAD_LENGTH, size & 0xff); + } else { + explicitHeaderMode(); + } + + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS); +} +#endif + +void LoRaClass::idle() +{ + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY); +} + +void LoRaClass::sleep() +{ + writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP); +} + +void LoRaClass::setTxPower(int level, int outputPin) +{ + if (PA_OUTPUT_RFO_PIN == outputPin) { + // RFO + if (level < 0) { + level = 0; + } else if (level > 14) { + level = 14; + } + + writeRegister(REG_PA_CONFIG, 0x70 | level); + } else { + // PA BOOST + if (level > 17) { + if (level > 20) { + level = 20; + } + + // subtract 3 from level, so 18 - 20 maps to 15 - 17 + level -= 3; + + // High Power +20 dBm Operation (Semtech SX1276/77/78/79 5.4.3.) + writeRegister(REG_PA_DAC, 0x87); + setOCP(140); + } else { + if (level < 2) { + level = 2; + } + //Default value PA_HF/LF or +17dBm + writeRegister(REG_PA_DAC, 0x84); + setOCP(100); + } + + writeRegister(REG_PA_CONFIG, PA_BOOST | (level - 2)); + } +} + +void LoRaClass::setFrequency(long frequency) +{ + _frequency = frequency; + + uint64_t frf = ((uint64_t)frequency << 19) / 32000000; + + writeRegister(REG_FRF_MSB, (uint8_t)(frf >> 16)); + writeRegister(REG_FRF_MID, (uint8_t)(frf >> 8)); + writeRegister(REG_FRF_LSB, (uint8_t)(frf >> 0)); +} + +int LoRaClass::getSpreadingFactor() +{ + return readRegister(REG_MODEM_CONFIG_2) >> 4; +} + +void LoRaClass::setSpreadingFactor(int sf) +{ + if (sf < 6) { + sf = 6; + } else if (sf > 12) { + sf = 12; + } + + if (sf == 6) { + writeRegister(REG_DETECTION_OPTIMIZE, 0xc5); + writeRegister(REG_DETECTION_THRESHOLD, 0x0c); + } else { + writeRegister(REG_DETECTION_OPTIMIZE, 0xc3); + writeRegister(REG_DETECTION_THRESHOLD, 0x0a); + } + + writeRegister(REG_MODEM_CONFIG_2, (readRegister(REG_MODEM_CONFIG_2) & 0x0f) | ((sf << 4) & 0xf0)); + setLdoFlag(); +} + +long LoRaClass::getSignalBandwidth() +{ + byte bw = (readRegister(REG_MODEM_CONFIG_1) >> 4); + + switch (bw) { + case 0: return 7.8E3; + case 1: return 10.4E3; + case 2: return 15.6E3; + case 3: return 20.8E3; + case 4: return 31.25E3; + case 5: return 41.7E3; + case 6: return 62.5E3; + case 7: return 125E3; + case 8: return 250E3; + case 9: return 500E3; + } + + return -1; +} + +void LoRaClass::setSignalBandwidth(long sbw) +{ + int bw; + + if (sbw <= 7.8E3) { + bw = 0; + } else if (sbw <= 10.4E3) { + bw = 1; + } else if (sbw <= 15.6E3) { + bw = 2; + } else if (sbw <= 20.8E3) { + bw = 3; + } else if (sbw <= 31.25E3) { + bw = 4; + } else if (sbw <= 41.7E3) { + bw = 5; + } else if (sbw <= 62.5E3) { + bw = 6; + } else if (sbw <= 125E3) { + bw = 7; + } else if (sbw <= 250E3) { + bw = 8; + } else /*if (sbw <= 250E3)*/ { + bw = 9; + } + + writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0x0f) | (bw << 4)); + setLdoFlag(); +} + +void LoRaClass::setLdoFlag() +{ + // Section 4.1.1.5 + long symbolDuration = 1000 / ( getSignalBandwidth() / (1L << getSpreadingFactor()) ) ; + + // Section 4.1.1.6 + boolean ldoOn = symbolDuration > 16; + + uint8_t config3 = readRegister(REG_MODEM_CONFIG_3); + bitWrite(config3, 3, ldoOn); + writeRegister(REG_MODEM_CONFIG_3, config3); +} + +void LoRaClass::setCodingRate4(int denominator) +{ + if (denominator < 5) { + denominator = 5; + } else if (denominator > 8) { + denominator = 8; + } + + int cr = denominator - 4; + + writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0xf1) | (cr << 1)); +} + +void LoRaClass::setPreambleLength(long length) +{ + writeRegister(REG_PREAMBLE_MSB, (uint8_t)(length >> 8)); + writeRegister(REG_PREAMBLE_LSB, (uint8_t)(length >> 0)); +} + +void LoRaClass::setSyncWord(int sw) +{ + writeRegister(REG_SYNC_WORD, sw); +} + +void LoRaClass::enableCrc() +{ + writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) | 0x04); +} + +void LoRaClass::disableCrc() +{ + writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) & 0xfb); +} + +void LoRaClass::enableInvertIQ() +{ + writeRegister(REG_INVERTIQ, 0x66); + writeRegister(REG_INVERTIQ2, 0x19); +} + +void LoRaClass::disableInvertIQ() +{ + writeRegister(REG_INVERTIQ, 0x27); + writeRegister(REG_INVERTIQ2, 0x1d); +} + +void LoRaClass::setOCP(uint8_t mA) +{ + uint8_t ocpTrim = 27; + + if (mA <= 120) { + ocpTrim = (mA - 45) / 5; + } else if (mA <=240) { + ocpTrim = (mA + 30) / 10; + } + + writeRegister(REG_OCP, 0x20 | (0x1F & ocpTrim)); +} + +byte LoRaClass::random() +{ + return readRegister(REG_RSSI_WIDEBAND); +} + +void LoRaClass::setPins(int ss, int reset, int dio0) +{ + _ss = ss; + _reset = reset; + _dio0 = dio0; +} + +void LoRaClass::setSPI(SPIClass& spi) +{ + _spi = &spi; +} + +void LoRaClass::setSPIFrequency(uint32_t frequency) +{ + _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); +} + +void LoRaClass::dumpRegisters(Stream& out) +{ + for (int i = 0; i < 128; i++) { + out.print("0x"); + out.print(i, HEX); + out.print(": 0x"); + out.println(readRegister(i), HEX); + } +} + +void LoRaClass::explicitHeaderMode() +{ + _implicitHeaderMode = 0; + + writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) & 0xfe); +} + +void LoRaClass::implicitHeaderMode() +{ + _implicitHeaderMode = 1; + + writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) | 0x01); +} + +void LoRaClass::handleDio0Rise() +{ + int irqFlags = readRegister(REG_IRQ_FLAGS); + + // clear IRQ's + writeRegister(REG_IRQ_FLAGS, irqFlags); + + if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { + // received a packet + _packetIndex = 0; + + // read packet length + int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH) : readRegister(REG_RX_NB_BYTES); + + // set FIFO address to current RX address + writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR)); + + if (_onReceive) { + _onReceive(packetLength); + } + + // reset FIFO address + writeRegister(REG_FIFO_ADDR_PTR, 0); + } +} + +uint8_t LoRaClass::readRegister(uint8_t address) +{ + return singleTransfer(address & 0x7f, 0x00); +} + +void LoRaClass::writeRegister(uint8_t address, uint8_t value) +{ + singleTransfer(address | 0x80, value); +} + +uint8_t LoRaClass::singleTransfer(uint8_t address, uint8_t value) +{ + uint8_t response; + + digitalWrite(_ss, LOW); + + _spi->beginTransaction(_spiSettings); + _spi->transfer(address); + response = _spi->transfer(value); + _spi->endTransaction(); + + digitalWrite(_ss, HIGH); + + return response; +} +/*ICACHE_RAM_ATTR*/ void LoRaClass::onDio0Rise() +{ + LoRa.handleDio0Rise(); +} + +LoRaClass LoRa; diff --git a/Examples/LoRaTests/lib/arduino-LoRa/src/LoRa.h b/Examples/LoRaTests/lib/arduino-LoRa/src/LoRa.h new file mode 100644 index 0000000..c5d239e --- /dev/null +++ b/Examples/LoRaTests/lib/arduino-LoRa/src/LoRa.h @@ -0,0 +1,118 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef LORA_H +#define LORA_H + +#include +#include + +#ifdef ARDUINO_SAMD_MKRWAN1300 +#define LORA_DEFAULT_SPI SPI1 +#define LORA_DEFAULT_SPI_FREQUENCY 250000 +#define LORA_DEFAULT_SS_PIN LORA_IRQ_DUMB +#define LORA_DEFAULT_RESET_PIN -1 +#define LORA_DEFAULT_DIO0_PIN -1 +#else +#define LORA_DEFAULT_SPI SPI +#define LORA_DEFAULT_SPI_FREQUENCY 8E6 +#define LORA_DEFAULT_SS_PIN 10 +#define LORA_DEFAULT_RESET_PIN 9 +#define LORA_DEFAULT_DIO0_PIN 2 +#endif + +#define PA_OUTPUT_RFO_PIN 0 +#define PA_OUTPUT_PA_BOOST_PIN 1 + +class LoRaClass : public Stream { +public: + LoRaClass(); + + int begin(long frequency); + void end(); + + int beginPacket(int implicitHeader = false); + int endPacket(bool async = false); + + int parsePacket(int size = 0); + int packetRssi(); + float packetSnr(); + long packetFrequencyError(); + + // from Print + virtual size_t write(uint8_t byte); + virtual size_t write(const uint8_t *buffer, size_t size); + + // from Stream + virtual int available(); + virtual int read(); + virtual int peek(); + virtual void flush(); + +#ifndef ARDUINO_SAMD_MKRWAN1300 + void onReceive(void(*callback)(int)); + + void receive(int size = 0); +#endif + void idle(); + void sleep(); + + void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); + void setFrequency(long frequency); + void setSpreadingFactor(int sf); + void setSignalBandwidth(long sbw); + void setCodingRate4(int denominator); + void setPreambleLength(long length); + void setSyncWord(int sw); + void enableCrc(); + void disableCrc(); + void enableInvertIQ(); + void disableInvertIQ(); + + void setOCP(uint8_t mA); // Over Current Protection control + + // deprecated + void crc() { enableCrc(); } + void noCrc() { disableCrc(); } + + byte random(); + + void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN); + void setSPI(SPIClass& spi); + void setSPIFrequency(uint32_t frequency); + + void dumpRegisters(Stream& out); + +private: + void explicitHeaderMode(); + void implicitHeaderMode(); + + void handleDio0Rise(); + bool isTransmitting(); + + int getSpreadingFactor(); + long getSignalBandwidth(); + + void setLdoFlag(); + + uint8_t readRegister(uint8_t address); + void writeRegister(uint8_t address, uint8_t value); + uint8_t singleTransfer(uint8_t address, uint8_t value); + + static void onDio0Rise(); + +private: + SPISettings _spiSettings; + SPIClass* _spi; + int _ss; + int _reset; + int _dio0; + long _frequency; + int _packetIndex; + int _implicitHeaderMode; + void (*_onReceive)(int); +}; + +extern LoRaClass LoRa; + +#endif