├── library.properties ├── .github ├── workflows │ ├── Arduino-Lint-Check.yml │ └── clang-format-check.yml └── ISSUE_TEMPLATE │ └── bug-report.yml ├── library.json ├── .gitignore ├── README.md ├── LICENSE ├── examples ├── LoRaDumpRegisters │ └── LoRaDumpRegisters.ino ├── LoRaSender │ └── LoRaSender.ino ├── LoRaReceiver │ └── LoRaReceiver.ino ├── LoRaSenderNonBlocking │ └── LoRaSenderNonBlocking.ino ├── LoRaReceiverCallback │ └── LoRaReceiverCallback.ino ├── LoRaSenderNonBlockingCallback │ └── LoRaSenderNonBlockingCallback.ino ├── LoRaCADCallback │ └── LoRaCADCallback.ino ├── LoRaSetSyncWord │ └── LoRaSetSyncWord.ino ├── LoRaSetSpread │ └── LoRaSetSpread.ino ├── LoRaSimpleNode │ └── LoRaSimpleNode.ino ├── LoRaSimpleGateway │ └── LoRaSimpleGateway.ino ├── LoRaDuplex │ └── LoRaDuplex.ino └── LoRaDuplexCallback │ └── LoRaDuplexCallback.ino ├── src ├── M5_SX127X.h └── M5_SX127X.cpp └── .clang-format /library.properties: -------------------------------------------------------------------------------- 1 | name=M5-SX127x 2 | version=1.0.0 3 | author=M5Stack 4 | maintainer=M5Stack 5 | sentence=Library for M5Stack SX127x LoRa 6 | paragraph= 7 | category=Device Control 8 | url=https://github.com/m5stack/M5-SX127x.git 9 | architectures=esp32 10 | includes=M5_SX127X.h 11 | 12 | -------------------------------------------------------------------------------- /.github/workflows/Arduino-Lint-Check.yml: -------------------------------------------------------------------------------- 1 | name: Arduino Lint 2 | on: 3 | push: 4 | pull_request: 5 | jobs: 6 | lint: 7 | name: Lint Check 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: arduino/arduino-lint-action@v1 12 | with: 13 | library-manager: update 14 | compliance: strict 15 | project-type: all -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "M5-SX127x", 3 | "description": "SX127x LoRa Module Driver", 4 | "keywords": "M5Stack SX127x LoRa", 5 | "authors": { 6 | "name": "M5Stack", 7 | "url": "http://www.m5stack.com" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/m5stack/M5-SX127x.git" 12 | }, 13 | "version": "1.0.0", 14 | "frameworks": "arduino", 15 | "platforms": "espressif32", 16 | "headers": "M5_SX127X.h" 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ~/.DS_Store 2 | */.DS_Store 3 | examples/Basics/.DS_Store 4 | .development 5 | examples/Touch/.DS_Store 6 | *.svd 7 | debug_custom.json 8 | .vscode 9 | .vscode/* 10 | # Prerequisites 11 | *.d 12 | 13 | # Compiled Object files 14 | *.slo 15 | *.lo 16 | *.o 17 | *.obj 18 | 19 | # Precompiled Headers 20 | *.gch 21 | *.pch 22 | 23 | # Compiled Dynamic libraries 24 | *.so 25 | *.dylib 26 | *.dll 27 | 28 | # Fortran module files 29 | *.mod 30 | *.smod 31 | 32 | # Compiled Static libraries 33 | *.lai 34 | *.la 35 | *.a 36 | *.lib 37 | 38 | # Executables 39 | *.exe 40 | *.out 41 | *.app 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # M5-SX127x 2 | 3 | ## Overview 4 | 5 | ### SKU:M005 & M005-V11 & M029 & M029-V11 6 | 7 | Contains M5Stack-**Module LoRa433 & Module LoRa433 V1.1 & Module LoRa868 & Module LoRa868 V1.1** series related case programs. 8 | 9 | ## Overview 10 | 11 | Contains case programs of M5Stack LoRa Module 12 | 13 | ## Related Link 14 | 15 | - [Module Lora433 - Document & Datasheet](https://docs.m5stack.com/en/module/lora) 16 | - [Module Lora433 V1.1 - Document & Datasheet](https://docs.m5stack.com/en/module/Module-LoRa433_V1.1) 17 | - [Module Lora868 - Document & Datasheet](https://docs.m5stack.com/en/module/lora868) 18 | - [Module Lora868 V1.1 - Document & Datasheet](https://docs.m5stack.com/en/module/Module-LoRa868_V1.1) 19 | 20 | ## License 21 | 22 | - [M5-SX127x - MIT](LICENSE) 23 | 24 | -------------------------------------------------------------------------------- /.github/workflows/clang-format-check.yml: -------------------------------------------------------------------------------- 1 | name: clang-format Check 2 | on: [push, pull_request] 3 | jobs: 4 | formatting-check: 5 | name: Formatting Check 6 | runs-on: ubuntu-latest 7 | strategy: 8 | matrix: 9 | path: 10 | - check: './' # path to include 11 | exclude: '(Fonts | MahonyAHRS)' # path to exclude 12 | # - check: 'src' 13 | # exclude: '(Fonts)' # Exclude file paths containing "Fonts" 14 | # - check: 'examples' 15 | # exclude: '' 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Run clang-format style check for C/C++/Protobuf programs. 19 | uses: jidicula/clang-format-action@v4.8.0 20 | with: 21 | clang-format-version: '13' 22 | check-path: ${{ matrix.path['check'] }} 23 | exclude-regex: ${{ matrix.path['exclude'] }} 24 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /examples/LoRaDumpRegisters/LoRaDumpRegisters.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | /* 8 | LoRa register dump 9 | 10 | This examples shows how to inspect and output the LoRa radio's 11 | registers on the Serial interface 12 | */ 13 | #include 14 | #include "M5_SX127X.h" 15 | 16 | // Module Connect Pins Config 17 | #define CS_PIN 5 18 | #define RST_PIN 13 19 | #define IRQ_PIN 34 20 | 21 | #define LORA_MISO 19 22 | #define LORA_MOSI 23 23 | #define LORA_SCLK 18 24 | 25 | // LoRa Parameters Config 26 | // #define LORA_FREQ 433E6 27 | #define LORA_FREQ 868E6 28 | #define LORA_SF 12 29 | #define LORA_BW 125E3 30 | #define LORA_TX_POWER 17 31 | 32 | void setup() { 33 | Serial.begin(115200); // initialize serial 34 | Serial.println("LoRa Dump Registers"); 35 | SPI.begin(LORA_SCLK, LORA_MISO, LORA_MOSI, -1); // SCK, MISO, MOSI, SS 36 | LoRa.setSPI(&SPI); 37 | LoRa.setPins(CS_PIN, RST_PIN, IRQ_PIN); // set CS, reset, IRQ pin 38 | while (!LoRa.begin(LORA_FREQ)) { 39 | Serial.println("LoRa init fail."); 40 | delay(1000); 41 | } 42 | 43 | LoRa.setTxPower(LORA_TX_POWER); 44 | LoRa.setSignalBandwidth(LORA_BW); 45 | LoRa.setSpreadingFactor(LORA_SF); 46 | 47 | LoRa.dumpRegisters(Serial); 48 | } 49 | 50 | void loop() { 51 | } 52 | -------------------------------------------------------------------------------- /examples/LoRaSender/LoRaSender.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | #include 7 | #include "M5_SX127X.h" 8 | 9 | // Module Connect Pins Config 10 | #define CS_PIN 5 11 | #define RST_PIN 13 12 | #define IRQ_PIN 34 13 | 14 | #define LORA_MISO 19 15 | #define LORA_MOSI 23 16 | #define LORA_SCLK 18 17 | 18 | // LoRa Parameters Config 19 | // #define LORA_FREQ 433E6 20 | #define LORA_FREQ 868E6 21 | #define LORA_SF 12 22 | #define LORA_BW 125E3 23 | #define LORA_TX_POWER 17 24 | 25 | int counter = 0; 26 | 27 | void setup() { 28 | Serial.begin(115200); 29 | 30 | Serial.println("LoRa Sender"); 31 | SPI.begin(LORA_SCLK, LORA_MISO, LORA_MOSI, -1); // SCK, MISO, MOSI, SS 32 | LoRa.setSPI(&SPI); 33 | LoRa.setPins(CS_PIN, RST_PIN, IRQ_PIN); // set CS, reset, IRQ pin 34 | while (!LoRa.begin(LORA_FREQ)) { 35 | Serial.println("LoRa init fail."); 36 | delay(1000); 37 | } 38 | 39 | LoRa.setTxPower(LORA_TX_POWER); 40 | LoRa.setSignalBandwidth(LORA_BW); 41 | LoRa.setSpreadingFactor(LORA_SF); 42 | } 43 | 44 | void loop() { 45 | Serial.print("Sending packet: "); 46 | Serial.println(counter); 47 | 48 | // send packet 49 | LoRa.beginPacket(); 50 | LoRa.print("hello "); 51 | LoRa.print(counter); 52 | LoRa.endPacket(); 53 | 54 | counter++; 55 | 56 | delay(5000); 57 | } 58 | -------------------------------------------------------------------------------- /examples/LoRaReceiver/LoRaReceiver.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | #include 7 | #include "M5_SX127X.h" 8 | 9 | // Module Connect Pins Config 10 | #define CS_PIN 5 11 | #define RST_PIN 13 12 | #define IRQ_PIN 34 13 | 14 | #define LORA_MISO 19 15 | #define LORA_MOSI 23 16 | #define LORA_SCLK 18 17 | 18 | // LoRa Parameters Config 19 | // #define LORA_FREQ 433E6 20 | #define LORA_FREQ 868E6 21 | #define LORA_SF 12 22 | #define LORA_BW 125E3 23 | #define LORA_TX_POWER 17 24 | 25 | void setup() { 26 | Serial.begin(115200); 27 | 28 | Serial.println("LoRa Receiver"); 29 | SPI.begin(LORA_SCLK, LORA_MISO, LORA_MOSI, -1); // SCK, MISO, MOSI, SS 30 | LoRa.setSPI(&SPI); 31 | LoRa.setPins(CS_PIN, RST_PIN, IRQ_PIN); // set CS, reset, IRQ pin 32 | while (!LoRa.begin(LORA_FREQ)) { 33 | Serial.println("LoRa init fail."); 34 | delay(1000); 35 | } 36 | 37 | LoRa.setTxPower(LORA_TX_POWER); 38 | LoRa.setSignalBandwidth(LORA_BW); 39 | LoRa.setSpreadingFactor(LORA_SF); 40 | } 41 | 42 | void loop() { 43 | // try to parse packet 44 | int packetSize = LoRa.parsePacket(); 45 | if (packetSize) { 46 | // received a packet 47 | Serial.print("Received packet '"); 48 | 49 | // read packet 50 | while (LoRa.available()) { 51 | Serial.print((char)LoRa.read()); 52 | } 53 | 54 | // print RSSI of packet 55 | Serial.print("' with RSSI "); 56 | Serial.println(LoRa.packetRssi()); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /examples/LoRaSenderNonBlocking/LoRaSenderNonBlocking.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | #include 7 | #include "M5_SX127X.h" 8 | 9 | // Module Connect Pins Config 10 | #define CS_PIN 5 11 | #define RST_PIN 13 12 | #define IRQ_PIN 34 13 | 14 | #define LORA_MISO 19 15 | #define LORA_MOSI 23 16 | #define LORA_SCLK 18 17 | 18 | // LoRa Parameters Config 19 | // #define LORA_FREQ 433E6 20 | #define LORA_FREQ 868E6 21 | #define LORA_SF 12 22 | #define LORA_BW 125E3 23 | #define LORA_TX_POWER 17 24 | 25 | int counter = 0; 26 | 27 | void setup() { 28 | Serial.begin(115200); 29 | 30 | Serial.println("LoRa Sender non-blocking"); 31 | SPI.begin(LORA_SCLK, LORA_MISO, LORA_MOSI, -1); // SCK, MISO, MOSI, SS 32 | LoRa.setSPI(&SPI); 33 | LoRa.setPins(CS_PIN, RST_PIN, IRQ_PIN); // set CS, reset, IRQ pin 34 | while (!LoRa.begin(LORA_FREQ)) { 35 | Serial.println("LoRa init fail."); 36 | delay(1000); 37 | } 38 | 39 | LoRa.setTxPower(LORA_TX_POWER); 40 | LoRa.setSignalBandwidth(LORA_BW); 41 | LoRa.setSpreadingFactor(LORA_SF); 42 | } 43 | 44 | void loop() { 45 | // wait until the radio is ready to send a packet 46 | while (LoRa.beginPacket() == 0) { 47 | Serial.print("waiting for radio ... "); 48 | delay(100); 49 | } 50 | 51 | Serial.print("Sending packet non-blocking: "); 52 | Serial.println(counter); 53 | 54 | // send in async / non-blocking mode 55 | LoRa.beginPacket(); 56 | LoRa.print("hello "); 57 | LoRa.print(counter); 58 | LoRa.endPacket(true); // true = async / non-blocking mode 59 | 60 | counter++; 61 | } 62 | -------------------------------------------------------------------------------- /examples/LoRaReceiverCallback/LoRaReceiverCallback.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | #include 7 | #include "M5_SX127X.h" 8 | 9 | // Module Connect Pins Config 10 | #define CS_PIN 5 11 | #define RST_PIN 13 12 | #define IRQ_PIN 34 13 | 14 | #define LORA_MISO 19 15 | #define LORA_MOSI 23 16 | #define LORA_SCLK 18 17 | 18 | // LoRa Parameters Config 19 | // #define LORA_FREQ 433E6 20 | #define LORA_FREQ 868E6 21 | #define LORA_SF 12 22 | #define LORA_BW 125E3 23 | #define LORA_TX_POWER 17 24 | 25 | void onReceive(int packetSize); 26 | 27 | void setup() { 28 | Serial.begin(115200); 29 | 30 | Serial.println("LoRa Receiver Callback"); 31 | SPI.begin(LORA_SCLK, LORA_MISO, LORA_MOSI, -1); // SCK, MISO, MOSI, SS 32 | LoRa.setSPI(&SPI); 33 | LoRa.setPins(CS_PIN, RST_PIN, IRQ_PIN); // set CS, reset, IRQ pin 34 | while (!LoRa.begin(LORA_FREQ)) { 35 | Serial.println("LoRa init fail."); 36 | delay(1000); 37 | } 38 | 39 | LoRa.setTxPower(LORA_TX_POWER); 40 | LoRa.setSignalBandwidth(LORA_BW); 41 | LoRa.setSpreadingFactor(LORA_SF); 42 | 43 | // Uncomment the next line to disable the default AGC and set LNA gain, 44 | // values between 1 - 6 are supported LoRa.setGain(6); 45 | 46 | // register the receive callback 47 | LoRa.onReceive(onReceive); 48 | 49 | // put the radio into receive mode 50 | LoRa.receive(); 51 | } 52 | 53 | void loop() { 54 | // do nothing 55 | } 56 | 57 | void onReceive(int packetSize) { 58 | // received a packet 59 | Serial.print("Received packet '"); 60 | 61 | // read packet 62 | for (int i = 0; i < packetSize; i++) { 63 | Serial.print((char)LoRa.read()); 64 | } 65 | 66 | // print RSSI of packet 67 | Serial.print("' with RSSI "); 68 | Serial.println(LoRa.packetRssi()); 69 | } 70 | -------------------------------------------------------------------------------- /examples/LoRaSenderNonBlockingCallback/LoRaSenderNonBlockingCallback.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | #include 7 | #include "M5_SX127X.h" 8 | 9 | // Module Connect Pins Config 10 | #define CS_PIN 5 11 | #define RST_PIN 13 12 | #define IRQ_PIN 34 13 | 14 | #define LORA_MISO 19 15 | #define LORA_MOSI 23 16 | #define LORA_SCLK 18 17 | 18 | // LoRa Parameters Config 19 | // #define LORA_FREQ 433E6 20 | #define LORA_FREQ 868E6 21 | #define LORA_SF 12 22 | #define LORA_BW 125E3 23 | #define LORA_TX_POWER 17 24 | 25 | int counter = 0; 26 | 27 | void onTxDone(); 28 | boolean runEvery(unsigned long interval); 29 | 30 | void setup() { 31 | Serial.begin(115200); 32 | 33 | Serial.println("LoRa Sender non-blocking Callback"); 34 | SPI.begin(LORA_SCLK, LORA_MISO, LORA_MOSI, -1); // SCK, MISO, MOSI, SS 35 | LoRa.setSPI(&SPI); 36 | LoRa.setPins(CS_PIN, RST_PIN, IRQ_PIN); // set CS, reset, IRQ pin 37 | while (!LoRa.begin(LORA_FREQ)) { 38 | Serial.println("LoRa init fail."); 39 | delay(1000); 40 | } 41 | 42 | LoRa.setTxPower(LORA_TX_POWER); 43 | LoRa.setSignalBandwidth(LORA_BW); 44 | LoRa.setSpreadingFactor(LORA_SF); 45 | 46 | LoRa.onTxDone(onTxDone); 47 | } 48 | 49 | void loop() { 50 | if (runEvery(5000)) { // repeat every 5000 millis 51 | 52 | Serial.print("Sending packet non-blocking: "); 53 | Serial.println(counter); 54 | 55 | // send in async / non-blocking mode 56 | LoRa.beginPacket(); 57 | LoRa.print("hello "); 58 | LoRa.print(counter); 59 | LoRa.endPacket(true); // true = async / non-blocking mode 60 | 61 | counter++; 62 | } 63 | } 64 | 65 | void onTxDone() { 66 | Serial.println("TxDone"); 67 | } 68 | 69 | boolean runEvery(unsigned long interval) { 70 | static unsigned long previousMillis = 0; 71 | unsigned long currentMillis = millis(); 72 | if (currentMillis - previousMillis >= interval) { 73 | previousMillis = currentMillis; 74 | return true; 75 | } 76 | return false; 77 | } 78 | -------------------------------------------------------------------------------- /examples/LoRaCADCallback/LoRaCADCallback.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | #include 7 | #include "M5_SX127X.h" 8 | 9 | // Module Connect Pins Config 10 | #define CS_PIN 5 11 | #define RST_PIN 13 12 | #define IRQ_PIN 34 13 | 14 | #define LORA_MISO 19 15 | #define LORA_MOSI 23 16 | #define LORA_SCLK 18 17 | 18 | // LoRa Parameters Config 19 | // #define LORA_FREQ 433E6 20 | #define LORA_FREQ 868E6 21 | #define LORA_SF 12 22 | #define LORA_BW 125E3 23 | #define LORA_TX_POWER 17 24 | 25 | void onCadDone(boolean signalDetected); 26 | void onReceive(int packetSize); 27 | 28 | void setup() { 29 | Serial.begin(115200); 30 | Serial.println("LoRa Receiver Callback"); 31 | SPI.begin(LORA_SCLK, LORA_MISO, LORA_MOSI, -1); // SCK, MISO, MOSI, SS 32 | LoRa.setSPI(&SPI); 33 | LoRa.setPins(CS_PIN, RST_PIN, IRQ_PIN); // set CS, reset, IRQ pin 34 | while (!LoRa.begin(LORA_FREQ)) { 35 | Serial.println("LoRa init fail."); 36 | delay(1000); 37 | } 38 | 39 | LoRa.setTxPower(LORA_TX_POWER); 40 | LoRa.setSignalBandwidth(LORA_BW); 41 | LoRa.setSpreadingFactor(LORA_SF); 42 | 43 | // register the channel activity dectection callback 44 | LoRa.onCadDone(onCadDone); 45 | // register the receive callback 46 | LoRa.onReceive(onReceive); 47 | // put the radio into CAD mode 48 | LoRa.channelActivityDetection(); 49 | } 50 | 51 | void loop() { 52 | // do nothing 53 | } 54 | 55 | void onCadDone(boolean signalDetected) { 56 | // detect preamble 57 | if (signalDetected) { 58 | Serial.println("Signal detected"); 59 | // put the radio into continuous receive mode 60 | LoRa.receive(); 61 | } else { 62 | // try next activity dectection 63 | LoRa.channelActivityDetection(); 64 | } 65 | } 66 | 67 | void onReceive(int packetSize) { 68 | // received a packet 69 | Serial.print("Received packet '"); 70 | 71 | // read packet 72 | for (int i = 0; i < packetSize; i++) { 73 | Serial.print((char)LoRa.read()); 74 | } 75 | 76 | // print RSSI of packet 77 | Serial.print("' with RSSI "); 78 | Serial.println(LoRa.packetRssi()); 79 | 80 | // put the radio into CAD mode 81 | LoRa.channelActivityDetection(); 82 | } 83 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | # Source: 2 | # https://github.com/Tinyu-Zhao/M5-Depends/blob/main/.github/ISSUE_TEMPLATE/bug-report.yml 3 | # See: 4 | # https://docs.github.com/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms 5 | 6 | name: Bug report 7 | description: Report a problem with the code in this repository. 8 | labels: 9 | - "type: bug" 10 | body: 11 | - type: markdown 12 | attributes: 13 | value: "Thank you for opening an issue on an M5Stack Arduino library repository.\n\ 14 | To improve the speed of resolution please review the following guidelines and common troubleshooting steps below before creating the issue:\n\ 15 | Do not use GitHub issues for troubleshooting projects and issues. Instead use the forums at https://community.m5stack.com to ask questions and troubleshoot why something isn't working as expected. In many cases the problem is a common issue that you will more quickly receive help from the forum community. GitHub issues are meant for known defects in the code. If you don't know if there is a defect in the code then start with troubleshooting on the forum first." 16 | - type: textarea 17 | id: description 18 | attributes: 19 | label: Describe the bug 20 | description: A clear and concise description of what the bug is. 21 | validations: 22 | required: true 23 | - type: textarea 24 | id: reproduce 25 | attributes: 26 | label: To reproduce 27 | description: Provide the specific set of steps we can follow to reproduce the problem. 28 | placeholder: | 29 | 1. In this environment... 30 | 2. With this config... 31 | 3. Run '...' 32 | 4. See error... 33 | validations: 34 | required: true 35 | - type: textarea 36 | id: expected 37 | attributes: 38 | label: Expected behavior 39 | description: What would you expect to happen after following those instructions? 40 | validations: 41 | required: true 42 | - type: textarea 43 | id: screenshots 44 | attributes: 45 | label: Screenshots 46 | description: If applicable, add screenshots to help explain your problem. 47 | validations: 48 | required: false 49 | - type: textarea 50 | id: information 51 | attributes: 52 | label: Environment 53 | description: | 54 | If applicable, add screenshots to help explain your problem. 55 | examples: 56 | - **OS**: Ubuntu 20.04 57 | - **IDE & IDE Version**: Arduino 1.8.19 Or Platform IO v2.5.0 58 | - **Repository Version**: 0.4.0 59 | value: | 60 | - OS: 61 | - IDE &IDE Version: 62 | - Repository Version: 63 | validations: 64 | required: false 65 | - type: textarea 66 | id: additional 67 | attributes: 68 | label: Additional context 69 | description: Add any additional information here. 70 | validations: 71 | required: false 72 | - type: checkboxes 73 | id: checklist 74 | attributes: 75 | label: Issue checklist 76 | description: Please double-check that you have done each of the following things before submitting the issue. 77 | options: 78 | - label: I searched for previous reports in [the issue tracker](https://github.com/m5stack/M5Core2/issues?q=) 79 | required: true 80 | - label: My report contains all necessary details 81 | required: true 82 | -------------------------------------------------------------------------------- /examples/LoRaSetSyncWord/LoRaSetSyncWord.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | /* 8 | LoRa Duplex communication with Sync Word 9 | 10 | Sends a message every half second, and polls continually 11 | for new incoming messages. Sets the LoRa radio's Sync Word. 12 | 13 | The Sync Word is basically the radio's network ID. Radios with different 14 | Sync Words will not receive each other's transmissions. This is one way you 15 | can filter out radios you want to ignore, without making an addressing scheme. 16 | 17 | See the Semtech datasheet, http://www.semtech.com/images/datasheet/sx1276.pdf 18 | for more on Sync Word. 19 | 20 | created 28 April 2017 21 | by Tom Igoe 22 | */ 23 | #include 24 | #include "M5_SX127X.h" 25 | 26 | // Module Connect Pins Config 27 | #define CS_PIN 5 28 | #define RST_PIN 13 29 | #define IRQ_PIN 34 30 | 31 | #define LORA_MISO 19 32 | #define LORA_MOSI 23 33 | #define LORA_SCLK 18 34 | 35 | // LoRa Parameters Config 36 | // #define LORA_FREQ 433E6 37 | #define LORA_FREQ 868E6 38 | #define LORA_SF 12 39 | #define LORA_BW 125E3 40 | #define LORA_TX_POWER 17 41 | 42 | #define LORA_SYNC_WORD 0xF3 43 | 44 | byte msgCount = 0; // count of outgoing messages 45 | int interval = 2000; // interval between sends 46 | long lastSendTime = 0; // time of last packet send 47 | 48 | void sendMessage(String outgoing); 49 | void onReceive(int packetSize); 50 | 51 | void setup() { 52 | Serial.begin(115200); // initialize serial 53 | 54 | Serial.println("LoRa Duplex - Set sync word"); 55 | SPI.begin(LORA_SCLK, LORA_MISO, LORA_MOSI, -1); // SCK, MISO, MOSI, SS 56 | LoRa.setSPI(&SPI); 57 | LoRa.setPins(CS_PIN, RST_PIN, IRQ_PIN); // set CS, reset, IRQ pin 58 | while (!LoRa.begin(LORA_FREQ)) { 59 | Serial.println("LoRa init fail."); 60 | delay(1000); 61 | } 62 | 63 | LoRa.setTxPower(LORA_TX_POWER); 64 | LoRa.setSignalBandwidth(LORA_BW); 65 | LoRa.setSpreadingFactor(LORA_SF); 66 | 67 | LoRa.setSyncWord( 68 | LORA_SYNC_WORD); // ranges from 0-0xFF, default 0x34, see API docs 69 | Serial.println("LoRa init succeeded."); 70 | } 71 | 72 | void loop() { 73 | if (millis() - lastSendTime > interval) { 74 | String message = "HeLoRa World! "; // send a message 75 | message += msgCount; 76 | sendMessage(message); 77 | Serial.println("Sending " + message); 78 | lastSendTime = millis(); // timestamp the message 79 | interval = random(2000) + 1000; // 2-3 seconds 80 | msgCount++; 81 | } 82 | 83 | // parse for a packet, and call onReceive with the result: 84 | onReceive(LoRa.parsePacket()); 85 | } 86 | 87 | void sendMessage(String outgoing) { 88 | LoRa.beginPacket(); // start packet 89 | LoRa.print(outgoing); // add payload 90 | LoRa.endPacket(); // finish packet and send it 91 | msgCount++; // increment message ID 92 | } 93 | 94 | void onReceive(int packetSize) { 95 | if (packetSize == 0) return; // if there's no packet, return 96 | 97 | // read packet header bytes: 98 | String incoming = ""; 99 | 100 | while (LoRa.available()) { 101 | incoming += (char)LoRa.read(); 102 | } 103 | 104 | Serial.println("Message: " + incoming); 105 | Serial.println("RSSI: " + String(LoRa.packetRssi())); 106 | Serial.println("Snr: " + String(LoRa.packetSnr())); 107 | Serial.println(); 108 | } 109 | -------------------------------------------------------------------------------- /examples/LoRaSetSpread/LoRaSetSpread.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | /* 8 | LoRa Duplex communication with Spreading Factor 9 | 10 | Sends a message every half second, and polls continually 11 | for new incoming messages. Sets the LoRa radio's spreading factor. 12 | 13 | Spreading factor affects how far apart the radio's transmissions 14 | are, across the available bandwidth. Radios with different spreading 15 | factors will not receive each other's transmissions. This is one way you 16 | can filter out radios you want to ignore, without making an addressing scheme. 17 | 18 | Spreading factor affects reliability of transmission at high rates, however, 19 | so avoid a huge spreading factor when you're sending continually. 20 | 21 | See the Semtech datasheet, http://www.semtech.com/images/datasheet/sx1276.pdf 22 | for more on Spreading Factor. 23 | 24 | created 28 April 2017 25 | by Tom Igoe 26 | */ 27 | #include 28 | #include "M5_SX127X.h" 29 | 30 | // Module Connect Pins Config 31 | #define CS_PIN 5 32 | #define RST_PIN 13 33 | #define IRQ_PIN 34 34 | 35 | #define LORA_MISO 19 36 | #define LORA_MOSI 23 37 | #define LORA_SCLK 18 38 | 39 | // LoRa Parameters Config 40 | // #define LORA_FREQ 433E6 41 | #define LORA_FREQ 868E6 42 | #define LORA_SF 12 43 | #define LORA_BW 125E3 44 | #define LORA_TX_POWER 17 45 | 46 | byte msgCount = 0; // count of outgoing messages 47 | int interval = 2000; // interval between sends 48 | long lastSendTime = 0; // time of last packet send 49 | 50 | void sendMessage(String outgoing); 51 | void onReceive(int packetSize); 52 | 53 | void setup() { 54 | Serial.begin(115200); // initialize serial 55 | 56 | Serial.println("LoRa Duplex - Set spreading factor"); 57 | SPI.begin(LORA_SCLK, LORA_MISO, LORA_MOSI, -1); // SCK, MISO, MOSI, SS 58 | LoRa.setSPI(&SPI); 59 | // override the default CS, reset, and IRQ pins (optional) 60 | LoRa.setPins(CS_PIN, RST_PIN, IRQ_PIN); // set CS, reset, IRQ pin 61 | while (!LoRa.begin(LORA_FREQ)) { 62 | Serial.println("LoRa init fail."); 63 | delay(1000); 64 | } 65 | 66 | LoRa.setTxPower(LORA_TX_POWER); 67 | LoRa.setSignalBandwidth(LORA_BW); 68 | LoRa.setSpreadingFactor(LORA_SF); 69 | Serial.println("LoRa init succeeded."); 70 | } 71 | 72 | void loop() { 73 | if (millis() - lastSendTime > interval) { 74 | String message = "HeLoRa World! "; // send a message 75 | message += msgCount; 76 | sendMessage(message); 77 | Serial.println("Sending " + message); 78 | lastSendTime = millis(); // timestamp the message 79 | interval = random(2000) + 1000; // 2-3 seconds 80 | msgCount++; 81 | } 82 | 83 | // parse for a packet, and call onReceive with the result: 84 | onReceive(LoRa.parsePacket()); 85 | } 86 | 87 | void sendMessage(String outgoing) { 88 | LoRa.beginPacket(); // start packet 89 | LoRa.print(outgoing); // add payload 90 | LoRa.endPacket(); // finish packet and send it 91 | msgCount++; // increment message ID 92 | } 93 | 94 | void onReceive(int packetSize) { 95 | if (packetSize == 0) return; // if there's no packet, return 96 | 97 | // read packet header bytes: 98 | String incoming = ""; 99 | 100 | while (LoRa.available()) { 101 | incoming += (char)LoRa.read(); 102 | } 103 | 104 | Serial.println("Message: " + incoming); 105 | Serial.println("RSSI: " + String(LoRa.packetRssi())); 106 | Serial.println("Snr: " + String(LoRa.packetSnr())); 107 | Serial.println(); 108 | } 109 | -------------------------------------------------------------------------------- /src/M5_SX127X.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Sandeep Mistry. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full 3 | // license information. 4 | 5 | #ifndef _M5_SX127X_H_ 6 | #define _M5_SX127X_H_ 7 | 8 | #include 9 | #include 10 | 11 | #define LORA_DEFAULT_SPI SPI 12 | #define LORA_DEFAULT_SPI_FREQUENCY 8E6 13 | #define LORA_DEFAULT_SS_PIN 10 14 | #define LORA_DEFAULT_RESET_PIN 9 15 | #define LORA_DEFAULT_DIO0_PIN 2 16 | 17 | #define PA_OUTPUT_RFO_PIN 0 18 | #define PA_OUTPUT_PA_BOOST_PIN 1 19 | 20 | class M5_SX127X : public Stream { 21 | public: 22 | M5_SX127X(); 23 | 24 | int begin(long frequency); 25 | void end(); 26 | 27 | void setSPI(SPIClass* spi); 28 | 29 | int beginPacket(int implicitHeader = false); 30 | int endPacket(bool async = false); 31 | 32 | int parsePacket(int size = 0); 33 | int packetRssi(); 34 | float packetSnr(); 35 | long packetFrequencyError(); 36 | 37 | int rssi(); 38 | 39 | // from Print 40 | virtual size_t write(uint8_t byte); 41 | virtual size_t write(const uint8_t* buffer, size_t size); 42 | 43 | // from Stream 44 | virtual int available(); 45 | virtual int read(); 46 | virtual int peek(); 47 | virtual void flush(); 48 | 49 | #ifndef ARDUINO_SAMD_MKRWAN1300 50 | void onReceive(void (*callback)(int)); 51 | void onCadDone(void (*callback)(boolean)); 52 | void onTxDone(void (*callback)()); 53 | 54 | void receive(int size = 0); 55 | void channelActivityDetection(void); 56 | #endif 57 | void idle(); 58 | void sleep(); 59 | 60 | void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); 61 | void setFrequency(long frequency); 62 | void setSpreadingFactor(int sf); 63 | void setSignalBandwidth(long sbw); 64 | void setCodingRate4(int denominator); 65 | void setPreambleLength(long length); 66 | void setSyncWord(int sw); 67 | void enableCrc(); 68 | void disableCrc(); 69 | void enableInvertIQ(); 70 | void disableInvertIQ(); 71 | void enableLowDataRateOptimize(); 72 | void disableLowDataRateOptimize(); 73 | 74 | void setOCP(uint8_t mA); // Over Current Protection control 75 | 76 | void setGain(uint8_t gain); // Set LNA gain 77 | 78 | // deprecated 79 | void crc() { 80 | enableCrc(); 81 | } 82 | void noCrc() { 83 | disableCrc(); 84 | } 85 | 86 | byte random(); 87 | 88 | void setPins(int ss = LORA_DEFAULT_SS_PIN, 89 | int reset = LORA_DEFAULT_RESET_PIN, 90 | int dio0 = LORA_DEFAULT_DIO0_PIN); 91 | void setSPI(SPIClass& spi); 92 | void setSPIFrequency(uint32_t frequency); 93 | 94 | void dumpRegisters(Stream& out); 95 | 96 | private: 97 | void explicitHeaderMode(); 98 | void implicitHeaderMode(); 99 | 100 | void handleDio0Rise(); 101 | bool isTransmitting(); 102 | 103 | int getSpreadingFactor(); 104 | long getSignalBandwidth(); 105 | 106 | void setLdoFlag(); 107 | void setLdoFlagForced(const boolean); 108 | 109 | uint8_t readRegister(uint8_t address); 110 | void writeRegister(uint8_t address, uint8_t value); 111 | uint8_t singleTransfer(uint8_t address, uint8_t value); 112 | 113 | static void onDio0Rise(); 114 | 115 | private: 116 | SPISettings _spiSettings; 117 | SPIClass* _spi; 118 | int _ss; 119 | int _reset; 120 | int _dio0; 121 | long _frequency; 122 | int _packetIndex; 123 | int _implicitHeaderMode; 124 | void (*_onReceive)(int); 125 | void (*_onCadDone)(boolean); 126 | void (*_onTxDone)(); 127 | }; 128 | 129 | extern M5_SX127X LoRa; 130 | 131 | #endif 132 | -------------------------------------------------------------------------------- /examples/LoRaSimpleNode/LoRaSimpleNode.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | /* 8 | LoRa Simple Gateway/Node Exemple 9 | 10 | This code uses InvertIQ function to create a simple Gateway/Node logic. 11 | 12 | Gateway - Sends messages with enableInvertIQ() 13 | - Receives messages with disableInvertIQ() 14 | 15 | Node - Sends messages with disableInvertIQ() 16 | - Receives messages with enableInvertIQ() 17 | 18 | With this arrangement a Gateway never receive messages from another Gateway 19 | and a Node never receive message from another Node. 20 | Only Gateway to Node and vice versa. 21 | 22 | This code receives messages and sends a message every second. 23 | 24 | InvertIQ function basically invert the LoRa I and Q signals. 25 | 26 | See the Semtech datasheet, http://www.semtech.com/images/datasheet/sx1276.pdf 27 | for more on InvertIQ register 0x33. 28 | 29 | created 05 August 2018 30 | by Luiz H. Cassettari 31 | */ 32 | 33 | #include 34 | #include "M5_SX127X.h" 35 | 36 | // Module Connect Pins Config 37 | #define CS_PIN 5 38 | #define RST_PIN 13 39 | #define IRQ_PIN 34 40 | 41 | #define LORA_MISO 19 42 | #define LORA_MOSI 23 43 | #define LORA_SCLK 18 44 | 45 | // LoRa Parameters Config 46 | // #define LORA_FREQ 433E6 47 | #define LORA_FREQ 868E6 48 | #define LORA_SF 12 49 | #define LORA_BW 125E3 50 | #define LORA_TX_POWER 17 51 | 52 | void LoRa_rxMode(); 53 | void LoRa_txMode(); 54 | void LoRa_sendMessage(String message); 55 | void onReceive(int packetSize); 56 | void onTxDone(); 57 | boolean runEvery(unsigned long interval); 58 | 59 | void setup() { 60 | Serial.begin(115200); // initialize serial 61 | SPI.begin(LORA_SCLK, LORA_MISO, LORA_MOSI, -1); // SCK, MISO, MOSI, SS 62 | LoRa.setSPI(&SPI); 63 | LoRa.setPins(CS_PIN, RST_PIN, IRQ_PIN); // set CS, reset, IRQ pin 64 | while (!LoRa.begin(LORA_FREQ)) { 65 | Serial.println("LoRa init fail."); 66 | delay(1000); 67 | } 68 | 69 | LoRa.setTxPower(LORA_TX_POWER); 70 | LoRa.setSignalBandwidth(LORA_BW); 71 | LoRa.setSpreadingFactor(LORA_SF); 72 | 73 | Serial.println("LoRa init succeeded."); 74 | Serial.println(); 75 | Serial.println("LoRa Simple Node"); 76 | Serial.println("Only receive messages from gateways"); 77 | Serial.println("Tx: invertIQ disable"); 78 | Serial.println("Rx: invertIQ enable"); 79 | Serial.println(); 80 | 81 | LoRa.onReceive(onReceive); 82 | LoRa.onTxDone(onTxDone); 83 | LoRa_rxMode(); 84 | } 85 | 86 | void loop() { 87 | if (runEvery(1000)) { // repeat every 1000 millis 88 | 89 | String message = "HeLoRa World! "; 90 | message += "I'm a Node! "; 91 | message += millis(); 92 | 93 | LoRa_sendMessage(message); // send a message 94 | 95 | Serial.println("Send Message!"); 96 | } 97 | } 98 | 99 | void LoRa_rxMode() { 100 | LoRa.enableInvertIQ(); // active invert I and Q signals 101 | LoRa.receive(); // set receive mode 102 | } 103 | 104 | void LoRa_txMode() { 105 | LoRa.idle(); // set standby mode 106 | LoRa.disableInvertIQ(); // normal mode 107 | } 108 | 109 | void LoRa_sendMessage(String message) { 110 | LoRa_txMode(); // set tx mode 111 | LoRa.beginPacket(); // start packet 112 | LoRa.print(message); // add payload 113 | LoRa.endPacket(true); // finish packet and send it 114 | } 115 | 116 | void onReceive(int packetSize) { 117 | String message = ""; 118 | 119 | while (LoRa.available()) { 120 | message += (char)LoRa.read(); 121 | } 122 | 123 | Serial.print("Node Receive: "); 124 | Serial.println(message); 125 | } 126 | 127 | void onTxDone() { 128 | Serial.println("TxDone"); 129 | LoRa_rxMode(); 130 | } 131 | 132 | boolean runEvery(unsigned long interval) { 133 | static unsigned long previousMillis = 0; 134 | unsigned long currentMillis = millis(); 135 | if (currentMillis - previousMillis >= interval) { 136 | previousMillis = currentMillis; 137 | return true; 138 | } 139 | return false; 140 | } 141 | -------------------------------------------------------------------------------- /examples/LoRaSimpleGateway/LoRaSimpleGateway.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | /* 8 | LoRa Simple Gateway/Node Exemple 9 | 10 | This code uses InvertIQ function to create a simple Gateway/Node logic. 11 | 12 | Gateway - Sends messages with enableInvertIQ() 13 | - Receives messages with disableInvertIQ() 14 | 15 | Node - Sends messages with disableInvertIQ() 16 | - Receives messages with enableInvertIQ() 17 | 18 | With this arrangement a Gateway never receive messages from another Gateway 19 | and a Node never receive message from another Node. 20 | Only Gateway to Node and vice versa. 21 | 22 | This code receives messages and sends a message every second. 23 | 24 | InvertIQ function basically invert the LoRa I and Q signals. 25 | 26 | See the Semtech datasheet, http://www.semtech.com/images/datasheet/sx1276.pdf 27 | for more on InvertIQ register 0x33. 28 | 29 | created 05 August 2018 30 | by Luiz H. Cassettari 31 | */ 32 | 33 | #include 34 | #include "M5_SX127X.h" 35 | 36 | // Module Connect Pins Config 37 | #define CS_PIN 5 38 | #define RST_PIN 13 39 | #define IRQ_PIN 34 40 | 41 | #define LORA_MISO 19 42 | #define LORA_MOSI 23 43 | #define LORA_SCLK 18 44 | 45 | // LoRa Parameters Config 46 | // #define LORA_FREQ 433E6 47 | #define LORA_FREQ 868E6 48 | #define LORA_SF 12 49 | #define LORA_BW 125E3 50 | #define LORA_TX_POWER 17 51 | 52 | void LoRa_rxMode(); 53 | void LoRa_txMode(); 54 | void LoRa_sendMessage(String message); 55 | void onReceive(int packetSize); 56 | void onTxDone(); 57 | boolean runEvery(unsigned long interval); 58 | 59 | void setup() { 60 | Serial.begin(115200); // initialize serial 61 | SPI.begin(LORA_SCLK, LORA_MISO, LORA_MOSI, -1); // SCK, MISO, MOSI, SS 62 | LoRa.setSPI(&SPI); 63 | LoRa.setPins(CS_PIN, RST_PIN, IRQ_PIN); // set CS, reset, IRQ pin 64 | while (!LoRa.begin(LORA_FREQ)) { 65 | Serial.println("LoRa init fail."); 66 | delay(1000); 67 | } 68 | 69 | LoRa.setTxPower(LORA_TX_POWER); 70 | LoRa.setSignalBandwidth(LORA_BW); 71 | LoRa.setSpreadingFactor(LORA_SF); 72 | 73 | Serial.println("LoRa init succeeded."); 74 | Serial.println(); 75 | Serial.println("LoRa Simple Gateway"); 76 | Serial.println("Only receive messages from nodes"); 77 | Serial.println("Tx: invertIQ enable"); 78 | Serial.println("Rx: invertIQ disable"); 79 | Serial.println(); 80 | 81 | LoRa.onReceive(onReceive); 82 | LoRa.onTxDone(onTxDone); 83 | LoRa_rxMode(); 84 | } 85 | 86 | void loop() { 87 | if (runEvery(5000)) { // repeat every 5000 millis 88 | 89 | String message = "HeLoRa World! "; 90 | message += "I'm a Gateway! "; 91 | message += millis(); 92 | 93 | LoRa_sendMessage(message); // send a message 94 | 95 | Serial.println("Send Message!"); 96 | } 97 | } 98 | 99 | void LoRa_rxMode() { 100 | LoRa.disableInvertIQ(); // normal mode 101 | LoRa.receive(); // set receive mode 102 | } 103 | 104 | void LoRa_txMode() { 105 | LoRa.idle(); // set standby mode 106 | LoRa.enableInvertIQ(); // active invert I and Q signals 107 | } 108 | 109 | void LoRa_sendMessage(String message) { 110 | LoRa_txMode(); // set tx mode 111 | LoRa.beginPacket(); // start packet 112 | LoRa.print(message); // add payload 113 | LoRa.endPacket(true); // finish packet and send it 114 | } 115 | 116 | void onReceive(int packetSize) { 117 | String message = ""; 118 | 119 | while (LoRa.available()) { 120 | message += (char)LoRa.read(); 121 | } 122 | 123 | Serial.print("Gateway Receive: "); 124 | Serial.println(message); 125 | } 126 | 127 | void onTxDone() { 128 | Serial.println("TxDone"); 129 | LoRa_rxMode(); 130 | } 131 | 132 | boolean runEvery(unsigned long interval) { 133 | static unsigned long previousMillis = 0; 134 | unsigned long currentMillis = millis(); 135 | if (currentMillis - previousMillis >= interval) { 136 | previousMillis = currentMillis; 137 | return true; 138 | } 139 | return false; 140 | } 141 | -------------------------------------------------------------------------------- /examples/LoRaDuplex/LoRaDuplex.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | /* 8 | LoRa Duplex communication 9 | 10 | Sends a message every half second, and polls continually 11 | for new incoming messages. Implements a one-byte addressing scheme, 12 | with 0xFF as the broadcast address. 13 | 14 | Uses readString() from Stream class to read payload. The Stream class' 15 | timeout may affect other functuons, like the radio's callback. For an 16 | 17 | created 28 April 2017 18 | by Tom Igoe 19 | */ 20 | #include 21 | #include "M5_SX127X.h" 22 | 23 | // Module Connect Pins Config 24 | #define CS_PIN 5 25 | #define RST_PIN 13 26 | #define IRQ_PIN 34 27 | 28 | #define LORA_MISO 19 29 | #define LORA_MOSI 23 30 | #define LORA_SCLK 18 31 | 32 | // LoRa Parameters Config 33 | // #define LORA_FREQ 433E6 34 | #define LORA_FREQ 868E6 35 | #define LORA_SF 12 36 | #define LORA_BW 125E3 37 | #define LORA_TX_POWER 17 38 | 39 | String outgoing; // outgoing message 40 | byte msgCount = 0; // count of outgoing messages 41 | byte localAddress = 0xBB; // address of this device 42 | byte destination = 0xFF; // destination to send to 43 | long lastSendTime = 0; // last send time 44 | int interval = 2000; // interval between sends 45 | 46 | void sendMessage(String outgoing); 47 | void onReceive(int packetSize); 48 | 49 | void setup() { 50 | Serial.begin(115200); // initialize serial 51 | 52 | Serial.println("LoRa Duplex"); 53 | SPI.begin(LORA_SCLK, LORA_MISO, LORA_MOSI, -1); // SCK, MISO, MOSI, SS 54 | LoRa.setSPI(&SPI); 55 | LoRa.setPins(CS_PIN, RST_PIN, IRQ_PIN); // set CS, reset, IRQ pin 56 | while (!LoRa.begin(LORA_FREQ)) { 57 | Serial.println("LoRa init fail."); 58 | delay(1000); 59 | } 60 | 61 | LoRa.setTxPower(LORA_TX_POWER); 62 | LoRa.setSignalBandwidth(LORA_BW); 63 | LoRa.setSpreadingFactor(LORA_SF); 64 | 65 | Serial.println("LoRa init succeeded."); 66 | } 67 | 68 | void loop() { 69 | if (millis() - lastSendTime > interval) { 70 | String message = "HeLoRa World!"; // send a message 71 | sendMessage(message); 72 | Serial.println("Sending " + message); 73 | lastSendTime = millis(); // timestamp the message 74 | interval = random(2000) + 1000; // 2-3 seconds 75 | } 76 | 77 | // parse for a packet, and call onReceive with the result: 78 | onReceive(LoRa.parsePacket()); 79 | } 80 | 81 | void sendMessage(String outgoing) { 82 | LoRa.beginPacket(); // start packet 83 | LoRa.write(destination); // add destination address 84 | LoRa.write(localAddress); // add sender address 85 | LoRa.write(msgCount); // add message ID 86 | LoRa.write(outgoing.length()); // add payload length 87 | LoRa.print(outgoing); // add payload 88 | LoRa.endPacket(); // finish packet and send it 89 | msgCount++; // increment message ID 90 | } 91 | 92 | void onReceive(int packetSize) { 93 | if (packetSize == 0) return; // if there's no packet, return 94 | 95 | // read packet header bytes: 96 | int recipient = LoRa.read(); // recipient address 97 | byte sender = LoRa.read(); // sender address 98 | byte incomingMsgId = LoRa.read(); // incoming msg ID 99 | byte incomingLength = LoRa.read(); // incoming msg length 100 | 101 | String incoming = ""; 102 | 103 | while (LoRa.available()) { 104 | incoming += (char)LoRa.read(); 105 | } 106 | 107 | if (incomingLength != incoming.length()) { // check length for error 108 | Serial.println("error: message length does not match length"); 109 | return; // skip rest of function 110 | } 111 | 112 | // if the recipient isn't this device or broadcast, 113 | if (recipient != localAddress && recipient != 0xFF) { 114 | Serial.println("This message is not for me."); 115 | return; // skip rest of function 116 | } 117 | 118 | // if message is for this device, or broadcast, print details: 119 | Serial.println("Received from: 0x" + String(sender, HEX)); 120 | Serial.println("Sent to: 0x" + String(recipient, HEX)); 121 | Serial.println("Message ID: " + String(incomingMsgId)); 122 | Serial.println("Message length: " + String(incomingLength)); 123 | Serial.println("Message: " + incoming); 124 | Serial.println("RSSI: " + String(LoRa.packetRssi())); 125 | Serial.println("Snr: " + String(LoRa.packetSnr())); 126 | Serial.println(); 127 | } 128 | -------------------------------------------------------------------------------- /examples/LoRaDuplexCallback/LoRaDuplexCallback.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | /* 8 | LoRa Duplex communication wth callback 9 | 10 | Sends a message every half second, and uses callback 11 | for new incoming messages. Implements a one-byte addressing scheme, 12 | with 0xFF as the broadcast address. 13 | 14 | Note: while sending, LoRa radio is not listening for incoming messages. 15 | Note2: when using the callback method, you can't use any of the Stream 16 | functions that rely on the timeout, such as readString, parseInt(), etc. 17 | 18 | created 28 April 2017 19 | by Tom Igoe 20 | */ 21 | #include 22 | #include "M5_SX127X.h" 23 | 24 | // Module Connect Pins Config 25 | #define CS_PIN 5 26 | #define RST_PIN 13 27 | #define IRQ_PIN 34 28 | 29 | #define LORA_MISO 19 30 | #define LORA_MOSI 23 31 | #define LORA_SCLK 18 32 | 33 | // LoRa Parameters Config 34 | // #define LORA_FREQ 433E6 35 | #define LORA_FREQ 868E6 36 | #define LORA_SF 12 37 | #define LORA_BW 125E3 38 | #define LORA_TX_POWER 17 39 | 40 | String outgoing; // outgoing message 41 | byte msgCount = 0; // count of outgoing messages 42 | byte localAddress = 0xBB; // address of this device 43 | byte destination = 0xFF; // destination to send to 44 | long lastSendTime = 0; // last send time 45 | int interval = 2000; // interval between sends 46 | 47 | void sendMessage(String outgoing); 48 | void onReceive(int packetSize); 49 | 50 | void setup() { 51 | Serial.begin(115200); // initialize serial 52 | 53 | Serial.println("LoRa Duplex with callback"); 54 | SPI.begin(LORA_SCLK, LORA_MISO, LORA_MOSI, -1); // SCK, MISO, MOSI, SS 55 | LoRa.setSPI(&SPI); 56 | LoRa.setPins(CS_PIN, RST_PIN, IRQ_PIN); // set CS, reset, IRQ pin 57 | while (!LoRa.begin(LORA_FREQ)) { 58 | Serial.println("LoRa init fail."); 59 | delay(1000); 60 | } 61 | 62 | LoRa.setTxPower(LORA_TX_POWER); 63 | LoRa.setSignalBandwidth(LORA_BW); 64 | LoRa.setSpreadingFactor(LORA_SF); 65 | 66 | LoRa.onReceive(onReceive); 67 | LoRa.receive(); 68 | Serial.println("LoRa init succeeded."); 69 | } 70 | 71 | void loop() { 72 | if (millis() - lastSendTime > interval) { 73 | String message = "HeLoRa World!"; // send a message 74 | sendMessage(message); 75 | Serial.println("Sending " + message); 76 | lastSendTime = millis(); // timestamp the message 77 | interval = random(2000) + 1000; // 2-3 seconds 78 | LoRa.receive(); // go back into receive mode 79 | } 80 | } 81 | 82 | void sendMessage(String outgoing) { 83 | LoRa.beginPacket(); // start packet 84 | LoRa.write(destination); // add destination address 85 | LoRa.write(localAddress); // add sender address 86 | LoRa.write(msgCount); // add message ID 87 | LoRa.write(outgoing.length()); // add payload length 88 | LoRa.print(outgoing); // add payload 89 | LoRa.endPacket(); // finish packet and send it 90 | msgCount++; // increment message ID 91 | } 92 | 93 | void onReceive(int packetSize) { 94 | if (packetSize == 0) return; // if there's no packet, return 95 | 96 | // read packet header bytes: 97 | int recipient = LoRa.read(); // recipient address 98 | byte sender = LoRa.read(); // sender address 99 | byte incomingMsgId = LoRa.read(); // incoming msg ID 100 | byte incomingLength = LoRa.read(); // incoming msg length 101 | 102 | String incoming = ""; // payload of packet 103 | 104 | while (LoRa.available()) { // can't use readString() in callback, so 105 | incoming += (char)LoRa.read(); // add bytes one by one 106 | } 107 | 108 | if (incomingLength != incoming.length()) { // check length for error 109 | Serial.println("error: message length does not match length"); 110 | return; // skip rest of function 111 | } 112 | 113 | // if the recipient isn't this device or broadcast, 114 | if (recipient != localAddress && recipient != 0xFF) { 115 | Serial.println("This message is not for me."); 116 | return; // skip rest of function 117 | } 118 | 119 | // if message is for this device, or broadcast, print details: 120 | Serial.println("Received from: 0x" + String(sender, HEX)); 121 | Serial.println("Sent to: 0x" + String(recipient, HEX)); 122 | Serial.println("Message ID: " + String(incomingMsgId)); 123 | Serial.println("Message length: " + String(incomingLength)); 124 | Serial.println("Message: " + incoming); 125 | Serial.println("RSSI: " + String(LoRa.packetRssi())); 126 | Serial.println("Snr: " + String(LoRa.packetSnr())); 127 | Serial.println(); 128 | } 129 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: Google 4 | AccessModifierOffset: -1 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveMacros: true 7 | AlignConsecutiveAssignments: true 8 | AlignConsecutiveDeclarations: false 9 | AlignEscapedNewlines: Left 10 | AlignOperands: true 11 | AlignTrailingComments: true 12 | AllowAllArgumentsOnNextLine: true 13 | AllowAllConstructorInitializersOnNextLine: true 14 | AllowAllParametersOfDeclarationOnNextLine: true 15 | AllowShortBlocksOnASingleLine: Never 16 | AllowShortCaseLabelsOnASingleLine: false 17 | AllowShortFunctionsOnASingleLine: false 18 | AllowShortLambdasOnASingleLine: All 19 | AllowShortIfStatementsOnASingleLine: WithoutElse 20 | AllowShortLoopsOnASingleLine: true 21 | AlwaysBreakAfterDefinitionReturnType: None 22 | AlwaysBreakAfterReturnType: None 23 | AlwaysBreakBeforeMultilineStrings: true 24 | AlwaysBreakTemplateDeclarations: Yes 25 | BinPackArguments: true 26 | BinPackParameters: true 27 | BraceWrapping: 28 | AfterCaseLabel: false 29 | AfterClass: false 30 | AfterControlStatement: false 31 | AfterEnum: false 32 | AfterFunction: false 33 | AfterNamespace: false 34 | AfterObjCDeclaration: false 35 | AfterStruct: false 36 | AfterUnion: false 37 | AfterExternBlock: false 38 | BeforeCatch: false 39 | BeforeElse: false 40 | IndentBraces: false 41 | SplitEmptyFunction: true 42 | SplitEmptyRecord: true 43 | SplitEmptyNamespace: true 44 | BreakBeforeBinaryOperators: None 45 | BreakBeforeBraces: Attach 46 | BreakBeforeInheritanceComma: false 47 | BreakInheritanceList: BeforeColon 48 | BreakBeforeTernaryOperators: true 49 | BreakConstructorInitializersBeforeComma: false 50 | BreakConstructorInitializers: BeforeColon 51 | BreakAfterJavaFieldAnnotations: false 52 | BreakStringLiterals: true 53 | ColumnLimit: 80 54 | CommentPragmas: '^ IWYU pragma:' 55 | CompactNamespaces: false 56 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 57 | ConstructorInitializerIndentWidth: 4 58 | ContinuationIndentWidth: 4 59 | Cpp11BracedListStyle: true 60 | DeriveLineEnding: true 61 | DerivePointerAlignment: true 62 | DisableFormat: false 63 | ExperimentalAutoDetectBinPacking: false 64 | FixNamespaceComments: true 65 | ForEachMacros: 66 | - foreach 67 | - Q_FOREACH 68 | - BOOST_FOREACH 69 | IncludeBlocks: Regroup 70 | IncludeCategories: 71 | - Regex: '^' 72 | Priority: 2 73 | SortPriority: 0 74 | - Regex: '^<.*\.h>' 75 | Priority: 1 76 | SortPriority: 0 77 | - Regex: '^<.*' 78 | Priority: 2 79 | SortPriority: 0 80 | - Regex: '.*' 81 | Priority: 3 82 | SortPriority: 0 83 | IncludeIsMainRegex: '([-_](test|unittest))?$' 84 | IncludeIsMainSourceRegex: '' 85 | IndentCaseLabels: true 86 | IndentGotoLabels: true 87 | IndentPPDirectives: None 88 | IndentWidth: 4 89 | IndentWrappedFunctionNames: false 90 | JavaScriptQuotes: Leave 91 | JavaScriptWrapImports: true 92 | KeepEmptyLinesAtTheStartOfBlocks: false 93 | MacroBlockBegin: '' 94 | MacroBlockEnd: '' 95 | MaxEmptyLinesToKeep: 1 96 | NamespaceIndentation: None 97 | ObjCBinPackProtocolList: Never 98 | ObjCBlockIndentWidth: 2 99 | ObjCSpaceAfterProperty: false 100 | ObjCSpaceBeforeProtocolList: true 101 | PenaltyBreakAssignment: 2 102 | PenaltyBreakBeforeFirstCallParameter: 1 103 | PenaltyBreakComment: 300 104 | PenaltyBreakFirstLessLess: 120 105 | PenaltyBreakString: 1000 106 | PenaltyBreakTemplateDeclaration: 10 107 | PenaltyExcessCharacter: 1000000 108 | PenaltyReturnTypeOnItsOwnLine: 200 109 | PointerAlignment: Left 110 | RawStringFormats: 111 | - Language: Cpp 112 | Delimiters: 113 | - cc 114 | - CC 115 | - cpp 116 | - Cpp 117 | - CPP 118 | - 'c++' 119 | - 'C++' 120 | CanonicalDelimiter: '' 121 | BasedOnStyle: google 122 | - Language: TextProto 123 | Delimiters: 124 | - pb 125 | - PB 126 | - proto 127 | - PROTO 128 | EnclosingFunctions: 129 | - EqualsProto 130 | - EquivToProto 131 | - PARSE_PARTIAL_TEXT_PROTO 132 | - PARSE_TEST_PROTO 133 | - PARSE_TEXT_PROTO 134 | - ParseTextOrDie 135 | - ParseTextProtoOrDie 136 | CanonicalDelimiter: '' 137 | BasedOnStyle: google 138 | ReflowComments: true 139 | SortIncludes: false 140 | SortUsingDeclarations: true 141 | SpaceAfterCStyleCast: false 142 | SpaceAfterLogicalNot: false 143 | SpaceAfterTemplateKeyword: true 144 | SpaceBeforeAssignmentOperators: true 145 | SpaceBeforeCpp11BracedList: false 146 | SpaceBeforeCtorInitializerColon: true 147 | SpaceBeforeInheritanceColon: true 148 | SpaceBeforeParens: ControlStatements 149 | SpaceBeforeRangeBasedForLoopColon: true 150 | SpaceInEmptyBlock: false 151 | SpaceInEmptyParentheses: false 152 | SpacesBeforeTrailingComments: 2 153 | SpacesInAngles: false 154 | SpacesInConditionalStatement: false 155 | SpacesInContainerLiterals: true 156 | SpacesInCStyleCastParentheses: false 157 | SpacesInParentheses: false 158 | SpacesInSquareBrackets: false 159 | SpaceBeforeSquareBrackets: false 160 | Standard: Auto 161 | StatementMacros: 162 | - Q_UNUSED 163 | - QT_REQUIRE_VERSION 164 | TabWidth: 4 165 | UseCRLF: false 166 | UseTab: Never 167 | ... 168 | -------------------------------------------------------------------------------- /src/M5_SX127X.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Sandeep Mistry. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full 3 | // license information. 4 | 5 | #include 6 | 7 | // registers 8 | #define REG_FIFO 0x00 9 | #define REG_OP_MODE 0x01 10 | #define REG_FRF_MSB 0x06 11 | #define REG_FRF_MID 0x07 12 | #define REG_FRF_LSB 0x08 13 | #define REG_PA_CONFIG 0x09 14 | #define REG_OCP 0x0b 15 | #define REG_LNA 0x0c 16 | #define REG_FIFO_ADDR_PTR 0x0d 17 | #define REG_FIFO_TX_BASE_ADDR 0x0e 18 | #define REG_FIFO_RX_BASE_ADDR 0x0f 19 | #define REG_FIFO_RX_CURRENT_ADDR 0x10 20 | #define REG_IRQ_FLAGS 0x12 21 | #define REG_RX_NB_BYTES 0x13 22 | #define REG_PKT_SNR_VALUE 0x19 23 | #define REG_PKT_RSSI_VALUE 0x1a 24 | #define REG_RSSI_VALUE 0x1b 25 | #define REG_MODEM_CONFIG_1 0x1d 26 | #define REG_MODEM_CONFIG_2 0x1e 27 | #define REG_PREAMBLE_MSB 0x20 28 | #define REG_PREAMBLE_LSB 0x21 29 | #define REG_PAYLOAD_LENGTH 0x22 30 | #define REG_MODEM_CONFIG_3 0x26 31 | #define REG_FREQ_ERROR_MSB 0x28 32 | #define REG_FREQ_ERROR_MID 0x29 33 | #define REG_FREQ_ERROR_LSB 0x2a 34 | #define REG_RSSI_WIDEBAND 0x2c 35 | #define REG_DETECTION_OPTIMIZE 0x31 36 | #define REG_INVERTIQ 0x33 37 | #define REG_DETECTION_THRESHOLD 0x37 38 | #define REG_SYNC_WORD 0x39 39 | #define REG_INVERTIQ2 0x3b 40 | #define REG_DIO_MAPPING_1 0x40 41 | #define REG_VERSION 0x42 42 | #define REG_PA_DAC 0x4d 43 | 44 | // modes 45 | #define MODE_LONG_RANGE_MODE 0x80 46 | #define MODE_SLEEP 0x00 47 | #define MODE_STDBY 0x01 48 | #define MODE_TX 0x03 49 | #define MODE_RX_CONTINUOUS 0x05 50 | #define MODE_RX_SINGLE 0x06 51 | #define MODE_CAD 0x07 52 | 53 | // PA config 54 | #define PA_BOOST 0x80 55 | 56 | // IRQ masks 57 | #define IRQ_TX_DONE_MASK 0x08 58 | #define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20 59 | #define IRQ_RX_DONE_MASK 0x40 60 | #define IRQ_CAD_DONE_MASK 0x04 61 | #define IRQ_CAD_DETECTED_MASK 0x01 62 | 63 | #define RF_MID_BAND_THRESHOLD 525E6 64 | #define RSSI_OFFSET_HF_PORT 157 65 | #define RSSI_OFFSET_LF_PORT 164 66 | 67 | #define MAX_PKT_LENGTH 255 68 | 69 | #if (ESP8266 || ESP32) 70 | #define ISR_PREFIX ICACHE_RAM_ATTR 71 | #else 72 | #define ISR_PREFIX 73 | #endif 74 | 75 | M5_SX127X::M5_SX127X() 76 | : _spiSettings(LORA_DEFAULT_SPI_FREQUENCY, MSBFIRST, SPI_MODE0), 77 | _spi(&LORA_DEFAULT_SPI), 78 | _ss(LORA_DEFAULT_SS_PIN), 79 | _reset(LORA_DEFAULT_RESET_PIN), 80 | _dio0(LORA_DEFAULT_DIO0_PIN), 81 | _frequency(0), 82 | _packetIndex(0), 83 | _implicitHeaderMode(0), 84 | _onReceive(NULL), 85 | _onCadDone(NULL), 86 | _onTxDone(NULL) { 87 | // overide Stream timeout value 88 | setTimeout(0); 89 | } 90 | 91 | int M5_SX127X::begin(long frequency) { 92 | #if defined(ARDUINO_SAMD_MKRWAN1300) || defined(ARDUINO_SAMD_MKRWAN1310) 93 | pinMode(LORA_IRQ_DUMB, OUTPUT); 94 | digitalWrite(LORA_IRQ_DUMB, LOW); 95 | 96 | // Hardware reset 97 | pinMode(LORA_BOOT0, OUTPUT); 98 | digitalWrite(LORA_BOOT0, LOW); 99 | 100 | pinMode(LORA_RESET, OUTPUT); 101 | digitalWrite(LORA_RESET, HIGH); 102 | delay(200); 103 | digitalWrite(LORA_RESET, LOW); 104 | delay(200); 105 | digitalWrite(LORA_RESET, HIGH); 106 | delay(50); 107 | #endif 108 | 109 | // setup pins 110 | pinMode(_ss, OUTPUT); 111 | // set SS high 112 | digitalWrite(_ss, HIGH); 113 | 114 | if (_reset != -1) { 115 | pinMode(_reset, OUTPUT); 116 | 117 | // perform reset 118 | digitalWrite(_reset, LOW); 119 | delay(10); 120 | digitalWrite(_reset, HIGH); 121 | delay(10); 122 | } 123 | 124 | // start SPI 125 | _spi->begin(); 126 | 127 | // check version 128 | uint8_t version = readRegister(REG_VERSION); 129 | if (version != 0x12) { 130 | return 0; 131 | } 132 | 133 | // put in sleep mode 134 | sleep(); 135 | 136 | // set frequency 137 | setFrequency(frequency); 138 | 139 | // set base addresses 140 | writeRegister(REG_FIFO_TX_BASE_ADDR, 0); 141 | writeRegister(REG_FIFO_RX_BASE_ADDR, 0); 142 | 143 | // set LNA boost 144 | writeRegister(REG_LNA, readRegister(REG_LNA) | 0x03); 145 | 146 | // set auto AGC 147 | writeRegister(REG_MODEM_CONFIG_3, 0x04); 148 | 149 | // set output power to 17 dBm 150 | setTxPower(17); 151 | 152 | // put in standby mode 153 | idle(); 154 | 155 | return 1; 156 | } 157 | 158 | void M5_SX127X::end() { 159 | // put in sleep mode 160 | sleep(); 161 | 162 | // stop SPI 163 | _spi->end(); 164 | } 165 | 166 | void M5_SX127X::setSPI(SPIClass* spi) { 167 | _spi = spi; 168 | } 169 | 170 | int M5_SX127X::beginPacket(int implicitHeader) { 171 | if (isTransmitting()) { 172 | return 0; 173 | } 174 | 175 | // put in standby mode 176 | idle(); 177 | 178 | if (implicitHeader) { 179 | implicitHeaderMode(); 180 | } else { 181 | explicitHeaderMode(); 182 | } 183 | 184 | // reset FIFO address and paload length 185 | writeRegister(REG_FIFO_ADDR_PTR, 0); 186 | writeRegister(REG_PAYLOAD_LENGTH, 0); 187 | 188 | return 1; 189 | } 190 | 191 | int M5_SX127X::endPacket(bool async) { 192 | if ((async) && (_onTxDone)) 193 | writeRegister(REG_DIO_MAPPING_1, 0x40); // DIO0 => TXDONE 194 | 195 | // put in TX mode 196 | writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX); 197 | 198 | if (!async) { 199 | // wait for TX done 200 | while ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0) { 201 | yield(); 202 | } 203 | // clear IRQ's 204 | writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK); 205 | } 206 | 207 | return 1; 208 | } 209 | 210 | bool M5_SX127X::isTransmitting() { 211 | if ((readRegister(REG_OP_MODE) & MODE_TX) == MODE_TX) { 212 | return true; 213 | } 214 | 215 | if (readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) { 216 | // clear IRQ's 217 | writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK); 218 | } 219 | 220 | return false; 221 | } 222 | 223 | int M5_SX127X::parsePacket(int size) { 224 | int packetLength = 0; 225 | int irqFlags = readRegister(REG_IRQ_FLAGS); 226 | 227 | if (size > 0) { 228 | implicitHeaderMode(); 229 | 230 | writeRegister(REG_PAYLOAD_LENGTH, size & 0xff); 231 | } else { 232 | explicitHeaderMode(); 233 | } 234 | 235 | // clear IRQ's 236 | writeRegister(REG_IRQ_FLAGS, irqFlags); 237 | 238 | if ((irqFlags & IRQ_RX_DONE_MASK) && 239 | (irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { 240 | // received a packet 241 | _packetIndex = 0; 242 | 243 | // read packet length 244 | if (_implicitHeaderMode) { 245 | packetLength = readRegister(REG_PAYLOAD_LENGTH); 246 | } else { 247 | packetLength = readRegister(REG_RX_NB_BYTES); 248 | } 249 | 250 | // set FIFO address to current RX address 251 | writeRegister(REG_FIFO_ADDR_PTR, 252 | readRegister(REG_FIFO_RX_CURRENT_ADDR)); 253 | 254 | // put in standby mode 255 | idle(); 256 | } else if (readRegister(REG_OP_MODE) != 257 | (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE)) { 258 | // not currently in RX mode 259 | 260 | // reset FIFO address 261 | writeRegister(REG_FIFO_ADDR_PTR, 0); 262 | 263 | // put in single RX mode 264 | writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE); 265 | } 266 | 267 | return packetLength; 268 | } 269 | 270 | int M5_SX127X::packetRssi() { 271 | return (readRegister(REG_PKT_RSSI_VALUE) - 272 | (_frequency < RF_MID_BAND_THRESHOLD ? RSSI_OFFSET_LF_PORT 273 | : RSSI_OFFSET_HF_PORT)); 274 | } 275 | 276 | float M5_SX127X::packetSnr() { 277 | return ((int8_t)readRegister(REG_PKT_SNR_VALUE)) * 0.25; 278 | } 279 | 280 | long M5_SX127X::packetFrequencyError() { 281 | int32_t freqError = 0; 282 | freqError = static_cast(readRegister(REG_FREQ_ERROR_MSB) & 0b111); 283 | freqError <<= 8L; 284 | freqError += static_cast(readRegister(REG_FREQ_ERROR_MID)); 285 | freqError <<= 8L; 286 | freqError += static_cast(readRegister(REG_FREQ_ERROR_LSB)); 287 | 288 | if (readRegister(REG_FREQ_ERROR_MSB) & 0b1000) { // Sign bit is on 289 | freqError -= 524288; // 0b1000'0000'0000'0000'0000 290 | } 291 | 292 | const float fXtal = 32E6; // FXOSC: crystal oscillator (XTAL) frequency 293 | // (2.5. Chip Specification, p. 14) 294 | const float fError = 295 | ((static_cast(freqError) * (1L << 24)) / fXtal) * 296 | (getSignalBandwidth() / 500000.0f); // p. 37 297 | 298 | return static_cast(fError); 299 | } 300 | 301 | int M5_SX127X::rssi() { 302 | return (readRegister(REG_RSSI_VALUE) - (_frequency < RF_MID_BAND_THRESHOLD 303 | ? RSSI_OFFSET_LF_PORT 304 | : RSSI_OFFSET_HF_PORT)); 305 | } 306 | 307 | size_t M5_SX127X::write(uint8_t byte) { 308 | return write(&byte, sizeof(byte)); 309 | } 310 | 311 | size_t M5_SX127X::write(const uint8_t* buffer, size_t size) { 312 | int currentLength = readRegister(REG_PAYLOAD_LENGTH); 313 | 314 | // check size 315 | if ((currentLength + size) > MAX_PKT_LENGTH) { 316 | size = MAX_PKT_LENGTH - currentLength; 317 | } 318 | 319 | // write data 320 | for (size_t i = 0; i < size; i++) { 321 | writeRegister(REG_FIFO, buffer[i]); 322 | } 323 | 324 | // update length 325 | writeRegister(REG_PAYLOAD_LENGTH, currentLength + size); 326 | 327 | return size; 328 | } 329 | 330 | int M5_SX127X::available() { 331 | return (readRegister(REG_RX_NB_BYTES) - _packetIndex); 332 | } 333 | 334 | int M5_SX127X::read() { 335 | if (!available()) { 336 | return -1; 337 | } 338 | 339 | _packetIndex++; 340 | 341 | return readRegister(REG_FIFO); 342 | } 343 | 344 | int M5_SX127X::peek() { 345 | if (!available()) { 346 | return -1; 347 | } 348 | 349 | // store current FIFO address 350 | int currentAddress = readRegister(REG_FIFO_ADDR_PTR); 351 | 352 | // read 353 | uint8_t b = readRegister(REG_FIFO); 354 | 355 | // restore FIFO address 356 | writeRegister(REG_FIFO_ADDR_PTR, currentAddress); 357 | 358 | return b; 359 | } 360 | 361 | void M5_SX127X::flush() { 362 | } 363 | 364 | #ifndef ARDUINO_SAMD_MKRWAN1300 365 | void M5_SX127X::onReceive(void (*callback)(int)) { 366 | _onReceive = callback; 367 | 368 | if (callback) { 369 | pinMode(_dio0, INPUT); 370 | #ifdef SPI_HAS_NOTUSINGINTERRUPT 371 | SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); 372 | #endif 373 | attachInterrupt(digitalPinToInterrupt(_dio0), M5_SX127X::onDio0Rise, 374 | RISING); 375 | } else { 376 | detachInterrupt(digitalPinToInterrupt(_dio0)); 377 | #ifdef SPI_HAS_NOTUSINGINTERRUPT 378 | SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); 379 | #endif 380 | } 381 | } 382 | 383 | void M5_SX127X::onCadDone(void (*callback)(boolean)) { 384 | _onCadDone = callback; 385 | 386 | if (callback) { 387 | pinMode(_dio0, INPUT); 388 | #ifdef SPI_HAS_NOTUSINGINTERRUPT 389 | SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); 390 | #endif 391 | attachInterrupt(digitalPinToInterrupt(_dio0), M5_SX127X::onDio0Rise, 392 | RISING); 393 | } else { 394 | detachInterrupt(digitalPinToInterrupt(_dio0)); 395 | #ifdef SPI_HAS_NOTUSINGINTERRUPT 396 | SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); 397 | #endif 398 | } 399 | } 400 | 401 | void M5_SX127X::onTxDone(void (*callback)()) { 402 | _onTxDone = callback; 403 | 404 | if (callback) { 405 | pinMode(_dio0, INPUT); 406 | #ifdef SPI_HAS_NOTUSINGINTERRUPT 407 | SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); 408 | #endif 409 | attachInterrupt(digitalPinToInterrupt(_dio0), M5_SX127X::onDio0Rise, 410 | RISING); 411 | } else { 412 | detachInterrupt(digitalPinToInterrupt(_dio0)); 413 | #ifdef SPI_HAS_NOTUSINGINTERRUPT 414 | SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); 415 | #endif 416 | } 417 | } 418 | 419 | void M5_SX127X::receive(int size) { 420 | writeRegister(REG_DIO_MAPPING_1, 0x00); // DIO0 => RXDONE 421 | 422 | if (size > 0) { 423 | implicitHeaderMode(); 424 | 425 | writeRegister(REG_PAYLOAD_LENGTH, size & 0xff); 426 | } else { 427 | explicitHeaderMode(); 428 | } 429 | 430 | writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS); 431 | } 432 | 433 | void M5_SX127X::channelActivityDetection(void) { 434 | writeRegister(REG_DIO_MAPPING_1, 0x80); // DIO0 => CADDONE 435 | writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_CAD); 436 | } 437 | #endif 438 | 439 | void M5_SX127X::idle() { 440 | writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY); 441 | } 442 | 443 | void M5_SX127X::sleep() { 444 | writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP); 445 | } 446 | 447 | void M5_SX127X::setTxPower(int level, int outputPin) { 448 | if (PA_OUTPUT_RFO_PIN == outputPin) { 449 | // RFO 450 | if (level < 0) { 451 | level = 0; 452 | } else if (level > 14) { 453 | level = 14; 454 | } 455 | 456 | writeRegister(REG_PA_CONFIG, 0x70 | level); 457 | } else { 458 | // PA BOOST 459 | if (level > 17) { 460 | if (level > 20) { 461 | level = 20; 462 | } 463 | 464 | // subtract 3 from level, so 18 - 20 maps to 15 - 17 465 | level -= 3; 466 | 467 | // High Power +20 dBm Operation (Semtech SX1276/77/78/79 5.4.3.) 468 | writeRegister(REG_PA_DAC, 0x87); 469 | setOCP(140); 470 | } else { 471 | if (level < 2) { 472 | level = 2; 473 | } 474 | // Default value PA_HF/LF or +17dBm 475 | writeRegister(REG_PA_DAC, 0x84); 476 | setOCP(100); 477 | } 478 | 479 | writeRegister(REG_PA_CONFIG, PA_BOOST | (level - 2)); 480 | } 481 | } 482 | 483 | void M5_SX127X::setFrequency(long frequency) { 484 | _frequency = frequency; 485 | 486 | uint64_t frf = ((uint64_t)frequency << 19) / 32000000; 487 | 488 | writeRegister(REG_FRF_MSB, (uint8_t)(frf >> 16)); 489 | writeRegister(REG_FRF_MID, (uint8_t)(frf >> 8)); 490 | writeRegister(REG_FRF_LSB, (uint8_t)(frf >> 0)); 491 | } 492 | 493 | int M5_SX127X::getSpreadingFactor() { 494 | return readRegister(REG_MODEM_CONFIG_2) >> 4; 495 | } 496 | 497 | void M5_SX127X::setSpreadingFactor(int sf) { 498 | if (sf < 6) { 499 | sf = 6; 500 | } else if (sf > 12) { 501 | sf = 12; 502 | } 503 | 504 | if (sf == 6) { 505 | writeRegister(REG_DETECTION_OPTIMIZE, 0xc5); 506 | writeRegister(REG_DETECTION_THRESHOLD, 0x0c); 507 | } else { 508 | writeRegister(REG_DETECTION_OPTIMIZE, 0xc3); 509 | writeRegister(REG_DETECTION_THRESHOLD, 0x0a); 510 | } 511 | 512 | writeRegister( 513 | REG_MODEM_CONFIG_2, 514 | (readRegister(REG_MODEM_CONFIG_2) & 0x0f) | ((sf << 4) & 0xf0)); 515 | setLdoFlag(); 516 | } 517 | 518 | long M5_SX127X::getSignalBandwidth() { 519 | byte bw = (readRegister(REG_MODEM_CONFIG_1) >> 4); 520 | 521 | switch (bw) { 522 | case 0: 523 | return 7.8E3; 524 | case 1: 525 | return 10.4E3; 526 | case 2: 527 | return 15.6E3; 528 | case 3: 529 | return 20.8E3; 530 | case 4: 531 | return 31.25E3; 532 | case 5: 533 | return 41.7E3; 534 | case 6: 535 | return 62.5E3; 536 | case 7: 537 | return 125E3; 538 | case 8: 539 | return 250E3; 540 | case 9: 541 | return 500E3; 542 | } 543 | 544 | return -1; 545 | } 546 | 547 | void M5_SX127X::setSignalBandwidth(long sbw) { 548 | int bw; 549 | 550 | if (sbw <= 7.8E3) { 551 | bw = 0; 552 | } else if (sbw <= 10.4E3) { 553 | bw = 1; 554 | } else if (sbw <= 15.6E3) { 555 | bw = 2; 556 | } else if (sbw <= 20.8E3) { 557 | bw = 3; 558 | } else if (sbw <= 31.25E3) { 559 | bw = 4; 560 | } else if (sbw <= 41.7E3) { 561 | bw = 5; 562 | } else if (sbw <= 62.5E3) { 563 | bw = 6; 564 | } else if (sbw <= 125E3) { 565 | bw = 7; 566 | } else if (sbw <= 250E3) { 567 | bw = 8; 568 | } else /*if (sbw <= 250E3)*/ { 569 | bw = 9; 570 | } 571 | 572 | writeRegister(REG_MODEM_CONFIG_1, 573 | (readRegister(REG_MODEM_CONFIG_1) & 0x0f) | (bw << 4)); 574 | setLdoFlag(); 575 | } 576 | 577 | void M5_SX127X::setLdoFlag() { 578 | // Section 4.1.1.5 579 | long symbolDuration = 580 | 1000 / (getSignalBandwidth() / (1L << getSpreadingFactor())); 581 | 582 | // Section 4.1.1.6 583 | boolean ldoOn = symbolDuration > 16; 584 | 585 | uint8_t config3 = readRegister(REG_MODEM_CONFIG_3); 586 | bitWrite(config3, 3, ldoOn); 587 | writeRegister(REG_MODEM_CONFIG_3, config3); 588 | } 589 | 590 | void M5_SX127X::setLdoFlagForced(const boolean ldoOn) { 591 | uint8_t config3 = readRegister(REG_MODEM_CONFIG_3); 592 | bitWrite(config3, 3, ldoOn); 593 | writeRegister(REG_MODEM_CONFIG_3, config3); 594 | } 595 | 596 | void M5_SX127X::setCodingRate4(int denominator) { 597 | if (denominator < 5) { 598 | denominator = 5; 599 | } else if (denominator > 8) { 600 | denominator = 8; 601 | } 602 | 603 | int cr = denominator - 4; 604 | 605 | writeRegister(REG_MODEM_CONFIG_1, 606 | (readRegister(REG_MODEM_CONFIG_1) & 0xf1) | (cr << 1)); 607 | } 608 | 609 | void M5_SX127X::setPreambleLength(long length) { 610 | writeRegister(REG_PREAMBLE_MSB, (uint8_t)(length >> 8)); 611 | writeRegister(REG_PREAMBLE_LSB, (uint8_t)(length >> 0)); 612 | } 613 | 614 | void M5_SX127X::setSyncWord(int sw) { 615 | writeRegister(REG_SYNC_WORD, sw); 616 | } 617 | 618 | void M5_SX127X::enableCrc() { 619 | writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) | 0x04); 620 | } 621 | 622 | void M5_SX127X::disableCrc() { 623 | writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) & 0xfb); 624 | } 625 | 626 | void M5_SX127X::enableInvertIQ() { 627 | writeRegister(REG_INVERTIQ, 0x66); 628 | writeRegister(REG_INVERTIQ2, 0x19); 629 | } 630 | 631 | void M5_SX127X::disableInvertIQ() { 632 | writeRegister(REG_INVERTIQ, 0x27); 633 | writeRegister(REG_INVERTIQ2, 0x1d); 634 | } 635 | 636 | void M5_SX127X::enableLowDataRateOptimize() { 637 | setLdoFlagForced(true); 638 | } 639 | 640 | void M5_SX127X::disableLowDataRateOptimize() { 641 | setLdoFlagForced(false); 642 | } 643 | 644 | void M5_SX127X::setOCP(uint8_t mA) { 645 | uint8_t ocpTrim = 27; 646 | 647 | if (mA <= 120) { 648 | ocpTrim = (mA - 45) / 5; 649 | } else if (mA <= 240) { 650 | ocpTrim = (mA + 30) / 10; 651 | } 652 | 653 | writeRegister(REG_OCP, 0x20 | (0x1F & ocpTrim)); 654 | } 655 | 656 | void M5_SX127X::setGain(uint8_t gain) { 657 | // check allowed range 658 | if (gain > 6) { 659 | gain = 6; 660 | } 661 | 662 | // set to standby 663 | idle(); 664 | 665 | // set gain 666 | if (gain == 0) { 667 | // if gain = 0, enable AGC 668 | writeRegister(REG_MODEM_CONFIG_3, 0x04); 669 | } else { 670 | // disable AGC 671 | writeRegister(REG_MODEM_CONFIG_3, 0x00); 672 | 673 | // clear Gain and set LNA boost 674 | writeRegister(REG_LNA, 0x03); 675 | 676 | // set gain 677 | writeRegister(REG_LNA, readRegister(REG_LNA) | (gain << 5)); 678 | } 679 | } 680 | 681 | byte M5_SX127X::random() { 682 | return readRegister(REG_RSSI_WIDEBAND); 683 | } 684 | 685 | void M5_SX127X::setPins(int ss, int reset, int dio0) { 686 | _ss = ss; 687 | _reset = reset; 688 | _dio0 = dio0; 689 | } 690 | 691 | void M5_SX127X::setSPI(SPIClass& spi) { 692 | _spi = &spi; 693 | } 694 | 695 | void M5_SX127X::setSPIFrequency(uint32_t frequency) { 696 | _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); 697 | } 698 | 699 | void M5_SX127X::dumpRegisters(Stream& out) { 700 | for (int i = 0; i < 128; i++) { 701 | out.print("0x"); 702 | out.print(i, HEX); 703 | out.print(": 0x"); 704 | out.println(readRegister(i), HEX); 705 | } 706 | } 707 | 708 | void M5_SX127X::explicitHeaderMode() { 709 | _implicitHeaderMode = 0; 710 | 711 | writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) & 0xfe); 712 | } 713 | 714 | void M5_SX127X::implicitHeaderMode() { 715 | _implicitHeaderMode = 1; 716 | 717 | writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) | 0x01); 718 | } 719 | 720 | void M5_SX127X::handleDio0Rise() { 721 | int irqFlags = readRegister(REG_IRQ_FLAGS); 722 | 723 | // clear IRQ's 724 | writeRegister(REG_IRQ_FLAGS, irqFlags); 725 | 726 | if ((irqFlags & IRQ_CAD_DONE_MASK) != 0) { 727 | if (_onCadDone) { 728 | _onCadDone((irqFlags & IRQ_CAD_DETECTED_MASK) != 0); 729 | } 730 | } else if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { 731 | if ((irqFlags & IRQ_RX_DONE_MASK) != 0) { 732 | // received a packet 733 | _packetIndex = 0; 734 | 735 | // read packet length 736 | int packetLength = _implicitHeaderMode 737 | ? readRegister(REG_PAYLOAD_LENGTH) 738 | : readRegister(REG_RX_NB_BYTES); 739 | 740 | // set FIFO address to current RX address 741 | writeRegister(REG_FIFO_ADDR_PTR, 742 | readRegister(REG_FIFO_RX_CURRENT_ADDR)); 743 | 744 | if (_onReceive) { 745 | _onReceive(packetLength); 746 | } 747 | } else if ((irqFlags & IRQ_TX_DONE_MASK) != 0) { 748 | if (_onTxDone) { 749 | _onTxDone(); 750 | } 751 | } 752 | } 753 | } 754 | 755 | uint8_t M5_SX127X::readRegister(uint8_t address) { 756 | return singleTransfer(address & 0x7f, 0x00); 757 | } 758 | 759 | void M5_SX127X::writeRegister(uint8_t address, uint8_t value) { 760 | singleTransfer(address | 0x80, value); 761 | } 762 | 763 | uint8_t M5_SX127X::singleTransfer(uint8_t address, uint8_t value) { 764 | uint8_t response; 765 | 766 | _spi->beginTransaction(_spiSettings); 767 | digitalWrite(_ss, LOW); 768 | _spi->transfer(address); 769 | response = _spi->transfer(value); 770 | digitalWrite(_ss, HIGH); 771 | _spi->endTransaction(); 772 | 773 | return response; 774 | } 775 | 776 | ISR_PREFIX void M5_SX127X::onDio0Rise() { 777 | LoRa.handleDio0Rise(); 778 | } 779 | 780 | M5_SX127X LoRa; 781 | --------------------------------------------------------------------------------