diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 2d4869ee..779c3533 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -11,7 +11,7 @@ assignees: '' Before submitting new issue, please check the [Wiki](https://github.com/jgromes/RadioLib/wiki) and the [API documentation](https://jgromes.github.io/RadioLib/). You might find a solution to your issue there. **Describe the bug** -A clear and concise description of what the bug is. +A clear and concise description of what the bug is. When applicable, please include debug mode output: uncomment [debug macro definitions in TypeDef.h](https://github.com/jgromes/RadioLib/blob/master/src/TypeDef.h#L36) and post the output. **To Reproduce** Minimal Arduino sketch to reproduce the behavior. Please user Markdown to style the code to make it readable (see [Markdown Cheatsheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#code)). diff --git a/.travis.yml b/.travis.yml index 0d669626..8e36b9cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ env: - BOARD="esp32:esp32:esp32" - BOARD="STM32:stm32:GenF3:pnum=BLACKPILL_F303CC" - BOARD="esp8266:esp8266:generic:xtal=80,ResetMethod=ck,CrystalFreq=26,FlashFreq=40,FlashMode=qio,eesz=512K" + # - BOARD="SparkFun:apollo3:amap3redboard" - BOARD="arduino:samd:arduino_zero_native" - BOARD="arduino:sam:arduino_due_x" - BOARD="arduino:avr:uno" @@ -29,7 +30,7 @@ before_install: - sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # install 3rd party boards - - arduino --pref "boardsmanager.additional.urls=http://arduino.esp8266.com/stable/package_esp8266com_index.json,https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json,https://github.com/stm32duino/BoardManagerFiles/raw/master/STM32/package_stm_index.json" --save-prefs 2>&1 + - arduino --pref "boardsmanager.additional.urls=http://arduino.esp8266.com/stable/package_esp8266com_index.json,https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json,https://github.com/stm32duino/BoardManagerFiles/raw/master/STM32/package_stm_index.json,https://raw.githubusercontent.com/sparkfun/Arduino_Boards/master/IDE_Board_Manager/package_sparkfun_index.json" --save-prefs 2>&1 - if [[ "$BOARD" =~ "esp8266:esp8266:" ]]; then arduino --install-boards esp8266:esp8266; export SKIP_PAT='(HTTP|MQTT).*ino'; @@ -41,6 +42,8 @@ before_install: arduino --install-boards arduino:samd; elif [[ "$BOARD" =~ "arduino:sam:" ]]; then arduino --install-boards arduino:sam; + elif [[ "$BOARD" =~ "SparkFun:apollo3:" ]]; then + arduino --install-boards SparkFun:apollo3; fi # create directory to save the library and create symbolic link diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 17d073ff..12fa8518 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,8 +15,6 @@ To report bugs or suggest new features, use the provided issue templates. Use th Issues with generic titles (e.g. "not working", "lora", etc.) will be **CLOSED** until the title is fixed, since the title is supposed to categorize the issue. The same applies for issues with very little information and extensive grammatical or formatting errors that make it difficult to find out what is the actual issue. 4. **Issues deserve some attention too.** Issues that are left for 2 weeks without response by the original author when asked for further information will be closed due to inactivity. This is to keep track of important issues, the author is encouraged to reopen the issue at a later date. -5. **LoRaLib is a separate project.** -RadioLib was created as an extension of LoRaLib for radios other than SX127x. However, LoRaLib project is still active and RadioLib is reusing its code. **Because of that, please open issues/PRs related to SX127x in [LoRaLib](https://github.com/jgromes/LoRaLib)**. ## Code style guidelines diff --git a/README.md b/README.md index 21ce28b8..0be4beb7 100644 --- a/README.md +++ b/README.md @@ -27,14 +27,21 @@ RadioLib was originally created as a driver for [__RadioShield__](https://github * __HTTP__ for modules: ESP8266 * __RTTY__ for modules: SX127x, RFM9x, SX126x, RF69, SX1231, CC1101 and nRF24L01 * __Morse Code__ for modules: SX127x, RFM9x, SX126x, RF69, SX1231, CC1101 and nRF24L01 +* __AX.25__ for modules: SX127x, RFM9x, SX126x, RF69, SX1231 and CC1101 ### Supported platforms: -* __Arduino AVR boards__ - tested on Uno, Mega and Leonardo -* __ESP8266 boards__ - NodeMCU, Wemos D1, etc. -* __ESP32 boards__ - tested on ESP-WROOM-32 -* __STM32 boards__ - tested on Nucleo L452RE-P -* __SAMD boards__ - Arduino Zero -* __SAM boards__ - Arduino Due +* __AVR__ - tested with hardware on Uno, Mega and Leonardo +* __ESP8266__ - tested with hardware on NodeMCU and Wemos D1 +* __ESP32__ - tested with hardware on ESP-WROOM-32 +* __STM32__ - tested with hardware on Nucleo L452RE-P +* __SAMD__ - Arduino Zero, Arduino MKR boards, M0 Pro etc. +* __SAM__ - Arduino Due +* __nRF52__ - Adafruit Bluefruit Feather etc. +* _Intel Curie_ - Arduino 101 +* _megaAVR_ - Arduino Uno WiFi Rev.2 etc. +* _Apollo3_ - SparkFun Artemis Redboard etc. + +The list above is by no means exhaustive. Most of RadioLib code is independent of the used platform, so as long as your board is running some Arduino-compatible core, RadioLib should work. Compilation of all examples is tested for all platoforms in __bold__ on each git push. Platforms in _italic_ are not tested on each push, but do compile and should be working. ### In development: * __SIM800C__ GSM module @@ -46,5 +53,8 @@ RadioLib was originally created as a driver for [__RadioShield__](https://github ### Where should I start? First of all, take a look at the [examples](https://github.com/jgromes/RadioLib/tree/master/examples) and the [Wiki](https://github.com/jgromes/RadioLib/wiki) - especially the [Basics](https://github.com/jgromes/RadioLib/wiki/Basics) page. There's a lot of useful information over there. Also, you should check out [RadioShield](https://github.com/jgromes/RadioShield) - open source Arduino shield that will allow you to easily connect any two wireless modules supported by RadioLib! +### Help, my module isn't working! +The fastest way to get help is by creating an [issue](https://github.com/jgromes/RadioLib/issues/new?assignees=&labels=&template=bug_report.md&title=) using the appropriate template. It is also highly recommended to try running the examples first - their functionality is tested from time to time and they should work. Finally, RadioLib is still under development, which means that sometimes, backwards-incompatible changes might be introduced. Though these are kept at minimum, sometimes it is unavoidable. You can check the [release changelog](https://github.com/jgromes/RadioLib/releases) to find out if there's been such a major change recently. + ### RadioLib doesn't support my module! What should I do? Start by creating new issue (if it doesn't exist yet). If you have some experience with Arduino and C/C++ in general, you can try to add the support yourself! Use the template files in `/extras/` folder to get started. This is by far the fastest way to implement new modules into RadioLib, since I can't be working on everything all the time. If you don't trust your programming skills enough to have a go at it yourself, don't worry. I will try to implement all requested modules, but it will take me a while. diff --git a/examples/AX25/AX25_Frames/AX25_Frames.ino b/examples/AX25/AX25_Frames/AX25_Frames.ino new file mode 100644 index 00000000..0d5ae5b6 --- /dev/null +++ b/examples/AX25/AX25_Frames/AX25_Frames.ino @@ -0,0 +1,167 @@ +/* + RadioLib AX.25 Frame Example + + This example shows how to send various + AX.25 frames using SX1278's FSK modem. + + Other modules that can be used for AX.25: + - SX127x/RFM9x + - RF69 + - SX1231 + - CC1101 + - SX126x + - nRF24 + + Using raw AX.25 frames requires some + knowledge of the protocol, refer to + AX25_Transmit for basic operation. + Frames shown in this example are not + exhaustive; all possible AX.25 frames + should be supported. +*/ + +// include the library +#include + +// SX1278 has the following connections: +// NSS pin: 10 +// DIO0 pin: 2 +// RESET pin: 9 +// DIO1 pin: 3 +SX1278 fsk = new Module(10, 2, 9, 3); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//SX1278 fsk = RadioShield.ModuleA; + +// create AX.25 client instance using the FSK module +AX25Client ax25(&fsk); + +void setup() { + Serial.begin(9600); + + // initialize SX1278 + Serial.print(F("[SX1278] Initializing ... ")); + // carrier frequency: 434.0 MHz + // bit rate: 1.2 kbps (1200 baud AFSK AX.25) + // frequency deviation: 0.5 kHz (1200 baud AFSK AX.25) + int state = fsk.beginFSK(434.0, 1.2, 0.5); + + // when using one of the non-LoRa modules for AX.25 + // (RF69, CC1101, etc.), use the basic begin() method + // int state = fsk.begin(); + + if(state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while(true); + } + + // initialize AX.25 client + Serial.print(F("[AX.25] Initializing ... ")); + // source station callsign: "N7LEM" + // source station SSID: 0 + // preamble length: 8 bytes + state = ax25.begin("N7LEM"); + if(state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while(true); + } +} + +void loop() { + // create AX.25 Unnumbered Information frame + // destination station callsign: "NJ7P" + // destination station SSID: 0 + // source station callsign: "N7LEM" + // source station SSID: 0 + // control field: UI, P/F not used, unnumbered frame + // protocol identifier: no layer 3 protocol implemented + // information field: "Hello World!" + AX25Frame frameUI("NJ7P", 0, "N7LEM", 0, AX25_CONTROL_U_UNNUMBERED_INFORMATION | + AX25_CONTROL_POLL_FINAL_DISABLED | AX25_CONTROL_UNNUMBERED_FRAME, + AX25_PID_NO_LAYER_3, "Hello World (unnumbered)!"); + + // send the frame + Serial.print(F("[AX.25] Sending UI frame ... ")); + int state = ax25.sendFrame(&frameUI); + if (state == ERR_NONE) { + // the packet was successfully transmitted + Serial.println(F("success!")); + + } else { + // some error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + + } + + delay(1000); + + // create AX.25 Receive Ready frame + // destination station callsign: "NJ7P" + // destination station SSID: 0 + // source station callsign: "N7LEM" + // source station SSID: 0 + // control field: RR, P/F not used, supervisory frame + AX25Frame frameRR("NJ7P", 0, "N7LEM", 0, AX25_CONTROL_S_RECEIVE_READY | + AX25_CONTROL_POLL_FINAL_DISABLED | AX25_CONTROL_SUPERVISORY_FRAME); + + // set receive sequence number (0 - 7) + frameRR.setRecvSequence(0); + + // send the frame + Serial.print(F("[AX.25] Sending RR frame ... ")); + state = ax25.sendFrame(&frameRR); + if (state == ERR_NONE) { + // the packet was successfully transmitted + Serial.println(F("success!")); + + } else { + // some error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + + } + + delay(1000); + + // create AX.25 Information frame + // destination station callsign: "NJ7P" + // destination station SSID: 0 + // source station callsign: "N7LEM" + // source station SSID: 0 + // control field: P/F not used, information frame + // protocol identifier: no layer 3 protocol implemented + // information field: "Hello World (numbered)!" + AX25Frame frameI("NJ7P", 0, "N7LEM", 0, AX25_CONTROL_POLL_FINAL_DISABLED | + AX25_CONTROL_INFORMATION_FRAME, AX25_PID_NO_LAYER_3, + "Hello World (numbered)!"); + + // set receive sequence number (0 - 7) + frameI.setRecvSequence(0); + + // set send sequence number (0 - 7) + frameI.setSendSequence(0); + + // send the frame + Serial.print(F("[AX.25] Sending I frame ... ")); + state = ax25.sendFrame(&frameI); + if (state == ERR_NONE) { + // the packet was successfully transmitted + Serial.println(F("success!")); + + } else { + // some error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + + } + + delay(1000); +} diff --git a/examples/AX25/AX25_Transmit/AX25_Transmit.ino b/examples/AX25/AX25_Transmit/AX25_Transmit.ino new file mode 100644 index 00000000..fa97067d --- /dev/null +++ b/examples/AX25/AX25_Transmit/AX25_Transmit.ino @@ -0,0 +1,88 @@ +/* + RadioLib AX.25 Transmit Example + + This example sends AX.25 messages using + SX1278's FSK modem. + + Other modules that can be used for AX.25: + - SX127x/RFM9x + - RF69 + - SX1231 + - CC1101 + - SX126x + - nRF24 +*/ + +// include the library +#include + +// SX1278 has the following connections: +// NSS pin: 10 +// DIO0 pin: 2 +// RESET pin: 9 +// DIO1 pin: 3 +SX1278 fsk = new Module(10, 2, 9, 3); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//SX1278 fsk = RadioShield.ModuleA; + +// create AX.25 client instance using the FSK module +AX25Client ax25(&fsk); + +void setup() { + Serial.begin(9600); + + // initialize SX1278 + Serial.print(F("[SX1278] Initializing ... ")); + // carrier frequency: 434.0 MHz + // bit rate: 1.2 kbps (1200 baud AFSK AX.25) + // frequency deviation: 0.5 kHz (1200 baud AFSK AX.25) + int state = fsk.beginFSK(434.0, 1.2, 0.5); + + // when using one of the non-LoRa modules for AX.25 + // (RF69, CC1101, etc.), use the basic begin() method + // int state = fsk.begin(); + + if(state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while(true); + } + + // initialize AX.25 client + Serial.print(F("[AX.25] Initializing ... ")); + // source station callsign: "N7LEM" + // source station SSID: 0 + // preamble length: 8 bytes + state = ax25.begin("N7LEM"); + if(state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while(true); + } +} + +void loop() { + // send AX.25 unnumbered infomration frame + Serial.print(F("[AX.25] Sending UI frame ... ")); + // destination station callsign: "NJ7P" + // destination station SSID: 0 + int state = ax25.transmit("Hello World!", "NJ7P"); + if (state == ERR_NONE) { + // the packet was successfully transmitted + Serial.println(F("success!")); + + } else { + // some error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + + } + + delay(1000); +} diff --git a/examples/CC1101/CC1101_Receive/CC1101_Receive.ino b/examples/CC1101/CC1101_Receive/CC1101_Receive.ino index 8c851088..e36c1d9a 100644 --- a/examples/CC1101/CC1101_Receive/CC1101_Receive.ino +++ b/examples/CC1101/CC1101_Receive/CC1101_Receive.ino @@ -17,10 +17,11 @@ #include // CC1101 has the following connections: -// NSS pin: 10 +// CS pin: 10 // GDO0 pin: 2 -// GDO2 pin: 3 -CC1101 cc = new Module(10, 2, 3); +// RST pin: unused +// GDO2 pin: 3 (optional) +CC1101 cc = new Module(10, 2, NC, 3); // or using RadioShield // https://github.com/jgromes/RadioShield @@ -33,8 +34,8 @@ void setup() { Serial.print(F("[CC1101] Initializing ... ")); // carrier frequency: 868.0 MHz // bit rate: 4.8 kbps - // Rx bandwidth: 325.0 kHz // frequency deviation: 48.0 kHz + // Rx bandwidth: 325.0 kHz // sync word: 0xD391 int state = cc.begin(); if (state == ERR_NONE) { diff --git a/examples/CC1101/CC1101_Receive_Address/CC1101_Receive_Address.ino b/examples/CC1101/CC1101_Receive_Address/CC1101_Receive_Address.ino index 1f1aa5bf..4cbd10f2 100644 --- a/examples/CC1101/CC1101_Receive_Address/CC1101_Receive_Address.ino +++ b/examples/CC1101/CC1101_Receive_Address/CC1101_Receive_Address.ino @@ -15,10 +15,11 @@ #include // CC1101 has the following connections: -// NSS pin: 10 +// CS pin: 10 // GDO0 pin: 2 -// GDO2 pin: 3 -CC1101 cc = new Module(10, 2, 3); +// RST pin: unused +// GDO2 pin: 3 (optional) +CC1101 cc = new Module(10, 2, NC, 3); // or using RadioShield // https://github.com/jgromes/RadioShield @@ -31,8 +32,8 @@ void setup() { Serial.print(F("[CC1101] Initializing ... ")); // carrier frequency: 868.0 MHz // bit rate: 4.8 kbps - // Rx bandwidth: 325.0 kHz // frequency deviation: 48.0 kHz + // Rx bandwidth: 325.0 kHz // sync word: 0xD391 int state = cc.begin(); if (state == ERR_NONE) { diff --git a/examples/CC1101/CC1101_Receive_Interrupt/CC1101_Receive_Interrupt.ino b/examples/CC1101/CC1101_Receive_Interrupt/CC1101_Receive_Interrupt.ino index a4307ab4..46f3279d 100644 --- a/examples/CC1101/CC1101_Receive_Interrupt/CC1101_Receive_Interrupt.ino +++ b/examples/CC1101/CC1101_Receive_Interrupt/CC1101_Receive_Interrupt.ino @@ -20,10 +20,11 @@ #include // CC1101 has the following connections: -// NSS pin: 10 +// CS pin: 10 // GDO0 pin: 2 -// GDO2 pin: 3 -CC1101 cc = new Module(10, 2, 3); +// RST pin: unused +// GDO2 pin: 3 (optional) +CC1101 cc = new Module(10, 2, NC, 3); // or using RadioShield // https://github.com/jgromes/RadioShield @@ -36,8 +37,8 @@ void setup() { Serial.print(F("[CC1101] Initializing ... ")); // carrier frequency: 868.0 MHz // bit rate: 4.8 kbps - // Rx bandwidth: 325.0 kHz // frequency deviation: 48.0 kHz + // Rx bandwidth: 325.0 kHz // sync word: 0xD391 int state = cc.begin(); if (state == ERR_NONE) { @@ -110,7 +111,7 @@ void loop() { // you can also read received data as byte array /* byte byteArr[8]; - int state = cc.receive(byteArr, 8); + int state = cc.readData(byteArr, 8); */ if (state == ERR_NONE) { diff --git a/examples/CC1101/CC1101_Settings/CC1101_Settings.ino b/examples/CC1101/CC1101_Settings/CC1101_Settings.ino index 6140a851..74b2b3b1 100644 --- a/examples/CC1101/CC1101_Settings/CC1101_Settings.ino +++ b/examples/CC1101/CC1101_Settings/CC1101_Settings.ino @@ -19,14 +19,22 @@ #include // CC1101 has the following connections: -// NSS pin: 10 +// CS pin: 10 // GDO0 pin: 2 -// GDO2 pin: 3 -CC1101 cc1 = new Module(10, 2, 3); +// RST pin: unused +// GDO2 pin: 3 (optional) +CC1101 cc1 = new Module(10, 2, NC, 3); + +// second CC1101 has different connections: +// CS pin: 9 +// GDO0 pin: 4 +// RST pin: unused +// GDO2 pin: 5 (optional) +CC1101 cc2 = new Module(9, 4, NC, 53); // or using RadioShield // https://github.com/jgromes/RadioShield -CC1101 cc2 = RadioShield.ModuleB; +//CC1101 cc3 = RadioShield.ModuleB; void setup() { Serial.begin(9600); @@ -35,8 +43,8 @@ void setup() { Serial.print(F("[CC1101] Initializing ... ")); // carrier frequency: 868.0 MHz // bit rate: 4.8 kbps - // Rx bandwidth: 325.0 kHz // frequency deviation: 48.0 kHz + // Rx bandwidth: 325.0 kHz // sync word: 0xD391 int state = cc1.begin(); if (state == ERR_NONE) { @@ -51,10 +59,10 @@ void setup() { Serial.print(F("[CC1101] Initializing ... ")); // carrier frequency: 434.0 MHz // bit rate: 32.0 kbps - // Rx bandwidth: 250.0 kHz // frequency deviation: 60.0 kHz + // Rx bandwidth: 250.0 kHz // sync word: 0xD391 - state = cc2.begin(434.0, 32.0, 250.0, 60.0); + state = cc2.begin(434.0, 32.0, 60.0, 250.0); if (state == ERR_NONE) { Serial.println(F("success!")); } else { diff --git a/examples/CC1101/CC1101_Transmit/CC1101_Transmit.ino b/examples/CC1101/CC1101_Transmit/CC1101_Transmit.ino index 87bde6e1..104fe2bb 100644 --- a/examples/CC1101/CC1101_Transmit/CC1101_Transmit.ino +++ b/examples/CC1101/CC1101_Transmit/CC1101_Transmit.ino @@ -15,10 +15,11 @@ #include // CC1101 has the following connections: -// NSS pin: 10 +// CS pin: 10 // GDO0 pin: 2 -// GDO2 pin: 3 -CC1101 cc = new Module(10, 2, 3); +// RST pin: unused +// GDO2 pin: 3 (optional) +CC1101 cc = new Module(10, 2, NC, 3); // or using RadioShield // https://github.com/jgromes/RadioShield @@ -31,8 +32,8 @@ void setup() { Serial.print(F("[CC1101] Initializing ... ")); // carrier frequency: 868.0 MHz // bit rate: 4.8 kbps - // Rx bandwidth: 325.0 kHz // frequency deviation: 48.0 kHz + // Rx bandwidth: 325.0 kHz // sync word: 0xD391 int state = cc.begin(); if (state == ERR_NONE) { diff --git a/examples/CC1101/CC1101_Transmit_Address/CC1101_Transmit_Address.ino b/examples/CC1101/CC1101_Transmit_Address/CC1101_Transmit_Address.ino index b9e8a966..7e130920 100644 --- a/examples/CC1101/CC1101_Transmit_Address/CC1101_Transmit_Address.ino +++ b/examples/CC1101/CC1101_Transmit_Address/CC1101_Transmit_Address.ino @@ -15,10 +15,11 @@ #include // CC1101 has the following connections: -// NSS pin: 10 +// CS pin: 10 // GDO0 pin: 2 -// GDO2 pin: 3 -CC1101 cc = new Module(10, 2, 3); +// RST pin: unused +// GDO2 pin: 3 (optional) +CC1101 cc = new Module(10, 2, NC, 3); // or using RadioShield // https://github.com/jgromes/RadioShield @@ -31,8 +32,8 @@ void setup() { Serial.print(F("[CC1101] Initializing ... ")); // carrier frequency: 868.0 MHz // bit rate: 4.8 kbps - // Rx bandwidth: 325.0 kHz // frequency deviation: 48.0 kHz + // Rx bandwidth: 325.0 kHz // sync word: 0xD391 int state = cc.begin(); if (state == ERR_NONE) { diff --git a/examples/CC1101/CC1101_Transmit_Interrupt/CC1101_Transmit_Interrupt.ino b/examples/CC1101/CC1101_Transmit_Interrupt/CC1101_Transmit_Interrupt.ino index 14bf31eb..dde5d54d 100644 --- a/examples/CC1101/CC1101_Transmit_Interrupt/CC1101_Transmit_Interrupt.ino +++ b/examples/CC1101/CC1101_Transmit_Interrupt/CC1101_Transmit_Interrupt.ino @@ -16,10 +16,11 @@ #include // CC1101 has the following connections: -// NSS pin: 10 +// CS pin: 10 // GDO0 pin: 2 -// GDO2 pin: 3 -CC1101 cc = new Module(10, 2, 3); +// RST pin: unused +// GDO2 pin: 3 (optional) +CC1101 cc = new Module(10, 2, NC, 3); // or using RadioShield // https://github.com/jgromes/RadioShield @@ -35,8 +36,8 @@ void setup() { Serial.print(F("[CC1101] Initializing ... ")); // carrier frequency: 868.0 MHz // bit rate: 4.8 kbps - // Rx bandwidth: 325.0 kHz // frequency deviation: 48.0 kHz + // Rx bandwidth: 325.0 kHz // sync word: 0xD391 int state = cc.begin(); if (state == ERR_NONE) { @@ -62,7 +63,7 @@ void setup() { /* byte byteArr[] = {0x01, 0x23, 0x45, 0x56, 0x78, 0xAB, 0xCD, 0xEF}; - state = cc.transmit(byteArr, 8); + state = cc.startTransmit(byteArr, 8); */ } @@ -124,7 +125,7 @@ void loop() { /* byte byteArr[] = {0x01, 0x23, 0x45, 0x56, 0x78, 0xAB, 0xCD, 0xEF}; - int state = cc.transmit(byteArr, 8); + int state = cc.startTransmit(byteArr, 8); */ // we're ready to send more packets, diff --git a/examples/Morse/Morse_Transmit/Morse_Transmit.ino b/examples/Morse/Morse_Transmit/Morse_Transmit.ino index 611ffd93..6d71d1a4 100644 --- a/examples/Morse/Morse_Transmit/Morse_Transmit.ino +++ b/examples/Morse/Morse_Transmit/Morse_Transmit.ino @@ -19,8 +19,9 @@ // SX1278 has the following connections: // NSS pin: 10 // DIO0 pin: 2 +// RESET pin: 9 // DIO1 pin: 3 -SX1278 fsk = new Module(10, 2, 3); +SX1278 fsk = new Module(10, 2, 9, 3); // or using RadioShield // https://github.com/jgromes/RadioShield @@ -104,7 +105,7 @@ void loop() { // floating point number // NOTE: When using println(), the transmission will be - // terminated with cross signal (.-.-.). + // terminated with end-of-work signal (...-.-). float f = -3.1415; morse.println(f, 3); diff --git a/examples/RF69/RF69_Receive/RF69_Receive.ino b/examples/RF69/RF69_Receive/RF69_Receive.ino index fb8b69bd..597ee63e 100644 --- a/examples/RF69/RF69_Receive/RF69_Receive.ino +++ b/examples/RF69/RF69_Receive/RF69_Receive.ino @@ -17,9 +17,9 @@ #include // RF69 has the following connections: -// NSS pin: 10 +// CS pin: 10 // DIO0 pin: 2 -// DIO1 pin: 3 +// RESET pin: 3 RF69 rf = new Module(10, 2, 3); // or using RadioShield @@ -33,8 +33,8 @@ void setup() { Serial.print(F("[RF69] Initializing ... ")); // carrier frequency: 434.0 MHz // bit rate: 48.0 kbps - // Rx bandwidth: 125.0 kHz // frequency deviation: 50.0 kHz + // Rx bandwidth: 125.0 kHz // output power: 13 dBm // sync word: 0x2D01 int state = rf.begin(); diff --git a/examples/RF69/RF69_Receive_AES/RF69_Receive_AES.ino b/examples/RF69/RF69_Receive_AES/RF69_Receive_AES.ino index cc5e074a..7c947cf0 100644 --- a/examples/RF69/RF69_Receive_AES/RF69_Receive_AES.ino +++ b/examples/RF69/RF69_Receive_AES/RF69_Receive_AES.ino @@ -13,9 +13,9 @@ #include // RF69 has the following connections: -// NSS pin: 10 +// CS pin: 10 // DIO0 pin: 2 -// DIO1 pin: 3 +// RESET pin: 3 RF69 rf = new Module(10, 2, 3); // or using RadioShield @@ -29,8 +29,8 @@ void setup() { Serial.print(F("[RF69] Initializing ... ")); // carrier frequency: 434.0 MHz // bit rate: 48.0 kbps - // Rx bandwidth: 125.0 kHz // frequency deviation: 50.0 kHz + // Rx bandwidth: 125.0 kHz // output power: 13 dBm // sync word: 0x2D01 int state = rf.begin(); diff --git a/examples/RF69/RF69_Receive_Address/RF69_Receive_Address.ino b/examples/RF69/RF69_Receive_Address/RF69_Receive_Address.ino index c0444357..86e1ebbc 100644 --- a/examples/RF69/RF69_Receive_Address/RF69_Receive_Address.ino +++ b/examples/RF69/RF69_Receive_Address/RF69_Receive_Address.ino @@ -15,9 +15,9 @@ #include // RF69 has the following connections: -// NSS pin: 10 +// CS pin: 10 // DIO0 pin: 2 -// DIO1 pin: 3 +// RESET pin: 3 RF69 rf = new Module(10, 2, 3); // or using RadioShield @@ -31,8 +31,8 @@ void setup() { Serial.print(F("[RF69] Initializing ... ")); // carrier frequency: 434.0 MHz // bit rate: 48.0 kbps - // Rx bandwidth: 125.0 kHz // frequency deviation: 50.0 kHz + // Rx bandwidth: 125.0 kHz // output power: 13 dBm // sync word: 0x2D01 int state = rf.begin(); diff --git a/examples/RF69/RF69_Receive_Interrupt/RF69_Receive_Interrupt.ino b/examples/RF69/RF69_Receive_Interrupt/RF69_Receive_Interrupt.ino index a38ec020..2e8929ac 100644 --- a/examples/RF69/RF69_Receive_Interrupt/RF69_Receive_Interrupt.ino +++ b/examples/RF69/RF69_Receive_Interrupt/RF69_Receive_Interrupt.ino @@ -13,9 +13,9 @@ #include // RF69 has the following connections: -// NSS pin: 10 +// CS pin: 10 // DIO0 pin: 2 -// DIO1 pin: 3 +// RESET pin: 3 RF69 rf = new Module(10, 2, 3); // or using RadioShield @@ -29,8 +29,8 @@ void setup() { Serial.print(F("[RF69] Initializing ... ")); // carrier frequency: 434.0 MHz // bit rate: 48.0 kbps - // Rx bandwidth: 125.0 kHz // frequency deviation: 50.0 kHz + // Rx bandwidth: 125.0 kHz // output power: 13 dBm // sync word: 0x2D01 int state = rf.begin(); @@ -104,7 +104,7 @@ void loop() { // you can also read received data as byte array /* byte byteArr[8]; - int state = lora.receive(byteArr, 8); + int state = lora.readData(byteArr, 8); */ if (state == ERR_NONE) { diff --git a/examples/RF69/RF69_Settings/RF69_Settings.ino b/examples/RF69/RF69_Settings/RF69_Settings.ino index 1f223745..2fee90ba 100644 --- a/examples/RF69/RF69_Settings/RF69_Settings.ino +++ b/examples/RF69/RF69_Settings/RF69_Settings.ino @@ -19,14 +19,20 @@ #include // RF69 has the following connections: -// NSS pin: 10 +// CS pin: 10 // DIO0 pin: 2 -// DIO1 pin: 3 +// RESET pin: 3 RF69 rf1 = new Module(10, 2, 3); +// second CC1101 has different connections: +// CS pin: 9 +// DIO0 pin: 4 +// RESET pin: 5 +RF69 rf2 = new Module(9, 4, 5); + // or using RadioShield // https://github.com/jgromes/RadioShield -RF69 rf2 = RadioShield.ModuleB; +//RF69 rf3 = RadioShield.ModuleB; void setup() { Serial.begin(9600); @@ -35,8 +41,8 @@ void setup() { Serial.print(F("[RF69] Initializing ... ")); // carrier frequency: 434.0 MHz // bit rate: 48.0 kbps - // Rx bandwidth: 125.0 kHz // frequency deviation: 50.0 kHz + // Rx bandwidth: 125.0 kHz // output power: 13 dBm // sync word: 0x2D01 int state = rf1.begin(); @@ -52,11 +58,11 @@ void setup() { Serial.print(F("[RF69] Initializing ... ")); // carrier frequency: 868.0 MHz // bit rate: 300.0 kbps - // Rx bandwidth: 250.0 kHz // frequency deviation: 60.0 kHz + // Rx bandwidth: 250.0 kHz // output power: 17 dBm // sync word: 0x2D01 - state = rf2.begin(868.0, 300.0, 250.0, 60.0, 17); + state = rf2.begin(868.0, 300.0, 60.0, 250.0, 17); if (state == ERR_NONE) { Serial.println(F("success!")); } else { diff --git a/examples/RF69/RF69_Transmit/RF69_Transmit.ino b/examples/RF69/RF69_Transmit/RF69_Transmit.ino index eb818b22..05879c0b 100644 --- a/examples/RF69/RF69_Transmit/RF69_Transmit.ino +++ b/examples/RF69/RF69_Transmit/RF69_Transmit.ino @@ -15,9 +15,9 @@ #include // RF69 has the following connections: -// NSS pin: 10 +// CS pin: 10 // DIO0 pin: 2 -// DIO1 pin: 3 +// RESET pin: 3 RF69 rf = new Module(10, 2, 3); // or using RadioShield @@ -31,8 +31,8 @@ void setup() { Serial.print(F("[RF69] Initializing ... ")); // carrier frequency: 434.0 MHz // bit rate: 48.0 kbps - // Rx bandwidth: 125.0 kHz // frequency deviation: 50.0 kHz + // Rx bandwidth: 125.0 kHz // output power: 13 dBm // sync word: 0x2D01 int state = rf.begin(); diff --git a/examples/RF69/RF69_Transmit_AES/RF69_Transmit_AES.ino b/examples/RF69/RF69_Transmit_AES/RF69_Transmit_AES.ino index b0b127ff..a9ec4ff5 100644 --- a/examples/RF69/RF69_Transmit_AES/RF69_Transmit_AES.ino +++ b/examples/RF69/RF69_Transmit_AES/RF69_Transmit_AES.ino @@ -13,9 +13,9 @@ #include // RF69 has the following connections: -// NSS pin: 10 +// CS pin: 10 // DIO0 pin: 2 -// DIO1 pin: 3 +// RESET pin: 3 RF69 rf = new Module(10, 2, 3); // or using RadioShield @@ -29,8 +29,8 @@ void setup() { Serial.print(F("[RF69] Initializing ... ")); // carrier frequency: 434.0 MHz // bit rate: 48.0 kbps - // Rx bandwidth: 125.0 kHz // frequency deviation: 50.0 kHz + // Rx bandwidth: 125.0 kHz // output power: 13 dBm // sync word: 0x2D01 int state = rf.begin(); diff --git a/examples/RF69/RF69_Transmit_Address/RF69_Transmit_Address.ino b/examples/RF69/RF69_Transmit_Address/RF69_Transmit_Address.ino index 03b61a70..b9b36870 100644 --- a/examples/RF69/RF69_Transmit_Address/RF69_Transmit_Address.ino +++ b/examples/RF69/RF69_Transmit_Address/RF69_Transmit_Address.ino @@ -15,9 +15,9 @@ #include // RF69 has the following connections: -// NSS pin: 10 +// CS pin: 10 // DIO0 pin: 2 -// DIO1 pin: 3 +// RESET pin: 3 RF69 rf = new Module(10, 2, 3); // or using RadioShield @@ -31,8 +31,8 @@ void setup() { Serial.print(F("[RF69] Initializing ... ")); // carrier frequency: 434.0 MHz // bit rate: 48.0 kbps - // Rx bandwidth: 125.0 kHz // frequency deviation: 50.0 kHz + // Rx bandwidth: 125.0 kHz // output power: 13 dBm // sync word: 0x2D01 int state = rf.begin(); diff --git a/examples/RF69/RF69_Transmit_Interrupt/RF69_Transmit_Interrupt.ino b/examples/RF69/RF69_Transmit_Interrupt/RF69_Transmit_Interrupt.ino index 684a9a9d..0387849a 100644 --- a/examples/RF69/RF69_Transmit_Interrupt/RF69_Transmit_Interrupt.ino +++ b/examples/RF69/RF69_Transmit_Interrupt/RF69_Transmit_Interrupt.ino @@ -16,9 +16,9 @@ #include // RF69 has the following connections: -// NSS pin: 10 +// CS pin: 10 // DIO0 pin: 2 -// DIO1 pin: 3 +// RESET pin: 3 RF69 rf = new Module(10, 2, 3); // or using RadioShield @@ -35,8 +35,8 @@ void setup() { Serial.print(F("[RF69] Initializing ... ")); // carrier frequency: 434.0 MHz // bit rate: 48.0 kbps - // Rx bandwidth: 125.0 kHz // frequency deviation: 50.0 kHz + // Rx bandwidth: 125.0 kHz // output power: 13 dBm // sync word: 0x2D01 int state = rf.begin(); @@ -63,7 +63,7 @@ void setup() { /* byte byteArr[] = {0x01, 0x23, 0x45, 0x56, 0x78, 0xAB, 0xCD, 0xEF}; - state = rf.transmit(byteArr, 8); + state = rf.startTransmit(byteArr, 8); */ } @@ -121,11 +121,11 @@ void loop() { // 256 characters long transmissionState = rf.startTransmit("Hello World!"); - // you can also transmit byte array up to 256 bytes long + // you can also transmit byte array up to 64 bytes long /* byte byteArr[] = {0x01, 0x23, 0x45, 0x56, 0x78, 0xAB, 0xCD, 0xEF}; - int state = rf.transmit(byteArr, 8); + int state = rf.startTransmit(byteArr, 8); */ // we're ready to send more packets, diff --git a/examples/RTTY/RTTY_Transmit/RTTY_Transmit.ino b/examples/RTTY/RTTY_Transmit/RTTY_Transmit.ino index 540f656a..da77ea5b 100644 --- a/examples/RTTY/RTTY_Transmit/RTTY_Transmit.ino +++ b/examples/RTTY/RTTY_Transmit/RTTY_Transmit.ino @@ -22,8 +22,9 @@ // SX1278 has the following connections: // NSS pin: 10 // DIO0 pin: 2 +// RESET pin: 9 // DIO1 pin: 3 -SX1278 fsk = new Module(10, 2, 3); +SX1278 fsk = new Module(10, 2, 9, 3); // or using RadioShield // https://github.com/jgromes/RadioShield diff --git a/examples/SX1231/SX1231_Receive/SX1231_Receive.ino b/examples/SX1231/SX1231_Receive/SX1231_Receive.ino index 8de99947..727de1e5 100644 --- a/examples/SX1231/SX1231_Receive/SX1231_Receive.ino +++ b/examples/SX1231/SX1231_Receive/SX1231_Receive.ino @@ -15,9 +15,9 @@ #include // SX1231 has the following connections: -// NSS pin: 10 +// CS pin: 10 // DIO0 pin: 2 -// DIO1 pin: 3 +// RESET pin: 3 SX1231 rf = new Module(10, 2, 3); // or using RadioShield @@ -35,7 +35,7 @@ void setup() { // frequency deviation: 50.0 kHz // output power: 13 dBm // sync word: 0x2D01 - byte state = rf.begin(); + int state = rf.begin(); if (state == ERR_NONE) { Serial.println(F("success!")); } else { diff --git a/examples/SX1231/SX1231_Transmit/SX1231_Transmit.ino b/examples/SX1231/SX1231_Transmit/SX1231_Transmit.ino index b3376f2d..c781b02a 100644 --- a/examples/SX1231/SX1231_Transmit/SX1231_Transmit.ino +++ b/examples/SX1231/SX1231_Transmit/SX1231_Transmit.ino @@ -15,9 +15,9 @@ #include // SX1231 has the following connections: -// NSS pin: 10 +// CS pin: 10 // DIO0 pin: 2 -// DIO1 pin: 3 +// RESET pin: 3 SX1231 rf = new Module(10, 2, 3); // or using RadioShield diff --git a/examples/SX126x/SX126x_Channel_Activity_Detection/SX126x_Channel_Activity_Detection.ino b/examples/SX126x/SX126x_Channel_Activity_Detection/SX126x_Channel_Activity_Detection.ino index ae15238a..99209b99 100644 --- a/examples/SX126x/SX126x_Channel_Activity_Detection/SX126x_Channel_Activity_Detection.ino +++ b/examples/SX126x/SX126x_Channel_Activity_Detection/SX126x_Channel_Activity_Detection.ino @@ -1,9 +1,9 @@ /* RadioLib SX126x Channel Activity Detection Example - This example uses SX1262 to scan the current LoRa + This example uses SX1262 to scan the current LoRa channel and detect ongoing LoRa transmissions. - Unlike SX127x CAD, SX126x can detect any part + Unlike SX127x CAD, SX126x can detect any part of LoRa transmission, not just the preamble. Other modules from SX126x family can also be used. @@ -18,7 +18,7 @@ // SX1262 has the following connections: // NSS pin: 10 // DIO1 pin: 2 -// DIO2 pin: 3 +// NRST pin: 3 // BUSY pin: 9 SX1262 lora = new Module(10, 2, 3, 9); @@ -35,10 +35,12 @@ void setup() { // bandwidth: 125.0 kHz // spreading factor: 9 // coding rate: 7 - // sync word: 0x1424 (private network) + // sync word: 0x12 (private network) // output power: 14 dBm // current limit: 60 mA // preamble length: 8 symbols + // TCXO voltage: 1.6 V (set to 0 to not use TCXO) + // regulator: DC-DC (set to true to use LDO) // CRC: enabled int state = lora.begin(); if (state == ERR_NONE) { diff --git a/examples/SX126x/SX126x_FSK_Modem/SX126x_FSK_Modem.ino b/examples/SX126x/SX126x_FSK_Modem/SX126x_FSK_Modem.ino index 18050269..e3e04627 100644 --- a/examples/SX126x/SX126x_FSK_Modem/SX126x_FSK_Modem.ino +++ b/examples/SX126x/SX126x_FSK_Modem/SX126x_FSK_Modem.ino @@ -19,7 +19,7 @@ // SX1262 has the following connections: // NSS pin: 10 // DIO1 pin: 2 -// DIO2 pin: 3 +// NRST pin: 3 // BUSY pin: 9 SX1262 fsk = new Module(10, 2, 3, 9); @@ -40,6 +40,8 @@ void setup() { // current limit: 60.0 mA // preamble length: 16 bits // data shaping: Gaussian, BT = 0.5 + // TCXO voltage: 1.6 V (set to 0 to not use TCXO) + // regulator: DC-DC (set to true to use LDO) // sync word: 0x2D 0x01 // CRC: enabled, CRC16 (CCIT) int state = fsk.beginFSK(); @@ -73,7 +75,7 @@ void setup() { Serial.println(state); while (true); } - + // FSK modem on SX126x can handle the sync word setting in bits, not just // whole bytes. The value used is left-justified. // This makes same result as fsk.setSyncWord(syncWord, 8): diff --git a/examples/SX126x/SX126x_Receive/SX126x_Receive.ino b/examples/SX126x/SX126x_Receive/SX126x_Receive.ino index 2f6f8319..c95592f0 100644 --- a/examples/SX126x/SX126x_Receive/SX126x_Receive.ino +++ b/examples/SX126x/SX126x_Receive/SX126x_Receive.ino @@ -23,7 +23,7 @@ // SX1262 has the following connections: // NSS pin: 10 // DIO1 pin: 2 -// DIO2 pin: 3 +// NRST pin: 3 // BUSY pin: 9 SX1262 lora = new Module(10, 2, 3, 9); @@ -40,10 +40,12 @@ void setup() { // bandwidth: 125.0 kHz // spreading factor: 9 // coding rate: 7 - // sync word: 0x1424 (private network) + // sync word: 0x12 (private network) // output power: 14 dBm // current limit: 60 mA // preamble length: 8 symbols + // TCXO voltage: 1.6 V (set to 0 to not use TCXO) + // regulator: DC-DC (set to true to use LDO) // CRC: enabled int state = lora.begin(); if (state == ERR_NONE) { @@ -53,28 +55,6 @@ void setup() { Serial.println(state); while (true); } - - // NOTE: Some SX126x modules use TCXO - // (Temprature-Compensated Crystal Oscillator). - // To be able to use these modules, TCXO - // control must be enabled by calling - // setTCXO() and specifying the reference - // voltage. - - /* - Serial.print(F("[SX1262] Setting TCXO reference ... ")); - // enable TCXO - // reference voltage: 1.6 V - // timeout: 5000 us - state = lora.setTCXO(1.6); - if (state == ERR_NONE) { - Serial.println(F("success!")); - } else { - Serial.print(F("failed, code ")); - Serial.println(state); - while (true); - } - */ } void loop() { diff --git a/examples/SX126x/SX126x_Receive_Interrupt/SX126x_Receive_Interrupt.ino b/examples/SX126x/SX126x_Receive_Interrupt/SX126x_Receive_Interrupt.ino index f61f1602..59d49f53 100644 --- a/examples/SX126x/SX126x_Receive_Interrupt/SX126x_Receive_Interrupt.ino +++ b/examples/SX126x/SX126x_Receive_Interrupt/SX126x_Receive_Interrupt.ino @@ -24,7 +24,7 @@ // SX1262 has the following connections: // NSS pin: 10 // DIO1 pin: 2 -// DIO2 pin: 3 +// NRST pin: 3 // BUSY pin: 9 SX1262 lora = new Module(10, 2, 3, 9); @@ -41,10 +41,12 @@ void setup() { // bandwidth: 125.0 kHz // spreading factor: 9 // coding rate: 7 - // sync word: 0x1424 (private network) + // sync word: 0x12 (private network) // output power: 14 dBm // current limit: 60 mA // preamble length: 8 symbols + // TCXO voltage: 1.6 V (set to 0 to not use TCXO) + // regulator: DC-DC (set to true to use LDO) // CRC: enabled int state = lora.begin(); if (state == ERR_NONE) { @@ -118,7 +120,7 @@ void loop() { // you can also read received data as byte array /* byte byteArr[8]; - int state = lora.receive(byteArr, 8); + int state = lora.readData(byteArr, 8); */ if (state == ERR_NONE) { diff --git a/examples/SX126x/SX126x_Settings/SX126x_Settings.ino b/examples/SX126x/SX126x_Settings/SX126x_Settings.ino index beb4b455..3c8a3e5e 100644 --- a/examples/SX126x/SX126x_Settings/SX126x_Settings.ino +++ b/examples/SX126x/SX126x_Settings/SX126x_Settings.ino @@ -27,13 +27,20 @@ // SX1262 has the following connections: // NSS pin: 10 // DIO1 pin: 2 -// DIO2 pin: 3 +// NRST pin: 3 // BUSY pin: 9 SX1262 loraSX1262 = new Module(10, 2, 3, 9); +// SX12628 has different connections: +// NSS pin: 8 +// DIO1 pin: 4 +// NRST pin: 5 +// BUSY pin: 6 +SX1268 loraSX1268 = new Module(8, 4, 5, 6); + // or using RadioShield // https://github.com/jgromes/RadioShield -SX1268 loraSX1268 = RadioShield.ModuleB; +//SX1261 loraSX1261 = RadioShield.ModuleB; void setup() { Serial.begin(9600); @@ -44,10 +51,12 @@ void setup() { // bandwidth: 125.0 kHz // spreading factor: 9 // coding rate: 7 - // sync word: 0x1424 (private network) + // sync word: 0x12 (private network) // output power: 14 dBm // current limit: 60 mA // preamble length: 8 symbols + // TCXO voltage: 1.6 V (set to 0 to not use TCXO) + // regulator: DC-DC (set to true to use LDO) // CRC: enabled int state = loraSX1262.begin(); if (state == ERR_NONE) { @@ -67,7 +76,7 @@ void setup() { // bandwidth: 500.0 kHz // spreading factor: 6 // coding rate: 5 - // sync word: 0x3444 (public network) + // sync word: 0x34 (public network) // output power: 2 dBm // current limit: 50 mA // preamble length: 20 symbols diff --git a/examples/SX126x/SX126x_Transmit/SX126x_Transmit.ino b/examples/SX126x/SX126x_Transmit/SX126x_Transmit.ino index 65d24eec..574fdff3 100644 --- a/examples/SX126x/SX126x_Transmit/SX126x_Transmit.ino +++ b/examples/SX126x/SX126x_Transmit/SX126x_Transmit.ino @@ -19,7 +19,7 @@ // SX1262 has the following connections: // NSS pin: 10 // DIO1 pin: 2 -// DIO2 pin: 3 +// NRST pin: 3 // BUSY pin: 9 SX1262 lora = new Module(10, 2, 3, 9); @@ -36,10 +36,12 @@ void setup() { // bandwidth: 125.0 kHz // spreading factor: 9 // coding rate: 7 - // sync word: 0x1424 (private network) + // sync word: 0x12 (private network) // output power: 14 dBm // current limit: 60 mA // preamble length: 8 symbols + // TCXO voltage: 1.6 V (set to 0 to not use TCXO) + // regulator: DC-DC (set to true to use LDO) // CRC: enabled int state = lora.begin(); if (state == ERR_NONE) { @@ -49,29 +51,6 @@ void setup() { Serial.println(state); while (true); } - - // NOTE: Some SX126x modules use TCXO - // (Temprature-Compensated Crystal Oscillator). - // To be able to use these modules, TCXO - // control must be enabled by calling - // setTCXO() and specifying the reference - // voltage. - - /* - Serial.print(F("[SX1262] Setting TCXO reference ... ")); - // enable TCXO - // reference voltage: 1.6 V - // timeout: 5000 us - state = lora.setTCXO(1.6); - if (state == ERR_NONE) { - Serial.println(F("success!")); - } else { - Serial.print(F("failed, code ")); - Serial.println(state); - while (true); - } - */ - } void loop() { diff --git a/examples/SX126x/SX126x_Transmit_Interrupt/SX126x_Transmit_Interrupt.ino b/examples/SX126x/SX126x_Transmit_Interrupt/SX126x_Transmit_Interrupt.ino index f295c2e5..157f00a1 100644 --- a/examples/SX126x/SX126x_Transmit_Interrupt/SX126x_Transmit_Interrupt.ino +++ b/examples/SX126x/SX126x_Transmit_Interrupt/SX126x_Transmit_Interrupt.ino @@ -20,7 +20,7 @@ // SX1262 has the following connections: // NSS pin: 10 // DIO1 pin: 2 -// DIO2 pin: 3 +// NRST pin: 3 // BUSY pin: 9 SX1262 lora = new Module(10, 2, 3, 9); @@ -36,10 +36,12 @@ void setup() { // bandwidth: 125.0 kHz // spreading factor: 9 // coding rate: 7 - // sync word: 0x1424 (private network) + // sync word: 0x12 (private network) // output power: 14 dBm // current limit: 60 mA // preamble length: 8 symbols + // TCXO voltage: 1.6 V (set to 0 to not use TCXO) + // regulator: DC-DC (set to true to use LDO) // CRC: enabled int state = lora.begin(); if (state == ERR_NONE) { diff --git a/examples/SX127x/SX127x_Channel_Activity_Detection/SX127x_Channel_Activity_Detection.ino b/examples/SX127x/SX127x_Channel_Activity_Detection/SX127x_Channel_Activity_Detection.ino index cf54ed75..163f8273 100644 --- a/examples/SX127x/SX127x_Channel_Activity_Detection/SX127x_Channel_Activity_Detection.ino +++ b/examples/SX127x/SX127x_Channel_Activity_Detection/SX127x_Channel_Activity_Detection.ino @@ -19,8 +19,9 @@ // SX1278 has the following connections: // NSS pin: 10 // DIO0 pin: 2 +// RESET pin: 9 // DIO1 pin: 3 -SX1278 lora = new Module(10, 2, 3); +SX1278 lora = new Module(10, 2, 9, 3); // or using RadioShield // https://github.com/jgromes/RadioShield diff --git a/examples/SX127x/SX127x_FSK_Modem/SX127x_FSK_Modem.ino b/examples/SX127x/SX127x_FSK_Modem/SX127x_FSK_Modem.ino index 1c8cd1da..f9c89154 100644 --- a/examples/SX127x/SX127x_FSK_Modem/SX127x_FSK_Modem.ino +++ b/examples/SX127x/SX127x_FSK_Modem/SX127x_FSK_Modem.ino @@ -19,8 +19,9 @@ // SX1278 has the following connections: // NSS pin: 10 // DIO0 pin: 2 +// RESET pin: 9 // DIO1 pin: 3 -SX1278 fsk = new Module(10, 2, 3); +SX1278 fsk = new Module(10, 2, 9, 3); // or using RadioShield // https://github.com/jgromes/RadioShield diff --git a/examples/SX127x/SX127x_Receive/SX127x_Receive.ino b/examples/SX127x/SX127x_Receive/SX127x_Receive.ino index c3c3392e..5627bdc3 100644 --- a/examples/SX127x/SX127x_Receive/SX127x_Receive.ino +++ b/examples/SX127x/SX127x_Receive/SX127x_Receive.ino @@ -23,8 +23,9 @@ // SX1278 has the following connections: // NSS pin: 10 // DIO0 pin: 2 +// RESET pin: 9 // DIO1 pin: 3 -SX1278 lora = new Module(10, 2, 3); +SX1278 lora = new Module(10, 2, 9, 3); // or using RadioShield // https://github.com/jgromes/RadioShield diff --git a/examples/SX127x/SX127x_Receive_Interrupt/SX127x_Receive_Interrupt.ino b/examples/SX127x/SX127x_Receive_Interrupt/SX127x_Receive_Interrupt.ino index d204348b..fbc20651 100644 --- a/examples/SX127x/SX127x_Receive_Interrupt/SX127x_Receive_Interrupt.ino +++ b/examples/SX127x/SX127x_Receive_Interrupt/SX127x_Receive_Interrupt.ino @@ -24,8 +24,9 @@ // SX1278 has the following connections: // NSS pin: 10 // DIO0 pin: 2 +// RESET pin: 9 // DIO1 pin: 3 -SX1278 lora = new Module(10, 2, 3); +SX1278 lora = new Module(10, 2, 9, 3); // or using RadioShield // https://github.com/jgromes/RadioShield diff --git a/examples/SX127x/SX127x_Settings/SX127x_Settings.ino b/examples/SX127x/SX127x_Settings/SX127x_Settings.ino index e3a834a6..681c76d2 100644 --- a/examples/SX127x/SX127x_Settings/SX127x_Settings.ino +++ b/examples/SX127x/SX127x_Settings/SX127x_Settings.ino @@ -22,14 +22,21 @@ // SX1278 has the following connections: // NSS pin: 10 -// DIO1 pin: 2 -// DIO2 pin: 3 -// BUSY pin: 9 -SX1278 loraSX1278 = new Module(10, 2, 3, 9); +// DIO0 pin: 2 +// RESET pin: 9 +// DIO1 pin: 3 +SX1278 loraSX1278 = new Module(10, 2, 9, 3); + +// SX1272 has different connections: +// NSS pin: 9 +// DIO0 pin: 4 +// RESET pin: 5 +// DIO1 pin: 6 +SX1272 loraSX1272 = new Module(9, 4, 5, 6); // or using RadioShield // https://github.com/jgromes/RadioShield -SX1272 loraSX1272 = RadioShield.ModuleB; +//SX1276 loraSX1276 = RadioShield.ModuleB; void setup() { Serial.begin(9600); diff --git a/examples/SX127x/SX127x_Transmit/SX127x_Transmit.ino b/examples/SX127x/SX127x_Transmit/SX127x_Transmit.ino index 2573a41d..d14e76f7 100644 --- a/examples/SX127x/SX127x_Transmit/SX127x_Transmit.ino +++ b/examples/SX127x/SX127x_Transmit/SX127x_Transmit.ino @@ -19,8 +19,9 @@ // SX1278 has the following connections: // NSS pin: 10 // DIO0 pin: 2 +// RESET pin: 9 // DIO1 pin: 3 -SX1278 lora = new Module(10, 2, 3); +SX1278 lora = new Module(10, 2, 9, 3); // or using RadioShield // https://github.com/jgromes/RadioShield diff --git a/examples/SX127x/SX127x_Transmit_Interrupt/SX127x_Transmit_Interrupt.ino b/examples/SX127x/SX127x_Transmit_Interrupt/SX127x_Transmit_Interrupt.ino index 08681c9e..8368a7d4 100644 --- a/examples/SX127x/SX127x_Transmit_Interrupt/SX127x_Transmit_Interrupt.ino +++ b/examples/SX127x/SX127x_Transmit_Interrupt/SX127x_Transmit_Interrupt.ino @@ -20,8 +20,9 @@ // SX1278 has the following connections: // NSS pin: 10 // DIO0 pin: 2 +// RESET pin: 9 // DIO1 pin: 3 -SX1278 lora = new Module(10, 2, 3); +SX1278 lora = new Module(10, 2, 9, 3); // or using RadioShield // https://github.com/jgromes/RadioShield diff --git a/examples/XBee/XBee_Receive/XBee_Receive.ino b/examples/XBee/XBee_Receive/XBee_Receive.ino index 41921283..e5036026 100644 --- a/examples/XBee/XBee_Receive/XBee_Receive.ino +++ b/examples/XBee/XBee_Receive/XBee_Receive.ino @@ -18,7 +18,7 @@ // TX pin: 9 // RX pin: 8 // RESET pin: 3 -XBee bee = new Module(9, 8); +XBee bee = new Module(9, 8, 3); // or using RadioShield // https://github.com/jgromes/RadioShield diff --git a/examples/XBee/XBee_Transmit/XBee_Transmit.ino b/examples/XBee/XBee_Transmit/XBee_Transmit.ino index 1123015d..1d7d8aa9 100644 --- a/examples/XBee/XBee_Transmit/XBee_Transmit.ino +++ b/examples/XBee/XBee_Transmit/XBee_Transmit.ino @@ -18,7 +18,7 @@ // TX pin: 9 // RX pin: 8 // RESET pin: 3 -XBee bee = new Module(9, 8); +XBee bee = new Module(9, 8, 3); // or using RadioShield // https://github.com/jgromes/RadioShield diff --git a/examples/XBee/XBee_Transparent/XBee_Transparent.ino b/examples/XBee/XBee_Transparent/XBee_Transparent.ino index deb29061..f27108eb 100644 --- a/examples/XBee/XBee_Transparent/XBee_Transparent.ino +++ b/examples/XBee/XBee_Transparent/XBee_Transparent.ino @@ -20,7 +20,7 @@ // TX pin: 9 // RX pin: 8 // RESET pin: 3 -XBeeSerial bee = new Module(9, 8); +XBeeSerial bee = new Module(9, 8, 3); // or using RadioShield // https://github.com/jgromes/RadioShield diff --git a/examples/nRF24/nRF24_Receive/nRF24_Receive.ino b/examples/nRF24/nRF24_Receive/nRF24_Receive.ino index 785ce037..b5b5571e 100644 --- a/examples/nRF24/nRF24_Receive/nRF24_Receive.ino +++ b/examples/nRF24/nRF24_Receive/nRF24_Receive.ino @@ -17,9 +17,9 @@ #include // nRF24 has the following connections: -// NSS pin: 10 -// CE pin: 2 -// IRQ pin: 3 +// CS pin: 10 +// IRQ pin: 2 +// CE pin: 3 nRF24 nrf = new Module(10, 2, 3); // or using RadioShield diff --git a/examples/nRF24/nRF24_Transmit/nRF24_Transmit.ino b/examples/nRF24/nRF24_Transmit/nRF24_Transmit.ino index d6c19f05..89006b38 100644 --- a/examples/nRF24/nRF24_Transmit/nRF24_Transmit.ino +++ b/examples/nRF24/nRF24_Transmit/nRF24_Transmit.ino @@ -17,9 +17,9 @@ #include // nRF24 has the following connections: -// NSS pin: 10 -// CE pin: 2 -// IRQ pin: 3 +// CS pin: 10 +// IRQ pin: 2 +// CE pin: 3 nRF24 nrf = new Module(10, 2, 3); // or using RadioShield diff --git a/keywords.txt b/keywords.txt index 02739f5a..10f66074 100644 --- a/keywords.txt +++ b/keywords.txt @@ -39,6 +39,8 @@ HTTPClient KEYWORD1 RTTYClient KEYWORD1 MorseClient KEYWORD1 PagerClient KEYWORD1 +AX25Client KEYWORD1 +AX25Frame KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -62,6 +64,8 @@ receiveDirect KEYWORD2 packetMode KEYWORD2 setDio0Action KEYWORD2 setDio1Action KEYWORD2 +clearDio0Action KEYWORD2 +clearDio1Action KEYWORD2 startTransmit KEYWORD2 startReceive KEYWORD2 readData KEYWORD2 @@ -107,15 +111,23 @@ setAmbientTemperature KEYWORD2 # CC1101-specific getLQI KEYWORD2 setGdo0Action KEYWORD2 -setGdo1Action KEYWORD2 +setGdo2Action KEYWORD2 +clearGdo0Action KEYWORD2 +clearGdo2Action KEYWORD2 # SX126x-specific -setDio2Action KEYWORD2 setTCXO KEYWORD2 setDio2AsRfSwitch KEYWORD2 getTimeOnAir KEYWORD2 +implicitHeader KEYWORD2 +explicitHeader KEYWORD2 setSyncBits KEYWORD2 setWhitening KEYWORD2 +startReceiveDutyCycle KEYWORD2 +startReceiveDutyCycleAuto KEYWORD2 +setRegulatorLDO KEYWORD2 +setRegulatorDCDC KEYWORD2 +getCurrentLimit KEYWORD2 # ESP8266 join KEYWORD2 @@ -133,6 +145,7 @@ setTransmitPipe KEYWORD2 setReceivePipe KEYWORD2 disablePipe KEYWORD2 getStatus KEYWORD2 +setAutoAck KEYWORD2 # HTTP get KEYWORD2 @@ -165,10 +178,19 @@ getNumBytes KEYWORD2 sendSMS KEYWORD2 shutdown KEYWORD2 +# AX.25 +setRepeaters KEYWORD2 +setRecvSequence KEYWORD2 +setSendSequence KEYWORD2 +sendFrame KEYWORD2 + ####################################### # Constants (LITERAL1) ####################################### +NC LITERAL1 +RADIOLIB_VERSION LITERAL1 + ERR_NONE LITERAL1 ERR_UNKNOWN LITERAL1 @@ -246,3 +268,9 @@ ERR_INVALID_MODULATION_PARAMETERS LITERAL1 ERR_SPI_CMD_TIMEOUT LITERAL1 ERR_SPI_CMD_INVALID LITERAL1 ERR_SPI_CMD_FAILED LITERAL1 +ERR_INVALID_SLEEP_PERIOD LITERAL1 +ERR_INVALID_RX_PERIOD LITERAL1 + +ERR_INVALID_CALLSIGN LITERAL1 +ERR_INVALID_NUM_REPEATERS LITERAL1 +ERR_INVALID_REPEATER_CALLSIGN LITERAL1 diff --git a/library.properties b/library.properties index cd1158c1..81829f5b 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=RadioLib -version=1.8.0 +version=3.3.0 author=Jan Gromes maintainer=Jan Gromes sentence=Universal wireless communication library for Arduino diff --git a/src/Module.cpp b/src/Module.cpp index 2c1bc7fc..bce8abd4 100644 --- a/src/Module.cpp +++ b/src/Module.cpp @@ -1,11 +1,11 @@ #include "Module.h" -Module::Module(int rx, int tx, HardwareSerial* useSer) { - _cs = -1; +Module::Module(int16_t rx, int16_t tx, HardwareSerial* useSer, int16_t rst) { + _cs = NC; _rx = rx; _tx = tx; - _int0 = -1; - _int1 = -1; + _irq = NC; + _rst = rst; #ifdef RADIOLIB_SOFTWARE_SERIAL_UNSUPPORTED ModuleSerial = useSer; @@ -15,22 +15,32 @@ Module::Module(int rx, int tx, HardwareSerial* useSer) { #endif } -Module::Module(int cs, int int0, int int1, SPIClass& spi, SPISettings spiSettings) { +Module::Module(int16_t cs, int16_t irq, int16_t rst, SPIClass& spi, SPISettings spiSettings) { _cs = cs; - _rx = -1; - _tx = -1; - _int0 = int0; - _int1 = int1; + _rx = NC; + _tx = NC; + _irq = irq; + _rst = rst; _spi = &spi; _spiSettings = spiSettings; } -Module::Module(int cs, int int0, int int1, int rx, int tx, SPIClass& spi, SPISettings spiSettings, HardwareSerial* useSer) { +Module::Module(int16_t cs, int16_t irq, int16_t rst, int16_t gpio, SPIClass& spi, SPISettings spiSettings) { + _cs = cs; + _rx = gpio; + _tx = NC; + _irq = irq; + _rst = rst; + _spi = &spi; + _spiSettings = spiSettings; +} + +Module::Module(int16_t cs, int16_t irq, int16_t rst, int16_t rx, int16_t tx, SPIClass& spi, SPISettings spiSettings, HardwareSerial* useSer) { _cs = cs; _rx = rx; _tx = tx; - _int0 = int0; - _int1 = int1; + _irq = irq; + _rst = rst; _spi = &spi; _spiSettings = spiSettings; @@ -42,22 +52,12 @@ Module::Module(int cs, int int0, int int1, int rx, int tx, SPIClass& spi, SPISet #endif } -Module::Module(int cs, int int0, int int1, int int2, SPIClass& spi, SPISettings spiSettings) { - _cs = cs; - _rx = int2; - _tx = -1; - _int0 = int0; - _int1 = int1; - _spi = &spi; - _spiSettings = spiSettings; -} - -void Module::init(uint8_t interface, uint8_t gpio) { +void Module::init(uint8_t interface) { // select interface switch(interface) { case RADIOLIB_USE_SPI: - pinMode(_cs, OUTPUT); - digitalWrite(_cs, HIGH); + Module::pinMode(_cs, OUTPUT); + Module::digitalWrite(_cs, HIGH); _spi->begin(); break; case RADIOLIB_USE_UART: @@ -70,27 +70,17 @@ void Module::init(uint8_t interface, uint8_t gpio) { case RADIOLIB_USE_I2C: break; } - - // select GPIO - switch(gpio) { - case RADIOLIB_INT_NONE: - break; - case RADIOLIB_INT_0: - pinMode(_int0, INPUT); - break; - case RADIOLIB_INT_1: - pinMode(_int1, INPUT); - break; - case RADIOLIB_INT_BOTH: - pinMode(_int0, INPUT); - pinMode(_int1, INPUT); - break; - } } void Module::term() { - // stop SPI - _spi->end(); + // stop hardware interfaces + if(_spi != nullptr) { + _spi->end(); + } + + if(ModuleSerial != nullptr) { + ModuleSerial->end(); + } } void Module::ATemptyBuffer() { @@ -199,7 +189,7 @@ void Module::SPIreadRegisterBurst(uint8_t reg, uint8_t numBytes, uint8_t* inByte } uint8_t Module::SPIreadRegister(uint8_t reg) { - uint8_t resp; + uint8_t resp = 0; SPItransfer(SPIreadCommand, reg, NULL, &resp, 1); return(resp); } @@ -217,14 +207,20 @@ void Module::SPItransfer(uint8_t cmd, uint8_t reg, uint8_t* dataOut, uint8_t* da _spi->beginTransaction(_spiSettings); // pull CS low - digitalWrite(_cs, LOW); + Module::digitalWrite(_cs, LOW); // send SPI register address with access command _spi->transfer(reg | cmd); - RADIOLIB_VERBOSE_PRINT(reg | cmd, HEX); - RADIOLIB_VERBOSE_PRINT('\t'); - RADIOLIB_VERBOSE_PRINT(reg | cmd, BIN); - RADIOLIB_VERBOSE_PRINT('\t'); + #ifdef RADIOLIB_VERBOSE + if(cmd == SPIwriteCommand) { + RADIOLIB_VERBOSE_PRINT('W'); + } else if(cmd == SPIreadCommand) { + RADIOLIB_VERBOSE_PRINT('R'); + } + RADIOLIB_VERBOSE_PRINT('\t') + RADIOLIB_VERBOSE_PRINT(reg, HEX); + RADIOLIB_VERBOSE_PRINT('\t'); + #endif // send data or get response if(cmd == SPIwriteCommand) { @@ -232,23 +228,31 @@ void Module::SPItransfer(uint8_t cmd, uint8_t reg, uint8_t* dataOut, uint8_t* da _spi->transfer(dataOut[n]); RADIOLIB_VERBOSE_PRINT(dataOut[n], HEX); RADIOLIB_VERBOSE_PRINT('\t'); - RADIOLIB_VERBOSE_PRINT(dataOut[n], BIN); - RADIOLIB_VERBOSE_PRINT('\t'); } } else if (cmd == SPIreadCommand) { for(size_t n = 0; n < numBytes; n++) { dataIn[n] = _spi->transfer(0x00); RADIOLIB_VERBOSE_PRINT(dataIn[n], HEX); RADIOLIB_VERBOSE_PRINT('\t'); - RADIOLIB_VERBOSE_PRINT(dataIn[n], BIN); - RADIOLIB_VERBOSE_PRINT('\t'); } } RADIOLIB_VERBOSE_PRINTLN(); // release CS - digitalWrite(_cs, HIGH); + Module::digitalWrite(_cs, HIGH); // end SPI transaction _spi->endTransaction(); } + +void Module::pinMode(int16_t pin, uint8_t mode) { + if(pin != NC) { + ::pinMode(pin, mode); + } +} + +void Module::digitalWrite(int16_t pin, uint8_t value) { + if(pin != NC) { + ::digitalWrite(pin, value); + } +} diff --git a/src/Module.h b/src/Module.h index d4b55321..cb688435 100644 --- a/src/Module.h +++ b/src/Module.h @@ -25,12 +25,14 @@ class Module { \param rx Arduino pin to be used as Rx pin for SoftwareSerial communication. - \param serial HardwareSerial to be used on ESP32 and SAMD. Defaults to 1 + \param serial HardwareSerial to be used on platforms that do not support SoftwareSerial. Defaults to Serial1. + + \param rst Arduino pin to be used as hardware reset for the module. Defaults to -1 (unused). */ #ifdef RADIOLIB_SOFTWARE_SERIAL_UNSUPPORTED - Module(int tx, int rx, HardwareSerial* useSer = &Serial1); + Module(int16_t tx, int16_t rx, HardwareSerial* serial = &Serial1, int16_t rst = -1); #else - Module(int tx, int rx, HardwareSerial* useSer = nullptr); + Module(int16_t tx, int16_t rx, HardwareSerial* serial = nullptr, int16_t rst = -1); #endif /*! @@ -38,41 +40,41 @@ class Module { \param cs Arduino pin to be used as chip select. - \param int0 Arduino pin to be used as interrupt/GPIO 0. + \param irq Arduino pin to be used as interrupt/GPIO. - \param int1 Arduino pin to be used as interrupt/GPIO 1. + \param rst Arduino pin to be used as hardware reset for the module. \param spi SPI interface to be used. Defaults to Arduino hardware SPI interface, can also use software SPI implementations. \param spiSettings SPI interface settings. Defaults to 2 MHz clock, MSB first, mode 0. */ - Module(int cs, int int0, int int1, SPIClass& spi = SPI, SPISettings spiSettings = SPISettings(2000000, MSBFIRST, SPI_MODE0)); + Module(int16_t cs, int16_t irq, int16_t rst, SPIClass& spi = SPI, SPISettings spiSettings = SPISettings(2000000, MSBFIRST, SPI_MODE0)); /*! \brief Extended SPI-based module constructor. \param cs Arduino pin to be used as chip select. - \param int0 Arduino pin to be used as interrupt/GPIO 0. + \param irq Arduino pin to be used as interrupt/GPIO. - \param int1 Arduino pin to be used as interrupt/GPIO 1. + \param rst Arduino pin to be used as hardware reset for the module. - \param int2 Arduino pin to be used as interrupt/GPIO 2. + \param gpio Arduino pin to be used as additional interrupt/GPIO. \param spi SPI interface to be used. Defaults to Arduino hardware SPI interface, can also use software SPI implementations. \param spiSettings SPI interface settings. Defaults to 2 MHz clock, MSB first, mode 0. */ - Module(int cs, int int0, int int1, int int2, SPIClass& spi = SPI, SPISettings spiSettings = SPISettings(2000000, MSBFIRST, SPI_MODE0)); + Module(int16_t cs, int16_t irq, int16_t rst, int16_t gpio, SPIClass& spi = SPI, SPISettings spiSettings = SPISettings(2000000, MSBFIRST, SPI_MODE0)); /*! \brief Generic module constructor. \param cs Arduino pin to be used as chip select. - \param int0 Arduino pin to be used as interrupt/GPIO 0. + \param irq Arduino pin to be used as interrupt/GPIO. - \param int1 Arduino pin to be used as interrupt/GPIO 1. + \param rst Arduino pin to be used as hardware reset for the module. \param tx Arduino pin to be used as Tx pin for SoftwareSerial communication. @@ -85,9 +87,9 @@ class Module { \param serial HardwareSerial to be used on ESP32 and SAMD. Defaults to 1 */ #ifdef RADIOLIB_SOFTWARE_SERIAL_UNSUPPORTED - Module(int cs, int int0, int int1, int rx, int tx, SPIClass& spi = SPI, SPISettings spiSettings = SPISettings(2000000, MSBFIRST, SPI_MODE0), HardwareSerial* useSer = &Serial1); + Module(int16_t cs, int16_t irq, int16_t rst, int16_t rx, int16_t tx, SPIClass& spi = SPI, SPISettings spiSettings = SPISettings(2000000, MSBFIRST, SPI_MODE0), HardwareSerial* serial = &Serial1); #else - Module(int cs, int int0, int int1, int rx, int tx, SPIClass& spi = SPI, SPISettings spiSettings = SPISettings(2000000, MSBFIRST, SPI_MODE0), HardwareSerial* useSer = nullptr); + Module(int16_t cs, int16_t irq, int16_t rst, int16_t rx, int16_t tx, SPIClass& spi = SPI, SPISettings spiSettings = SPISettings(2000000, MSBFIRST, SPI_MODE0), HardwareSerial* serial = nullptr); #endif @@ -128,10 +130,8 @@ class Module { \brief Initialize low-level module control. \param interface Interface to be used on the module. See \ref shield_config for details. - - \param gpio GPIO/interrupt pins to be used on the module. See \ref uart_config for details. */ - void init(uint8_t interface, uint8_t gpio); + void init(uint8_t interface); /*! \brief Terminate low-level module control. @@ -266,35 +266,42 @@ class Module { \returns Pin number of SPI chip select configured in the constructor. */ - int getCs() const { return(_cs); } + int16_t getCs() const { return(_cs); } /*! - \brief Access method to get the pin number of interrupt/GPIO 0. + \brief Access method to get the pin number of interrupt/GPIO. - \returns Pin number of interrupt/GPIO 0 configured in the constructor. + \returns Pin number of interrupt/GPIO configured in the constructor. */ - int getInt0() const { return(_int0); } + int16_t getIrq() const { return(_irq); } /*! - \brief Access method to get the pin number of interrupt/GPIO 1. + \brief Access method to get the pin number of hardware reset pin. - \returns Pin number of interrupt/GPIO 1 configured in the constructor. + \returns Pin number of hardware reset pin configured in the constructor. */ - int getInt1() const { return(_int1); } + int16_t getRst() const { return(_rst); } + + /*! + \brief Access method to get the pin number of second interrupt/GPIO. + + \returns Pin number of second interrupt/GPIO configured in the constructor. + */ + int16_t getGpio() const { return(_rx); } /*! \brief Access method to get the pin number of UART Rx. \returns Pin number of UART Rx configured in the constructor. */ - int getRx() const { return(_rx); } + int16_t getRx() const { return(_rx); } /*! \brief Access method to get the pin number of UART Rx. \returns Pin number of UART Rx configured in the constructor. */ - int getTx() const { return(_tx); } + int16_t getTx() const { return(_tx); } /*! \brief Access method to get the SPI interface. @@ -310,14 +317,32 @@ class Module { */ SPISettings getSpiSettings() const { return(_spiSettings); } + /*! + \brief Arduino core pinMode override that checks -1 as alias for unused pin. + + \param pin Pin to change the mode of. + + \param mode Which mode to set. + */ + static void pinMode(int16_t pin, uint8_t mode); + + /*! + \brief Arduino core digitalWrite override that checks -1 as alias for unused pin. + + \param pin Pin to write to. + + \param value Whether to set the pin high or low. + */ + static void digitalWrite(int16_t pin, uint8_t value); + #ifndef RADIOLIB_GODMODE private: #endif - int _cs; - int _tx; - int _rx; - int _int0; - int _int1; + int16_t _cs; + int16_t _tx; + int16_t _rx; + int16_t _irq; + int16_t _rst; SPIClass* _spi; SPISettings _spiSettings; diff --git a/src/RadioLib.h b/src/RadioLib.h index db927d6a..1775fa0e 100644 --- a/src/RadioLib.h +++ b/src/RadioLib.h @@ -18,6 +18,7 @@ - PhysicalLayer protocols - RTTY (RTTYClient) - Morse Code (MorseClient) + - AX.25 (AX25Client) - TransportLayer protocols - HTTP (HTTPClient) - MQTT (MQTTClient) @@ -67,6 +68,7 @@ // physical layer protocols #include "protocols/PhysicalLayer/PhysicalLayer.h" +#include "protocols/AX25/AX25.h" #include "protocols/Morse/Morse.h" #include "protocols/RTTY/RTTY.h" @@ -77,6 +79,9 @@ #include "protocols/MQTT/MQTT.h" #endif +// only create Radio class when using RadioShield +#ifdef RADIOLIB_RADIOSHIELD + // RadioShield pin definitions #define RADIOSHIELD_CS_A 10 #define RADIOSHIELD_RX_A 9 @@ -115,5 +120,6 @@ class Radio { }; Radio RadioShield; +#endif #endif diff --git a/src/TypeDef.h b/src/TypeDef.h index 1c782564..fb5455ae 100644 --- a/src/TypeDef.h +++ b/src/TypeDef.h @@ -1,11 +1,20 @@ #ifndef _RADIOLIB_TYPES_H #define _RADIOLIB_TYPES_H + #if ARDUINO >= 100 #include "Arduino.h" #else #error "Unsupported Arduino version (< 1.0.0)" #endif +// version definitions +#define RADIOLIB_VERSION_MAJOR (0x03) +#define RADIOLIB_VERSION_MINOR (0x03) +#define RADIOLIB_VERSION_PATCH (0x00) +#define RADIOLIB_VERSION_EXTRA (0x00) + +#define RADIOLIB_VERSION ((RADIOLIB_VERSION_MAJOR << 24) | (RADIOLIB_VERSION_MAJOR << 16) | (RADIOLIB_VERSION_MAJOR << 8) | (RADIOLIB_VERSION_EXTRA)) + /* * Uncomment to enable static-only memory management: no dynamic allocation will be performed. * Warning: Large static arrays will be created in some methods. It is not advised to send large packets in this mode. @@ -53,13 +62,30 @@ */ //#define RADIOLIB_GODMODE +/* + * Uncomment to enable pre-defined modules when using RadioShield. + */ +//#define RADIOLIB_RADIOSHIELD + /* * The following platforms do not support SoftwareSerial library. */ -#if defined(ESP32) || defined(SAMD_SERIES) || defined(ARDUINO_ARCH_STM32) || defined(__SAM3X8E__) +#if defined(ESP32) || defined(SAMD_SERIES) || defined(ARDUINO_ARCH_STM32) || defined(__SAM3X8E__) || defined(AM_PART_APOLLO3) #define RADIOLIB_SOFTWARE_SERIAL_UNSUPPORTED #endif +/*! + \brief Alias for unused pin, if not supplied by the Arduino core. +*/ +#if !(defined(NC) || defined(ARDUINO_ARCH_STM32)) +#define NC (-1) +#endif + +/*! + \brief A simple assert macro, will return on error. +*/ +#define RADIOLIB_ASSERT(STATEVAR) { if(STATEVAR != ERR_NONE) { return(STATEVAR); } } + /*! \defgroup shield_config Shield Configuration @@ -69,37 +95,17 @@ /*! \brief Use SPI interface. */ -#define RADIOLIB_USE_SPI 0x00 +#define RADIOLIB_USE_SPI 0x00 /*! \brief Use UART interface. */ -#define RADIOLIB_USE_UART 0x01 +#define RADIOLIB_USE_UART 0x01 /*! \brief Use I2C interface. */ -#define RADIOLIB_USE_I2C 0x02 - -/*! - \brief Do not use any interrupts/GPIOs. -*/ -#define RADIOLIB_INT_NONE 0x00 - -/*! - \brief Use interrupt/GPIO 0. -*/ -#define RADIOLIB_INT_0 0x01 - -/*! - \brief Use interrupt/GPIO 1. -*/ -#define RADIOLIB_INT_1 0x02 - -/*! - \brief Use both interrupts/GPIOs. -*/ -#define RADIOLIB_INT_BOTH 0x03 +#define RADIOLIB_USE_I2C 0x02 /*! \} @@ -114,52 +120,52 @@ /*! \brief Use 1 bit stop. */ -#define RADIOLIB_UART_STOPBIT_1 0x01 +#define RADIOLIB_UART_STOPBIT_1 0x01 /*! \brief Use 1.5 bit stop. */ -#define RADIOLIB_UART_STOPBIT_1_5 0x02 +#define RADIOLIB_UART_STOPBIT_1_5 0x02 /*! \brief Use 2 bit stop. */ -#define RADIOLIB_UART_STOPBIT_2 0x03 +#define RADIOLIB_UART_STOPBIT_2 0x03 /*! \brief No parity. */ -#define RADIOLIB_UART_PARITY_NONE 0x00 +#define RADIOLIB_UART_PARITY_NONE 0x00 /*! \brief Odd parity. */ -#define RADIOLIB_UART_PARITY_ODD 0x01 +#define RADIOLIB_UART_PARITY_ODD 0x01 /*! \brief Even parity. */ -#define RADIOLIB_UART_PARITY_EVEN 0x02 +#define RADIOLIB_UART_PARITY_EVEN 0x02 /*! \brief No flow control. */ -#define RADIOLIB_UART_FLOW_NONE 0x00 +#define RADIOLIB_UART_FLOW_NONE 0x00 /*! \brief RTS only. */ -#define RADIOLIB_UART_FLOW_RTS 0x01 +#define RADIOLIB_UART_FLOW_RTS 0x01 /*! \brief CTS only. */ -#define RADIOLIB_UART_FLOW_CTS 0x02 +#define RADIOLIB_UART_FLOW_CTS 0x02 /*! \brief Both RTS and CTS. */ -#define RADIOLIB_UART_FLOW_BOTH 0x03 +#define RADIOLIB_UART_FLOW_BOTH 0x03 /*! \} @@ -176,13 +182,13 @@ /*! \brief No error, method executed successfully. */ -#define ERR_NONE 0 +#define ERR_NONE 0 /*! \brief There was an unexpected, unknown error. If you see this, something went incredibly wrong. Your Arduino may be possessed, contact your local exorcist to resolve this error. */ -#define ERR_UNKNOWN -1 +#define ERR_UNKNOWN -1 // SX127x/RFM9x status codes @@ -190,334 +196,372 @@ \brief Radio chip was not found during initialization. This can be caused by specifying wrong chip type in the constructor (i.e. calling SX1272 constructor for SX1278 chip) or by a fault in your wiring (incorrect slave select pin). */ -#define ERR_CHIP_NOT_FOUND -2 +#define ERR_CHIP_NOT_FOUND -2 /*! \brief Failed to allocate memory for temporary buffer. This can be cause by not enough RAM or by passing invalid pointer. */ -#define ERR_MEMORY_ALLOCATION_FAILED -3 +#define ERR_MEMORY_ALLOCATION_FAILED -3 /*! \brief Packet supplied to transmission method was longer than limit. */ -#define ERR_PACKET_TOO_LONG -4 +#define ERR_PACKET_TOO_LONG -4 /*! \brief Timed out waiting for transmission finish. */ -#define ERR_TX_TIMEOUT -5 +#define ERR_TX_TIMEOUT -5 /*! \brief Timed out waiting for incoming transmission. */ -#define ERR_RX_TIMEOUT -6 +#define ERR_RX_TIMEOUT -6 /*! \brief The calculated and expected CRCs of received packet do not match. This means that the packet was damaged during transmission and should be sent again. */ -#define ERR_CRC_MISMATCH -7 +#define ERR_CRC_MISMATCH -7 /*! \brief The supplied bandwidth value is invalid for this module. */ -#define ERR_INVALID_BANDWIDTH -8 +#define ERR_INVALID_BANDWIDTH -8 /*! \brief The supplied spreading factor value is invalid for this module. */ -#define ERR_INVALID_SPREADING_FACTOR -9 +#define ERR_INVALID_SPREADING_FACTOR -9 /*! \brief The supplied coding rate value is invalid for this module. */ -#define ERR_INVALID_CODING_RATE -10 +#define ERR_INVALID_CODING_RATE -10 /*! \brief Internal only. */ -#define ERR_INVALID_BIT_RANGE -11 +#define ERR_INVALID_BIT_RANGE -11 /*! \brief The supplied frequency value is invalid for this module. */ -#define ERR_INVALID_FREQUENCY -12 +#define ERR_INVALID_FREQUENCY -12 /*! \brief The supplied output power value is invalid for this module. */ -#define ERR_INVALID_OUTPUT_POWER -13 +#define ERR_INVALID_OUTPUT_POWER -13 /*! \brief LoRa preamble was detected during channel activity detection. This means that there is some LoRa device currently transmitting in your channel. */ -#define PREAMBLE_DETECTED -14 +#define PREAMBLE_DETECTED -14 /*! \brief No LoRa preambles were detected during channel activity detection. Your channel is free. */ -#define CHANNEL_FREE -15 +#define CHANNEL_FREE -15 /*! \brief Real value in SPI register does not match the expected one. This can be caused by faulty SPI wiring. */ -#define ERR_SPI_WRITE_FAILED -16 +#define ERR_SPI_WRITE_FAILED -16 /*! \brief The supplied current limit value is invalid. */ -#define ERR_INVALID_CURRENT_LIMIT -17 +#define ERR_INVALID_CURRENT_LIMIT -17 /*! \brief The supplied preamble length is invalid. */ -#define ERR_INVALID_PREAMBLE_LENGTH -18 +#define ERR_INVALID_PREAMBLE_LENGTH -18 /*! \brief The supplied gain value is invalid. */ -#define ERR_INVALID_GAIN -19 +#define ERR_INVALID_GAIN -19 /*! \brief User tried to execute modem-exclusive method on a wrong modem. For example, this can happen when you try to change LoRa configuration when FSK modem is active. */ -#define ERR_WRONG_MODEM -20 +#define ERR_WRONG_MODEM -20 /*! \brief The supplied number of RSSI samples is invalid. */ -#define ERR_INVALID_NUM_SAMPLES -21 +#define ERR_INVALID_NUM_SAMPLES -21 /*! \brief The supplied RSSI offset is invalid. */ -#define ERR_INVALID_RSSI_OFFSET -22 +#define ERR_INVALID_RSSI_OFFSET -22 /*! \brief The supplied encoding is invalid. */ -#define ERR_INVALID_ENCODING -23 +#define ERR_INVALID_ENCODING -23 // RF69-specific status codes /*! \brief The supplied bit rate value is invalid. */ -#define ERR_INVALID_BIT_RATE -101 +#define ERR_INVALID_BIT_RATE -101 /*! \brief The supplied frequency deviation value is invalid. */ -#define ERR_INVALID_FREQUENCY_DEVIATION -102 +#define ERR_INVALID_FREQUENCY_DEVIATION -102 /*! \brief The supplied bit rate to bandwidth ratio is invalid. See the module datasheet for more information. */ -#define ERR_INVALID_BIT_RATE_BW_RATIO -103 +#define ERR_INVALID_BIT_RATE_BW_RATIO -103 /*! \brief The supplied receiver bandwidth value is invalid. */ -#define ERR_INVALID_RX_BANDWIDTH -104 +#define ERR_INVALID_RX_BANDWIDTH -104 /*! \brief The supplied FSK sync word is invalid. */ -#define ERR_INVALID_SYNC_WORD -105 +#define ERR_INVALID_SYNC_WORD -105 /*! \brief The supplied FSK data shaping option is invalid. */ -#define ERR_INVALID_DATA_SHAPING -106 +#define ERR_INVALID_DATA_SHAPING -106 /*! \brief The current modulation is invalid for the requested operation. */ -#define ERR_INVALID_MODULATION -107 +#define ERR_INVALID_MODULATION -107 // ESP8266 status codes /*! \brief AT command failed to execute, or timed out. */ -#define ERR_AT_FAILED -201 +#define ERR_AT_FAILED -201 /*! \brief Supplied URL is malformed or invalid. */ -#define ERR_URL_MALFORMED -202 +#define ERR_URL_MALFORMED -202 /*! \brief AT command response was malformed. */ -#define ERR_RESPONSE_MALFORMED_AT -203 +#define ERR_RESPONSE_MALFORMED_AT -203 /*! \brief Data response was malformed. */ -#define ERR_RESPONSE_MALFORMED -204 +#define ERR_RESPONSE_MALFORMED -204 /*! \brief MQTT broker rejected connection due to version mismatch. */ -#define ERR_MQTT_CONN_VERSION_REJECTED -205 +#define ERR_MQTT_CONN_VERSION_REJECTED -205 /*! \brief MQTT broker rejected connection due to unknown ID. */ -#define ERR_MQTT_CONN_ID_REJECTED -206 +#define ERR_MQTT_CONN_ID_REJECTED -206 /*! \brief Failed to establish connection with MQTT broker. */ -#define ERR_MQTT_CONN_SERVER_UNAVAILABLE -207 +#define ERR_MQTT_CONN_SERVER_UNAVAILABLE -207 /*! \brief Supplied username/password combination is incorrect. */ -#define ERR_MQTT_CONN_BAD_USERNAME_PASSWORD -208 +#define ERR_MQTT_CONN_BAD_USERNAME_PASSWORD -208 /*! \brief Unauthorized connection to MQTT broker. */ -#define ERR_MQTT_CONN_NOT_AUTHORIZED -208 +#define ERR_MQTT_CONN_NOT_AUTHORIZED -208 /*! \brief Received packet ID does not match the expected ID. */ -#define ERR_MQTT_UNEXPECTED_PACKET_ID -209 +#define ERR_MQTT_UNEXPECTED_PACKET_ID -209 /*! \brief No new packet was received since the last check. */ -#define ERR_MQTT_NO_NEW_PACKET_AVAILABLE -210 +#define ERR_MQTT_NO_NEW_PACKET_AVAILABLE -210 /*! \brief Successfully subscribed to MQTT topic with QoS 0. */ -#define MQTT_SUBS_SUCCESS_QOS_0 0x00 +#define MQTT_SUBS_SUCCESS_QOS_0 0x00 /*! \brief Successfully subscribed to MQTT topic with QoS 1. */ -#define MQTT_SUBS_SUCCESS_QOS_1 0x01 +#define MQTT_SUBS_SUCCESS_QOS_1 0x01 /*! \brief Successfully subscribed to MQTT topic with QoS 2. */ -#define MQTT_SUBS_SUCCESS_QOS_2 0x02 +#define MQTT_SUBS_SUCCESS_QOS_2 0x02 /*! \brief Failed to subscribe to MQTT topic. */ -#define ERR_MQTT_SUBS_FAILED 0x80 +#define ERR_MQTT_SUBS_FAILED 0x80 // XBee status codes /*! \brief Failed to enter command mode. */ -#define ERR_CMD_MODE_FAILED -301 +#define ERR_CMD_MODE_FAILED -301 /*! \brief Received ZigBee frame is malformed. */ -#define ERR_FRAME_MALFORMED -302 +#define ERR_FRAME_MALFORMED -302 /*! \brief Received ZigBee frame checksum does not match the calculated. */ -#define ERR_FRAME_INCORRECT_CHECKSUM -303 +#define ERR_FRAME_INCORRECT_CHECKSUM -303 /*! \brief Received ZigBee frame with unexpected ID. */ -#define ERR_FRAME_UNEXPECTED_ID -304 +#define ERR_FRAME_UNEXPECTED_ID -304 /*! \brief Timed out waiting for response to ZigBee frame. */ -#define ERR_FRAME_NO_RESPONSE -305 +#define ERR_FRAME_NO_RESPONSE -305 // RTTY status codes /*! \brief Supplied RTTY frequency shift is invalid for this module. */ -#define ERR_INVALID_RTTY_SHIFT -401 +#define ERR_INVALID_RTTY_SHIFT -401 /*! \brief Supplied RTTY encoding is invalid. */ -#define ERR_UNSUPPORTED_ENCODING -402 +#define ERR_UNSUPPORTED_ENCODING -402 // nRF24-specific status codes /*! \brief Supplied data rate is invalid. */ -#define ERR_INVALID_DATA_RATE -501 +#define ERR_INVALID_DATA_RATE -501 /*! \brief Supplied address width is invalid. */ -#define ERR_INVALID_ADDRESS_WIDTH -502 +#define ERR_INVALID_ADDRESS_WIDTH -502 /*! \brief Supplied data pipe number is invalid. */ -#define ERR_INVALID_PIPE_NUMBER -503 +#define ERR_INVALID_PIPE_NUMBER -503 /*! \brief ACK packet from destination module was not received within 15 retries. */ -#define ERR_ACK_NOT_RECEIVED -504 +#define ERR_ACK_NOT_RECEIVED -504 // CC1101-specific status codes /*! \brief Supplied number of broadcast addresses is invalid. */ -#define ERR_INVALID_NUM_BROAD_ADDRS -601 +#define ERR_INVALID_NUM_BROAD_ADDRS -601 // SX126x-specific status codes /*! \brief Supplied CRC configuration is invalid. */ -#define ERR_INVALID_CRC_CONFIGURATION -701 +#define ERR_INVALID_CRC_CONFIGURATION -701 /*! \brief Detected LoRa transmission while scanning channel. */ -#define LORA_DETECTED -702 +#define LORA_DETECTED -702 /*! \brief Supplied TCXO reference voltage is invalid. */ -#define ERR_INVALID_TCXO_VOLTAGE -703 +#define ERR_INVALID_TCXO_VOLTAGE -703 /*! \brief Bit rate / bandwidth / frequency deviation ratio is invalid. See SX126x datasheet for details. */ -#define ERR_INVALID_MODULATION_PARAMETERS -704 +#define ERR_INVALID_MODULATION_PARAMETERS -704 /*! \brief SX126x timed out while waiting for complete SPI command. */ -#define ERR_SPI_CMD_TIMEOUT -705 +#define ERR_SPI_CMD_TIMEOUT -705 /*! \brief SX126x received invalid SPI command. */ -#define ERR_SPI_CMD_INVALID -706 +#define ERR_SPI_CMD_INVALID -706 /*! \brief SX126x failed to execute SPI command. */ -#define ERR_SPI_CMD_FAILED -707 +#define ERR_SPI_CMD_FAILED -707 + +/*! + \brief The supplied sleep period is invalid. + + The specified sleep period is shorter than the time necessary to sleep and wake the hardware + including TCXO delay, or longer than the maximum possible +*/ +#define ERR_INVALID_SLEEP_PERIOD -708 + +/*! + \brief The supplied Rx period is invalid. + + The specified Rx period is shorter or longer than the hardware can handle. +*/ +#define ERR_INVALID_RX_PERIOD -709 + +// AX.25-specific status codes + +/*! + \brief The provided callsign is invalid. + + The specified callsign is longer than 6 ASCII characters. +*/ +#define ERR_INVALID_CALLSIGN -801 + +/*! + \brief The provided repeater configuration is invalid. + + The specified number of repeaters does not match number of repeater IDs or their callsigns. +*/ +#define ERR_INVALID_NUM_REPEATERS -802 + +/*! + \brief One of the provided repeater callsigns is invalid. + + The specified callsign is longer than 6 ASCII characters. +*/ +#define ERR_INVALID_REPEATER_CALLSIGN -803 /*! \} diff --git a/src/modules/CC1101/CC1101.cpp b/src/modules/CC1101/CC1101.cpp index 6ee5cdad..a207bafb 100644 --- a/src/modules/CC1101/CC1101.cpp +++ b/src/modules/CC1101/CC1101.cpp @@ -1,18 +1,20 @@ #include "CC1101.h" -CC1101::CC1101(Module* module) : PhysicalLayer(CC1101_CRYSTAL_FREQ, CC1101_DIV_EXPONENT, CC1101_MAX_PACKET_LENGTH) { +CC1101::CC1101(Module* module) : PhysicalLayer(CC1101_FREQUENCY_STEP_SIZE, CC1101_MAX_PACKET_LENGTH) { _mod = module; _packetLengthQueried = false; _packetLengthConfig = CC1101_LENGTH_CONFIG_VARIABLE; + _modulation = CC1101_MOD_FORMAT_2_FSK; _syncWordLength = 2; } -int16_t CC1101::begin(float freq, float br, float rxBw, float freqDev, int8_t power, uint8_t preambleLength) { +int16_t CC1101::begin(float freq, float br, float freqDev, float rxBw, int8_t power, uint8_t preambleLength) { // set module properties _mod->SPIreadCommand = CC1101_CMD_READ; _mod->SPIwriteCommand = CC1101_CMD_WRITE; - _mod->init(RADIOLIB_USE_SPI, RADIOLIB_INT_0); + _mod->init(RADIOLIB_USE_SPI); + Module::pinMode(_mod->getIrq(), INPUT); // try to find the CC1101 chip uint8_t i = 0; @@ -40,7 +42,7 @@ int16_t CC1101::begin(float freq, float br, float rxBw, float freqDev, int8_t po if(!flagFound) { RADIOLIB_DEBUG_PRINTLN(F("No CC1101 found!")); - SPI.end(); + _mod->term(); return(ERR_CHIP_NOT_FOUND); } else { RADIOLIB_DEBUG_PRINTLN(F("Found CC1101! (match by CC1101_REG_VERSION == 0x14)")); @@ -48,51 +50,43 @@ int16_t CC1101::begin(float freq, float br, float rxBw, float freqDev, int8_t po // configure settings not accessible by API int16_t state = config(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure publicly accessible settings state = setFrequency(freq); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure bitrate state = setBitRate(br); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure default RX bandwidth state = setRxBandwidth(rxBw); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure default frequency deviation state = setFrequencyDeviation(freqDev); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure default TX output power state = setOutputPower(power); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set default packet length mode state = variablePacketLengthMode(); - if (state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure default preamble lenght state = setPreambleLength(preambleLength); - if (state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); + + // set default data shaping + state = setDataShaping(0); + RADIOLIB_ASSERT(state); + + // set default encoding + state = setEncoding(2); + RADIOLIB_ASSERT(state); // flush FIFOs SPIsendCommand(CC1101_CMD_FLUSH_RX); @@ -104,15 +98,17 @@ int16_t CC1101::begin(float freq, float br, float rxBw, float freqDev, int8_t po int16_t CC1101::transmit(uint8_t* data, size_t len, uint8_t addr) { // start transmission int16_t state = startTransmit(data, len, addr); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // wait for transmission start - while(!digitalRead(_mod->getInt0())); + while(!digitalRead(_mod->getIrq())) { + yield(); + } // wait for transmission end - while(digitalRead(_mod->getInt0())); + while(digitalRead(_mod->getIrq())) { + yield(); + } // set mode to standby standby(); @@ -126,15 +122,17 @@ int16_t CC1101::transmit(uint8_t* data, size_t len, uint8_t addr) { int16_t CC1101::receive(uint8_t* data, size_t len) { // start reception int16_t state = startReceive(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // wait for sync word - while(!digitalRead(_mod->getInt0())); + while(!digitalRead(_mod->getIrq())) { + yield(); + } // wait for packet end - while(digitalRead(_mod->getInt0())); + while(digitalRead(_mod->getIrq())) { + yield(); + } // read packet data return(readData(data, len)); @@ -157,9 +155,7 @@ int16_t CC1101::transmitDirect(uint32_t frf) { // activate direct mode int16_t state = directMode(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // start transmitting SPIsendCommand(CC1101_CMD_TX); @@ -169,9 +165,7 @@ int16_t CC1101::transmitDirect(uint32_t frf) { int16_t CC1101::receiveDirect() { // activate direct mode int16_t state = directMode(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // start receiving SPIsendCommand(CC1101_CMD_RX); @@ -186,11 +180,26 @@ int16_t CC1101::packetMode() { } void CC1101::setGdo0Action(void (*func)(void), uint8_t dir) { - attachInterrupt(digitalPinToInterrupt(_mod->getInt0()), func, dir); + attachInterrupt(digitalPinToInterrupt(_mod->getIrq()), func, dir); +} + +void CC1101::clearGdo0Action() { + detachInterrupt(digitalPinToInterrupt(_mod->getIrq())); } void CC1101::setGdo2Action(void (*func)(void), uint8_t dir) { - attachInterrupt(digitalPinToInterrupt(_mod->getInt1()), func, dir); + if(_mod->getGpio() != NC) { + return; + } + Module::pinMode(_mod->getGpio(), INPUT); + attachInterrupt(digitalPinToInterrupt(_mod->getGpio()), func, dir); +} + +void CC1101::clearGdo2Action() { + if(_mod->getGpio() != NC) { + return; + } + detachInterrupt(digitalPinToInterrupt(_mod->getGpio())); } int16_t CC1101::startTransmit(uint8_t* data, size_t len, uint8_t addr) { @@ -206,10 +215,8 @@ int16_t CC1101::startTransmit(uint8_t* data, size_t len, uint8_t addr) { SPIsendCommand(CC1101_CMD_FLUSH_TX); // set GDO0 mapping - int state = SPIsetRegValue(CC1101_REG_IOCFG0, CC1101_GDOX_SYNC_WORD_SENT_OR_RECEIVED); - if(state != ERR_NONE) { - return(state); - } + int16_t state = SPIsetRegValue(CC1101_REG_IOCFG0, CC1101_GDOX_SYNC_WORD_SENT_OR_RECEIVED); + RADIOLIB_ASSERT(state); // optionally write packet length if (_packetLengthConfig == CC1101_LENGTH_CONFIG_VARIABLE) { @@ -240,9 +247,7 @@ int16_t CC1101::startReceive() { // set GDO0 mapping int state = SPIsetRegValue(CC1101_REG_IOCFG0, CC1101_GDOX_SYNC_WORD_SENT_OR_RECEIVED); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set mode to receive SPIsendCommand(CC1101_CMD_RX); @@ -273,9 +278,6 @@ int16_t CC1101::readData(uint8_t* data, size_t len) { uint8_t val = SPIgetRegValue(CC1101_REG_FIFO); _rawLQI = val & 0x7F; - // add terminating null - data[length] = 0; - // flush Rx FIFO SPIsendCommand(CC1101_CMD_FLUSH_RX); @@ -286,8 +288,8 @@ int16_t CC1101::readData(uint8_t* data, size_t len) { standby(); // check CRC - if((val & 0b10000000) == 0b00000000) { - return(ERR_CRC_MISMATCH); + if (_crcOn && (val & 0b10000000) == 0b00000000) { + return (ERR_CRC_MISMATCH); } return(ERR_NONE); @@ -315,7 +317,8 @@ int16_t CC1101::setFrequency(float freq) { _freq = freq; } - return(state); + // Update the TX power accordingly to new freq. (PA values depend on chosen freq) + return(setOutputPower(_power)); } int16_t CC1101::setBitRate(float br) { @@ -351,14 +354,14 @@ int16_t CC1101::setRxBandwidth(float rxBw) { for(int8_t e = 3; e >= 0; e--) { for(int8_t m = 3; m >= 0; m --) { float point = (CC1101_CRYSTAL_FREQ * 1000000.0)/(8 * (m + 4) * ((uint32_t)1 << e)); - if(abs((rxBw * 1000.0) - point) <= 0.001) { + if(abs((rxBw * 1000.0) - point) <= 1000) { // set Rx channel filter bandwidth return(SPIsetRegValue(CC1101_REG_MDMCFG4, (e << 6) | (m << 4), 7, 4)); } } } - return(ERR_UNKNOWN); + return(ERR_INVALID_RX_BANDWIDTH); } int16_t CC1101::setFrequencyDeviation(float freqDev) { @@ -445,12 +448,27 @@ int16_t CC1101::setOutputPower(int8_t power) { return(ERR_INVALID_OUTPUT_POWER); } - // write raw power setting - return(SPIsetRegValue(CC1101_REG_PATABLE, powerRaw)); + // store the value + _power = power; + + if(_modulation == CC1101_MOD_FORMAT_ASK_OOK){ + // Amplitude modulation: + // PA_TABLE[0] is the power to be used when transmitting a 0 (no power) + // PA_TABLE[1] is the power to be used when transmitting a 1 (full power) + + uint8_t paValues[2] = {0x00, powerRaw}; + SPIwriteRegisterBurst(CC1101_REG_PATABLE, paValues, 2); + return(ERR_NONE); + + } else { + // Freq modulation: + // PA_TABLE[0] is the power to be used when transmitting. + return(SPIsetRegValue(CC1101_REG_PATABLE, powerRaw)); + } } -int16_t CC1101::setSyncWord(uint8_t* syncWord, uint8_t len, uint8_t maxErrBits) { - if((maxErrBits > 1) || (len > 2)) { +int16_t CC1101::setSyncWord(uint8_t* syncWord, uint8_t len, uint8_t maxErrBits, bool requireCarrierSense) { + if((maxErrBits > 1) || (len != 2)) { return(ERR_INVALID_SYNC_WORD); } @@ -464,20 +482,19 @@ int16_t CC1101::setSyncWord(uint8_t* syncWord, uint8_t len, uint8_t maxErrBits) _syncWordLength = len; // enable sync word filtering - int16_t state = enableSyncWordFiltering(maxErrBits); - if (state != ERR_NONE) { - return(state); - } + int16_t state = enableSyncWordFiltering(maxErrBits, requireCarrierSense); + RADIOLIB_ASSERT(state); // set sync word register - _mod->SPIwriteRegisterBurst(CC1101_REG_SYNC1, syncWord, len); + state = SPIsetRegValue(CC1101_REG_SYNC1, syncWord[0]); + state |= SPIsetRegValue(CC1101_REG_SYNC0, syncWord[1]); - return(ERR_NONE); + return(state); } -int16_t CC1101::setSyncWord(uint8_t syncH, uint8_t syncL, uint8_t maxErrBits) { +int16_t CC1101::setSyncWord(uint8_t syncH, uint8_t syncL, uint8_t maxErrBits, bool requireCarrierSense) { uint8_t syncWord[] = { syncH, syncL }; - return(setSyncWord(syncWord, sizeof(syncWord), maxErrBits)); + return(setSyncWord(syncWord, sizeof(syncWord), maxErrBits, requireCarrierSense)); } int16_t CC1101::setPreambleLength(uint8_t preambleLength) { @@ -524,9 +541,7 @@ int16_t CC1101::setNodeAddress(uint8_t nodeAddr, uint8_t numBroadcastAddrs) { // enable address filtering int16_t state = SPIsetRegValue(CC1101_REG_PKTCTRL1, numBroadcastAddrs + 0x01, 1, 0); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set node address return(SPIsetRegValue(CC1101_REG_ADDR, nodeAddr)); @@ -535,14 +550,44 @@ int16_t CC1101::setNodeAddress(uint8_t nodeAddr, uint8_t numBroadcastAddrs) { int16_t CC1101::disableAddressFiltering() { // disable address filtering int16_t state = _mod->SPIsetRegValue(CC1101_REG_PKTCTRL1, CC1101_ADR_CHK_NONE, 1, 0); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set node address to default (0x00) return(SPIsetRegValue(CC1101_REG_ADDR, 0x00)); } + +int16_t CC1101::setOOK(bool enableOOK) { + // Change modulation + if(enableOOK) { + int16_t state = SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_MOD_FORMAT_ASK_OOK, 6, 4); + RADIOLIB_ASSERT(state); + + // PA_TABLE[0] is (by default) the power value used when transmitting a "0L". + // Set PA_TABLE[1] to be used when transmitting a "1L". + state = SPIsetRegValue(CC1101_REG_FREND0, 1, 2, 0); + RADIOLIB_ASSERT(state); + + + // update current modulation + _modulation = CC1101_MOD_FORMAT_ASK_OOK; + } else { + int16_t state = SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_MOD_FORMAT_2_FSK, 6, 4); + RADIOLIB_ASSERT(state); + + // Reset FREND0 to default value. + state = SPIsetRegValue(CC1101_REG_FREND0, 0, 2, 0); + RADIOLIB_ASSERT(state); + + // update current modulation + _modulation = CC1101_MOD_FORMAT_2_FSK; + } + + // Update PA_TABLE values according to the new _modulation. + return(setOutputPower(_power)); +} + + float CC1101::getRSSI() { float rssi; if(_rawRSSI >= 128) { @@ -579,39 +624,29 @@ int16_t CC1101::variablePacketLengthMode(uint8_t maxLen) { return(setPacketMode(CC1101_LENGTH_CONFIG_VARIABLE, maxLen)); } -int16_t CC1101::enableSyncWordFiltering(uint8_t maxErrBits) { - if (maxErrBits > 1) { - return(ERR_INVALID_SYNC_WORD); +int16_t CC1101::enableSyncWordFiltering(uint8_t maxErrBits, bool requireCarrierSense) { + switch (maxErrBits){ + case 0: + // in 16 bit sync word, expect all 16 bits. + return (SPIsetRegValue(CC1101_REG_MDMCFG2, + requireCarrierSense ? CC1101_SYNC_MODE_16_16_THR : CC1101_SYNC_MODE_16_16, 2, 0)); + case 1: + // in 16 bit sync word, expect at least 15 bits. + return (SPIsetRegValue(CC1101_REG_MDMCFG2, + requireCarrierSense ? CC1101_SYNC_MODE_15_16_THR : CC1101_SYNC_MODE_15_16, 2, 0)); + default: + return (ERR_INVALID_SYNC_WORD); } - - if (maxErrBits == 0) { - if (_syncWordLength == 1) { - // in 16 bit sync word, expect all 16 bits - return(SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_SYNC_MODE_16_16, 2, 0)); - } else { - // there's no 32 of 32 case, so we resort to 30 of 32 bits required - return(SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_SYNC_MODE_30_32, 2, 0)); - } - } - - if (maxErrBits == 1) { - if (_syncWordLength == 1) { - // in 16 bit sync word, expect at least 15 bits - return(SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_SYNC_MODE_15_16, 2, 0)); - } else { - // in 32 bits sync word (16 + 16), expect 30 of 32 to match - return(SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_SYNC_MODE_30_32, 2, 0)); - } - } - - return(ERR_UNKNOWN); } -int16_t CC1101::disableSyncWordFiltering() { - return(SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_SYNC_MODE_NONE, 2, 0)); +int16_t CC1101::disableSyncWordFiltering(bool requireCarrierSense) { + return(SPIsetRegValue(CC1101_REG_MDMCFG2, + requireCarrierSense ? CC1101_SYNC_MODE_NONE_THR : CC1101_SYNC_MODE_NONE, 2, 0)); } int16_t CC1101::setCrcFiltering(bool crcOn) { + _crcOn = crcOn; + if (crcOn == true) { return(SPIsetRegValue(CC1101_REG_PKTCTRL0, CC1101_CRC_ON, 2, 2)); } else { @@ -629,18 +664,14 @@ int16_t CC1101::setPromiscuousMode(bool promiscuous) { if (promiscuous == true) { // disable preamble and sync word filtering and insertion state = disableSyncWordFiltering(); - if (state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // disable CRC filtering state = setCrcFiltering(false); } else { // enable preamble and sync word filtering and insertion state = enableSyncWordFiltering(); - if (state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // enable CRC filtering state = setCrcFiltering(true); @@ -649,12 +680,57 @@ int16_t CC1101::setPromiscuousMode(bool promiscuous) { return(state); } +int16_t CC1101::setDataShaping(float sh) { + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // set data shaping + sh *= 10.0; + if(abs(sh - 0.0) <= 0.001) { + state = _mod->SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_MOD_FORMAT_2_FSK, 6, 4); + } else if(abs(sh - 5.0) <= 0.001) { + state = _mod->SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_MOD_FORMAT_GFSK, 6, 4); + } else { + return(ERR_INVALID_DATA_SHAPING); + } + return(state); +} + +int16_t CC1101::setEncoding(uint8_t encoding) { + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // set encoding + switch(encoding) { + case 0: + state = _mod->SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_MANCHESTER_EN_OFF, 3, 3); + RADIOLIB_ASSERT(state); + return(_mod->SPIsetRegValue(CC1101_REG_PKTCTRL0, CC1101_WHITE_DATA_OFF, 6, 6)); + case 1: + state = _mod->SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_MANCHESTER_EN_ON, 3, 3); + RADIOLIB_ASSERT(state); + return(_mod->SPIsetRegValue(CC1101_REG_PKTCTRL0, CC1101_WHITE_DATA_OFF, 6, 6)); + case 2: + state = _mod->SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_MANCHESTER_EN_OFF, 3, 3); + RADIOLIB_ASSERT(state); + return(_mod->SPIsetRegValue(CC1101_REG_PKTCTRL0, CC1101_WHITE_DATA_ON, 6, 6)); + default: + return(ERR_INVALID_ENCODING); + } +} + int16_t CC1101::config() { + // Reset the radio. Registers may be dirty from previous usage. + SPIsendCommand(CC1101_CMD_RESET); + + // Wait a ridiculous amount of time to be sure radio is ready. + delay(150); + // enable automatic frequency synthesizer calibration int16_t state = SPIsetRegValue(CC1101_REG_MCSM0, CC1101_FS_AUTOCAL_IDLE_TO_RXTX, 5, 4); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set packet mode state = packetMode(); @@ -707,19 +783,16 @@ int16_t CC1101::setPacketMode(uint8_t mode, uint8_t len) { return(ERR_PACKET_TOO_LONG); } - // set to fixed packet length - int16_t state = _mod->SPIsetRegValue(CC1101_REG_PKTCTRL0, mode, 7, 7); - if (state != ERR_NONE) { - return(state); - } + // set PKTCTRL0.LENGTH_CONFIG + int16_t state = _mod->SPIsetRegValue(CC1101_REG_PKTCTRL0, mode, 1, 0); + RADIOLIB_ASSERT(state); // set length to register state = _mod->SPIsetRegValue(CC1101_REG_PKTLEN, len); - if (state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // update the cached value + _packetLength = len; _packetLengthConfig = mode; return(state); } @@ -769,9 +842,9 @@ void CC1101::SPIwriteRegisterBurst(uint8_t reg, uint8_t* data, size_t len) { } void CC1101::SPIsendCommand(uint8_t cmd) { - digitalWrite(_mod->getCs(), LOW); + Module::digitalWrite(_mod->getCs(), LOW); SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0)); SPI.transfer(cmd); SPI.endTransaction(); - digitalWrite(_mod->getCs(), HIGH); + Module::digitalWrite(_mod->getCs(), HIGH); } diff --git a/src/modules/CC1101/CC1101.h b/src/modules/CC1101/CC1101.h index 7622efcd..2486647f 100644 --- a/src/modules/CC1101/CC1101.h +++ b/src/modules/CC1101/CC1101.h @@ -7,9 +7,10 @@ #include "../../protocols/PhysicalLayer/PhysicalLayer.h" // CC1101 physical layer properties +#define CC1101_FREQUENCY_STEP_SIZE 396.7285156 +#define CC1101_MAX_PACKET_LENGTH 63 #define CC1101_CRYSTAL_FREQ 26.0 #define CC1101_DIV_EXPONENT 16 -#define CC1101_MAX_PACKET_LENGTH 63 // CC1101 SPI commands #define CC1101_CMD_READ 0b10000000 @@ -524,17 +525,17 @@ class CC1101: public PhysicalLayer { \param br Bit rate to be used in kbps. Defaults to 4.8 kbps. - \param rxBw Receiver bandwidth in kHz. Defaults to 325.0 kHz. - \param freqDev Frequency deviation from carrier frequency in kHz Defaults to 48.0 kHz. + \param rxBw Receiver bandwidth in kHz. Defaults to 325.0 kHz. + \param power Output power in dBm. Defaults to 0 dBm. \param preambleLength Preamble Length in bytes. Defaults to 4 bytes. \returns \ref status_codes */ - int16_t begin(float freq = 868.0, float br = 4.8, float rxBw = 325.0, float freqDev = 48.0, int8_t power = 0, uint8_t preambleLength = 4); + int16_t begin(float freq = 868.0, float br = 4.8, float freqDev = 48.0, float rxBw = 325.0, int8_t power = 0, uint8_t preambleLength = 4); /*! \brief Blocking binary transmit method. @@ -601,6 +602,11 @@ class CC1101: public PhysicalLayer { */ void setGdo0Action(void (*func)(void), uint8_t dir = FALLING); + /*! + \brief Clears interrupt service routine to call when GDO0 activates. + */ + void clearGdo0Action(); + /*! \brief Sets interrupt service routine to call when GDO2 activates. @@ -610,6 +616,11 @@ class CC1101: public PhysicalLayer { */ void setGdo2Action(void (*func)(void), uint8_t dir = FALLING); + /*! + \brief Clears interrupt service routine to call when GDO0 activates. + */ + void clearGdo2Action(); + /*! \brief Interrupt-driven binary transmit method. Overloads for string-based transmissions are implemented in PhysicalLayer. @@ -698,9 +709,11 @@ class CC1101: public PhysicalLayer { \param maxErrBits Maximum allowed number of bit errors in received sync word. Defaults to 0. + \param requireCarrierSense Require carrier sense above threshold in addition to sync word. + \returns \ref status_codes */ - int16_t setSyncWord(uint8_t syncH, uint8_t syncL, uint8_t maxErrBits = 0); + int16_t setSyncWord(uint8_t syncH, uint8_t syncL, uint8_t maxErrBits = 0, bool requireCarrierSense = false); /*! \brief Sets 1 or 2 bytes of sync word. @@ -711,9 +724,11 @@ class CC1101: public PhysicalLayer { \param maxErrBits Maximum allowed number of bit errors in received sync word. Defaults to 0. + \param requireCarrierSense Require carrier sense above threshold in addition to sync word. + \returns \ref status_codes */ - int16_t setSyncWord(uint8_t* syncWord, uint8_t len, uint8_t maxErrBits = 0); + int16_t setSyncWord(uint8_t* syncWord, uint8_t len, uint8_t maxErrBits = 0, bool requireCarrierSense = false); /*! \brief Sets preamble length. @@ -742,6 +757,15 @@ class CC1101: public PhysicalLayer { */ int16_t disableAddressFiltering(); + /*! + \brief Enables/disables OOK modulation instead of FSK. + + \param enableOOK Enable (true) or disable (false) OOK. + + \returns \ref status_codes + */ + int16_t setOOK(bool enableOOK); + /*! \brief Gets RSSI (Recorded Signal Strength Indicator) of the last received packet. @@ -788,16 +812,20 @@ class CC1101: public PhysicalLayer { \param numBits Sync word length in bits. + \param requireCarrierSense Require carrier sense above threshold in addition to sync word. + \returns \ref status_codes */ - int16_t enableSyncWordFiltering(uint8_t maxErrBits = 0); + int16_t enableSyncWordFiltering(uint8_t maxErrBits = 0, bool requireCarrierSense = false); /*! \brief Disable preamble and sync word filtering and generation. + \param requireCarrierSense Require carrier sense above threshold. + \returns \ref status_codes */ - int16_t disableSyncWordFiltering(); + int16_t disableSyncWordFiltering(bool requireCarrierSense = false); /*! \brief Enable CRC filtering and generation. @@ -817,6 +845,25 @@ class CC1101: public PhysicalLayer { */ int16_t setPromiscuousMode(bool promiscuous = true); + /*! + \brief Sets Gaussian filter bandwidth-time product that will be used for data shaping. + Allowed value is 0.5. Set to 0 to disable data shaping. + + \param sh Gaussian shaping bandwidth-time product that will be used for data shaping + + \returns \ref status_codes + */ + int16_t setDataShaping(float sh); + + /*! + \brief Sets transmission encoding. + + \param encoding Encoding to be used. Set to 0 for NRZ, 1 for Manchester and 2 for whitening. + + \returns \ref status_codes + */ + int16_t setEncoding(uint8_t encoding); + #ifndef RADIOLIB_GODMODE private: #endif @@ -825,14 +872,17 @@ class CC1101: public PhysicalLayer { float _freq; uint8_t _rawRSSI; uint8_t _rawLQI; + uint8_t _modulation; size_t _packetLength; bool _packetLengthQueried; uint8_t _packetLengthConfig; bool _promiscuous; + bool _crcOn = true; uint8_t _syncWordLength; + int8_t _power; int16_t config(); int16_t directMode(); diff --git a/src/modules/ESP8266/ESP8266.cpp b/src/modules/ESP8266/ESP8266.cpp index e3ff2fbb..ab50f640 100644 --- a/src/modules/ESP8266/ESP8266.cpp +++ b/src/modules/ESP8266/ESP8266.cpp @@ -9,7 +9,7 @@ int16_t ESP8266::begin(long speed) { // set module properties _mod->AtLineFeed = "\r\n"; _mod->baudrate = speed; - _mod->init(RADIOLIB_USE_UART, RADIOLIB_INT_NONE); + _mod->init(RADIOLIB_USE_UART); // empty UART buffer (garbage data) _mod->ATemptyBuffer(); diff --git a/src/modules/HC05/HC05.cpp b/src/modules/HC05/HC05.cpp index be54f672..2aecab76 100644 --- a/src/modules/HC05/HC05.cpp +++ b/src/modules/HC05/HC05.cpp @@ -1,11 +1,11 @@ #include "HC05.h" HC05::HC05(Module* mod) : ISerial(mod) { - + } void HC05::begin(long speed) { // set module properties _mod->baudrate = speed; - _mod->init(RADIOLIB_USE_UART, RADIOLIB_INT_NONE); + _mod->init(RADIOLIB_USE_UART); } diff --git a/src/modules/JDY08/JDY08.cpp b/src/modules/JDY08/JDY08.cpp index c4eee4a3..78bc596b 100644 --- a/src/modules/JDY08/JDY08.cpp +++ b/src/modules/JDY08/JDY08.cpp @@ -1,12 +1,12 @@ #include "JDY08.h" JDY08::JDY08(Module* mod) : ISerial(mod) { - + } void JDY08::begin(long speed) { // set module properties _mod->AtLineFeed = ""; _mod->baudrate = speed; - _mod->init(RADIOLIB_USE_UART, RADIOLIB_INT_NONE); + _mod->init(RADIOLIB_USE_UART); } diff --git a/src/modules/RF69/RF69.cpp b/src/modules/RF69/RF69.cpp index 1f236485..b652a5fd 100644 --- a/src/modules/RF69/RF69.cpp +++ b/src/modules/RF69/RF69.cpp @@ -1,6 +1,6 @@ #include "RF69.h" -RF69::RF69(Module* module) : PhysicalLayer(RF69_CRYSTAL_FREQ, RF69_DIV_EXPONENT, RF69_MAX_PACKET_LENGTH) { +RF69::RF69(Module* module) : PhysicalLayer(RF69_FREQUENCY_STEP_SIZE, RF69_MAX_PACKET_LENGTH) { _mod = module; _tempOffset = 0; @@ -12,14 +12,19 @@ RF69::RF69(Module* module) : PhysicalLayer(RF69_CRYSTAL_FREQ, RF69_DIV_EXPONENT, _syncWordLength = 2; } -int16_t RF69::begin(float freq, float br, float rxBw, float freqDev, int8_t power) { +int16_t RF69::begin(float freq, float br, float freqDev, float rxBw, int8_t power) { // set module properties - _mod->init(RADIOLIB_USE_SPI, RADIOLIB_INT_0); + _mod->init(RADIOLIB_USE_SPI); + Module::pinMode(_mod->getIrq(), INPUT); // try to find the RF69 chip uint8_t i = 0; bool flagFound = false; while((i < 10) && !flagFound) { + // reset the module + reset(); + + // check version register uint8_t version = _mod->SPIreadRegister(RF69_REG_VERSION); if(version == 0x24) { flagFound = true; @@ -42,7 +47,7 @@ int16_t RF69::begin(float freq, float br, float rxBw, float freqDev, int8_t powe if(!flagFound) { RADIOLIB_DEBUG_PRINTLN(F("No RF69 found!")); - SPI.end(); + _mod->term(); return(ERR_CHIP_NOT_FOUND); } else { RADIOLIB_DEBUG_PRINTLN(F("Found RF69! (match by RF69_REG_VERSION == 0x24)")); @@ -50,66 +55,77 @@ int16_t RF69::begin(float freq, float br, float rxBw, float freqDev, int8_t powe // configure settings not accessible by API int16_t state = config(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure publicly accessible settings state = setFrequency(freq); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure bitrate _rxBw = 125.0; state = setBitRate(br); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure default RX bandwidth state = setRxBandwidth(rxBw); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure default frequency deviation state = setFrequencyDeviation(freqDev); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure default TX output power state = setOutputPower(power); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set default packet length mode state = variablePacketLengthMode(); - if (state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // default sync word values 0x2D01 is the same as the default in LowPowerLab RFM69 library uint8_t syncWord[] = {0x2D, 0x01}; state = setSyncWord(syncWord, sizeof(syncWord)); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); + + // set default data shaping + state = setDataShaping(0); + RADIOLIB_ASSERT(state); + + // set default encoding + state = setEncoding(0); + RADIOLIB_ASSERT(state); return(ERR_NONE); } +void RF69::reset() { + Module::pinMode(_mod->getRst(), OUTPUT); + Module::digitalWrite(_mod->getRst(), HIGH); + delayMicroseconds(100); + Module::digitalWrite(_mod->getRst(), LOW); + delay(10); +} + int16_t RF69::transmit(uint8_t* data, size_t len, uint8_t addr) { + // calculate timeout (5ms + 500 % of expected time-on-air) + uint32_t timeout = 5000000 + (uint32_t)((((float)(len * 8)) / (_br * 1000.0)) * 5000000.0); + // start transmission int16_t state = startTransmit(data, len, addr); - if(state != ERR_NONE) { - return(state); + RADIOLIB_ASSERT(state); + + // wait for transmission end or timeout + uint32_t start = micros(); + while(!digitalRead(_mod->getIrq())) { + if(micros() - start > timeout) { + standby(); + clearIRQFlags(); + return(ERR_TX_TIMEOUT); + } } - // wait for transmission end - while(!digitalRead(_mod->getInt0())); + // set mode to standby + standby(); // clear interrupt flags clearIRQFlags(); @@ -118,15 +134,18 @@ int16_t RF69::transmit(uint8_t* data, size_t len, uint8_t addr) { } int16_t RF69::receive(uint8_t* data, size_t len) { + // calculate timeout (500 ms + 400 full 64-byte packets at current bit rate) + uint32_t timeout = 500000 + (1.0/(_br*1000.0))*(RF69_MAX_PACKET_LENGTH*400.0); + // start reception - int16_t state = startReceive(true); - if(state != ERR_NONE) { - return(state); - } + int16_t state = startReceive(); + RADIOLIB_ASSERT(state); // wait for packet reception or timeout - while(!digitalRead(_mod->getInt0())) { - if(digitalRead(_mod->getInt1())) { + uint32_t start = micros(); + while(!digitalRead(_mod->getIrq())) { + if(micros() - start > timeout) { + standby(); clearIRQFlags(); return(ERR_RX_TIMEOUT); } @@ -158,9 +177,7 @@ int16_t RF69::transmitDirect(uint32_t frf) { // activate direct mode int16_t state = directMode(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // start transmitting return(setMode(RF69_TX)); @@ -169,9 +186,7 @@ int16_t RF69::transmitDirect(uint32_t frf) { int16_t RF69::receiveDirect() { // activate direct mode int16_t state = directMode(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // start receiving return(setMode(RF69_RX)); @@ -180,16 +195,14 @@ int16_t RF69::receiveDirect() { int16_t RF69::directMode() { // set mode to standby int16_t state = setMode(RF69_STANDBY); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set DIO mapping state = _mod->SPIsetRegValue(RF69_REG_DIO_MAPPING_1, RF69_DIO1_CONT_DCLK | RF69_DIO2_CONT_DATA, 5, 2); + RADIOLIB_ASSERT(state); // set continuous mode - state |= _mod->SPIsetRegValue(RF69_REG_DATA_MODUL, RF69_CONTINUOUS_MODE_WITH_SYNC, 6, 5); - return(state); + return(_mod->SPIsetRegValue(RF69_REG_DATA_MODUL, RF69_CONTINUOUS_MODE_WITH_SYNC, 6, 5)); } int16_t RF69::packetMode() { @@ -208,23 +221,15 @@ int16_t RF69::disableAES() { return(_mod->SPIsetRegValue(RF69_REG_PACKET_CONFIG_2, RF69_AES_OFF, 0, 0)); } -int16_t RF69::startReceive(bool timeout) { +int16_t RF69::startReceive() { // set mode to standby int16_t state = setMode(RF69_STANDBY); // set RX timeouts and DIO pin mapping - if(timeout) { - state = _mod->SPIsetRegValue(RF69_REG_DIO_MAPPING_1, RF69_DIO0_PACK_PAYLOAD_READY | RF69_DIO1_PACK_TIMEOUT, 7, 4); - state |= _mod->SPIsetRegValue(RF69_REG_RX_TIMEOUT_1, RF69_TIMEOUT_RX_START); - state |= _mod->SPIsetRegValue(RF69_REG_RX_TIMEOUT_2, RF69_TIMEOUT_RSSI_THRESH); - } else { - state = _mod->SPIsetRegValue(RF69_REG_DIO_MAPPING_1, RF69_DIO0_PACK_PAYLOAD_READY, 7, 6); - state |= _mod->SPIsetRegValue(RF69_REG_RX_TIMEOUT_1, RF69_TIMEOUT_RX_START_OFF); - state |= _mod->SPIsetRegValue(RF69_REG_RX_TIMEOUT_2, RF69_TIMEOUT_RSSI_THRESH_OFF); - } - if(state != ERR_NONE) { - return(state); - } + state = _mod->SPIsetRegValue(RF69_REG_DIO_MAPPING_1, RF69_DIO0_PACK_PAYLOAD_READY, 7, 4); + state |= _mod->SPIsetRegValue(RF69_REG_RX_TIMEOUT_1, RF69_TIMEOUT_RX_START); + state |= _mod->SPIsetRegValue(RF69_REG_RX_TIMEOUT_2, RF69_TIMEOUT_RSSI_THRESH); + RADIOLIB_ASSERT(state); // clear interrupt flags clearIRQFlags(); @@ -238,11 +243,26 @@ int16_t RF69::startReceive(bool timeout) { } void RF69::setDio0Action(void (*func)(void)) { - attachInterrupt(digitalPinToInterrupt(_mod->getInt0()), func, RISING); + attachInterrupt(digitalPinToInterrupt(_mod->getIrq()), func, RISING); +} + +void RF69::clearDio0Action() { + detachInterrupt(digitalPinToInterrupt(_mod->getIrq())); } void RF69::setDio1Action(void (*func)(void)) { - attachInterrupt(digitalPinToInterrupt(_mod->getInt1()), func, RISING); + if(_mod->getGpio() != NC) { + return; + } + Module::pinMode(_mod->getGpio(), INPUT); + attachInterrupt(digitalPinToInterrupt(_mod->getGpio()), func, RISING); +} + +void RF69::clearDio1Action() { + if(_mod->getGpio() != NC) { + return; + } + detachInterrupt(digitalPinToInterrupt(_mod->getGpio())); } int16_t RF69::startTransmit(uint8_t* data, size_t len, uint8_t addr) { @@ -253,12 +273,11 @@ int16_t RF69::startTransmit(uint8_t* data, size_t len, uint8_t addr) { // set mode to standby int16_t state = setMode(RF69_STANDBY); + RADIOLIB_ASSERT(state); // set DIO pin mapping - state |= _mod->SPIsetRegValue(RF69_REG_DIO_MAPPING_1, RF69_DIO0_PACK_PACKET_SENT, 7, 6); - if(state != ERR_NONE) { - return(state); - } + state = _mod->SPIsetRegValue(RF69_REG_DIO_MAPPING_1, RF69_DIO0_PACK_PACKET_SENT, 7, 6); + RADIOLIB_ASSERT(state); // clear interrupt flags clearIRQFlags(); @@ -291,10 +310,7 @@ int16_t RF69::startTransmit(uint8_t* data, size_t len, uint8_t addr) { int16_t RF69::readData(uint8_t* data, size_t len) { // set mdoe to standby int16_t state = standby(); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); // get packet length size_t length = len; if(len == RF69_MAX_PACKET_LENGTH) { @@ -310,9 +326,6 @@ int16_t RF69::readData(uint8_t* data, size_t len) { // read packet data _mod->SPIreadRegisterBurst(RF69_REG_FIFO, length, data); - // add terminating null - data[length] = 0; - // update RSSI lastPacketRSSI = -1.0 * (_mod->SPIgetRegValue(RF69_REG_RSSI_VALUE)/2.0); @@ -520,9 +533,7 @@ int16_t RF69::setSyncWord(uint8_t* syncWord, size_t len, uint8_t maxErrBits) { _syncWordLength = len; int16_t state = enableSyncWordFiltering(maxErrBits); - if (state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set sync word register _mod->SPIwriteRegisterBurst(RF69_REG_SYNC_VALUE_1, syncWord, len); @@ -532,9 +543,7 @@ int16_t RF69::setSyncWord(uint8_t* syncWord, size_t len, uint8_t maxErrBits) { int16_t RF69::setNodeAddress(uint8_t nodeAddr) { // enable address filtering (node only) int16_t state = _mod->SPIsetRegValue(RF69_REG_PACKET_CONFIG_1, RF69_ADDRESS_FILTERING_NODE, 2, 1); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set node address return(_mod->SPIsetRegValue(RF69_REG_NODE_ADRS, nodeAddr)); @@ -543,9 +552,7 @@ int16_t RF69::setNodeAddress(uint8_t nodeAddr) { int16_t RF69::setBroadcastAddress(uint8_t broadAddr) { // enable address filtering (node + broadcast) int16_t state = _mod->SPIsetRegValue(RF69_REG_PACKET_CONFIG_1, RF69_ADDRESS_FILTERING_NODE_BROADCAST, 2, 1); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set broadcast address return(_mod->SPIsetRegValue(RF69_REG_BROADCAST_ADRS, broadAddr)); @@ -554,15 +561,11 @@ int16_t RF69::setBroadcastAddress(uint8_t broadAddr) { int16_t RF69::disableAddressFiltering() { // disable address filtering int16_t state = _mod->SPIsetRegValue(RF69_REG_PACKET_CONFIG_1, RF69_ADDRESS_FILTERING_OFF, 2, 1); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set node address to default (0x00) state = _mod->SPIsetRegValue(RF69_REG_NODE_ADRS, 0x00); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set broadcast address to default (0x00) return(_mod->SPIsetRegValue(RF69_REG_BROADCAST_ADRS, 0x00)); @@ -612,27 +615,17 @@ int16_t RF69::variablePacketLengthMode(uint8_t maxLen) { int16_t RF69::enableSyncWordFiltering(uint8_t maxErrBits) { // enable sync word recognition - int16_t state = _mod->SPIsetRegValue(RF69_REG_SYNC_CONFIG, RF69_SYNC_ON | RF69_FIFO_FILL_CONDITION_SYNC | (_syncWordLength - 1) << 3 | maxErrBits, 7, 0); - if(state != ERR_NONE) { - return(state); - } - - return(state); + return(_mod->SPIsetRegValue(RF69_REG_SYNC_CONFIG, RF69_SYNC_ON | RF69_FIFO_FILL_CONDITION_SYNC | (_syncWordLength - 1) << 3 | maxErrBits, 7, 0)); } int16_t RF69::disableSyncWordFiltering() { // disable preamble detection and generation int16_t state = _mod->SPIsetRegValue(RF69_REG_PREAMBLE_LSB, 0, 7, 0); state |= _mod->SPIsetRegValue(RF69_REG_PREAMBLE_MSB, 0, 7, 0); - if (state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // disable sync word detection and generation state = _mod->SPIsetRegValue(RF69_REG_SYNC_CONFIG, RF69_SYNC_OFF | RF69_FIFO_FILL_CONDITION, 7, 6); - if (state != ERR_NONE) { - return(state); - } return(state); } @@ -655,18 +648,14 @@ int16_t RF69::setPromiscuousMode(bool promiscuous) { if (promiscuous == true) { // disable preamble and sync word filtering and insertion state = disableSyncWordFiltering(); - if (state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // disable CRC filtering state = setCrcFiltering(false); } else { // enable preamble and sync word filtering and insertion state = enableSyncWordFiltering(); - if (state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // enable CRC filtering state = setCrcFiltering(true); @@ -675,81 +664,97 @@ int16_t RF69::setPromiscuousMode(bool promiscuous) { return(state); } +int16_t RF69::setDataShaping(float sh) { + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // set data shaping + sh *= 10.0; + if(abs(sh - 0.0) <= 0.001) { + state = _mod->SPIsetRegValue(RF69_REG_DATA_MODUL, RF69_NO_SHAPING, 1, 0); + } else if(abs(sh - 3.0) <= 0.001) { + state = _mod->SPIsetRegValue(RF69_REG_DATA_MODUL, RF69_FSK_GAUSSIAN_0_3, 1, 0); + } else if(abs(sh - 5.0) <= 0.001) { + state = _mod->SPIsetRegValue(RF69_REG_DATA_MODUL, RF69_FSK_GAUSSIAN_0_5, 1, 0); + } else if(abs(sh - 10.0) <= 0.001) { + state = _mod->SPIsetRegValue(RF69_REG_DATA_MODUL, RF69_FSK_GAUSSIAN_1_0, 1, 0); + } else { + return(ERR_INVALID_DATA_SHAPING); + } + return(state); +} + +int16_t RF69::setEncoding(uint8_t encoding) { + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // set encoding + switch(encoding) { + case 0: + return(_mod->SPIsetRegValue(RF69_REG_PACKET_CONFIG_1, RF69_DC_FREE_NONE, 6, 5)); + case 1: + return(_mod->SPIsetRegValue(RF69_REG_PACKET_CONFIG_1, RF69_DC_FREE_MANCHESTER, 6, 5)); + case 2: + return(_mod->SPIsetRegValue(RF69_REG_PACKET_CONFIG_1, RF69_DC_FREE_WHITENING, 6, 5)); + default: + return(ERR_INVALID_ENCODING); + } +} + int16_t RF69::config() { int16_t state = ERR_NONE; // set mode to STANDBY state = setMode(RF69_STANDBY); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set operation modes state = _mod->SPIsetRegValue(RF69_REG_OP_MODE, RF69_SEQUENCER_ON | RF69_LISTEN_OFF, 7, 6); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // enable over-current protection state = _mod->SPIsetRegValue(RF69_REG_OCP, RF69_OCP_ON, 4, 4); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set data mode, modulation type and shaping state = _mod->SPIsetRegValue(RF69_REG_DATA_MODUL, RF69_PACKET_MODE | RF69_FSK, 6, 3); state |= _mod->SPIsetRegValue(RF69_REG_DATA_MODUL, RF69_FSK_GAUSSIAN_0_3, 1, 0); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set RSSI threshold state = _mod->SPIsetRegValue(RF69_REG_RSSI_THRESH, RF69_RSSI_THRESHOLD, 7, 0); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // reset FIFO flag _mod->SPIwriteRegister(RF69_REG_IRQ_FLAGS_2, RF69_IRQ_FIFO_OVERRUN); // disable ClkOut on DIO5 state = _mod->SPIsetRegValue(RF69_REG_DIO_MAPPING_2, RF69_CLK_OUT_OFF, 2, 0); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set packet configuration and disable encryption state = _mod->SPIsetRegValue(RF69_REG_PACKET_CONFIG_1, RF69_PACKET_FORMAT_VARIABLE | RF69_DC_FREE_NONE | RF69_CRC_ON | RF69_CRC_AUTOCLEAR_ON | RF69_ADDRESS_FILTERING_OFF, 7, 1); state |= _mod->SPIsetRegValue(RF69_REG_PACKET_CONFIG_2, RF69_INTER_PACKET_RX_DELAY, 7, 4); state |= _mod->SPIsetRegValue(RF69_REG_PACKET_CONFIG_2, RF69_AUTO_RX_RESTART_ON | RF69_AES_OFF, 1, 0); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set payload length state = _mod->SPIsetRegValue(RF69_REG_PAYLOAD_LENGTH, RF69_PAYLOAD_LENGTH, 7, 0); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set FIFO threshold state = _mod->SPIsetRegValue(RF69_REG_FIFO_THRESH, RF69_TX_START_CONDITION_FIFO_NOT_EMPTY | RF69_FIFO_THRESHOLD, 7, 0); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set Rx timeouts state = _mod->SPIsetRegValue(RF69_REG_RX_TIMEOUT_1, RF69_TIMEOUT_RX_START, 7, 0); state = _mod->SPIsetRegValue(RF69_REG_RX_TIMEOUT_2, RF69_TIMEOUT_RSSI_THRESH, 7, 0); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // enable improved fading margin state = _mod->SPIsetRegValue(RF69_REG_TEST_DAGC, RF69_CONTINUOUS_DAGC_LOW_BETA_OFF, 7, 0); - if(state != ERR_NONE) { - return(state); - } return(ERR_NONE); } @@ -762,15 +767,11 @@ int16_t RF69::setPacketMode(uint8_t mode, uint8_t len) { // set to fixed packet length int16_t state = _mod->SPIsetRegValue(RF69_REG_PACKET_CONFIG_1, mode, 7, 7); - if (state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set length to register state = _mod->SPIsetRegValue(RF69_REG_PAYLOAD_LENGTH, len); - if (state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // update the cached value _packetLengthConfig = mode; diff --git a/src/modules/RF69/RF69.h b/src/modules/RF69/RF69.h index fbe8abb2..6c5333fe 100644 --- a/src/modules/RF69/RF69.h +++ b/src/modules/RF69/RF69.h @@ -7,9 +7,10 @@ #include "../../protocols/PhysicalLayer/PhysicalLayer.h" // RF69 physical layer properties +#define RF69_FREQUENCY_STEP_SIZE 61.03515625 +#define RF69_MAX_PACKET_LENGTH 64 #define RF69_CRYSTAL_FREQ 32.0 #define RF69_DIV_EXPONENT 19 -#define RF69_MAX_PACKET_LENGTH 64 // RF69 register map #define RF69_REG_FIFO 0x00 @@ -454,15 +455,20 @@ class RF69: public PhysicalLayer { \param br Bit rate to be used in kbps. Defaults to 48.0 kbps. - \param rxBw Receiver bandwidth in kHz. Defaults to 125.0 kHz. - \param freqDev Frequency deviation from carrier frequency in kHz Defaults to 50.0 kHz. + \param rxBw Receiver bandwidth in kHz. Defaults to 125.0 kHz. + \param power Output power in dBm. Defaults to 13 dBm. \returns \ref status_codes */ - int16_t begin(float freq = 434.0, float br = 48.0, float rxBw = 125.0, float freqDev = 50.0, int8_t power = 13); + int16_t begin(float freq = 434.0, float br = 48.0, float freqDev = 50.0, float rxBw = 125.0, int8_t power = 13); + + /*! + \brief Reset method. Will reset the chip to the default state using RST pin. + */ + void reset(); /*! \brief Blocking binary transmit method. @@ -557,6 +563,11 @@ class RF69: public PhysicalLayer { */ void setDio0Action(void (*func)(void)); + /*! + \brief Clears interrupt service routine to call when DIO0 activates. + */ + void clearDio0Action(); + /*! \brief Sets interrupt service routine to call when DIO1 activates. @@ -564,6 +575,11 @@ class RF69: public PhysicalLayer { */ void setDio1Action(void (*func)(void)); + /*! + \brief Clears interrupt service routine to call when DIO1 activates. + */ + void clearDio1Action(); + /*! \brief Interrupt-driven binary transmit method. Overloads for string-based transmissions are implemented in PhysicalLayer. @@ -581,11 +597,9 @@ class RF69: public PhysicalLayer { /*! \brief Interrupt-driven receive method. GDO0 will be activated when full packet is received. - \param timeout Enable module-driven timeout. Set to false for listen mode. - \returns \ref status_codes */ - int16_t startReceive(bool timeout = false); + int16_t startReceive(); /*! \brief Reads data received after calling startReceive method. @@ -758,6 +772,25 @@ class RF69: public PhysicalLayer { */ int16_t setPromiscuousMode(bool promiscuous = true); + /*! + \brief Sets Gaussian filter bandwidth-time product that will be used for data shaping. + Allowed values are 0.3, 0.5 or 1.0. Set to 0 to disable data shaping. + + \param sh Gaussian shaping bandwidth-time product that will be used for data shaping + + \returns \ref status_codes + */ + int16_t setDataShaping(float sh); + + /*! + \brief Sets transmission encoding. + + \param encoding Encoding to be used. Set to 0 for NRZ, 1 for Manchester and 2 for whitening. + + \returns \ref status_codes + */ + int16_t setEncoding(uint8_t encoding); + #ifndef RADIOLIB_GODMODE protected: #endif diff --git a/src/modules/RFM9x/RFM95.cpp b/src/modules/RFM9x/RFM95.cpp index 812d928c..72df63c4 100644 --- a/src/modules/RFM9x/RFM95.cpp +++ b/src/modules/RFM9x/RFM95.cpp @@ -7,47 +7,30 @@ RFM95::RFM95(Module* mod) : SX1278(mod) { int16_t RFM95::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint8_t currentLimit, uint16_t preambleLength, uint8_t gain) { // execute common part int16_t state = SX127x::begin(RFM95_CHIP_VERSION, syncWord, currentLimit, preambleLength); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + // configure settings not accessible by API state = config(); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + // configure publicly accessible settings state = setFrequency(freq); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setBandwidth(bw); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setSpreadingFactor(sf); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setCodingRate(cr); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setOutputPower(power); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setGain(gain); - if(state != ERR_NONE) { - return(state); - } - + return(state); } @@ -56,7 +39,7 @@ int16_t RFM95::setFrequency(float freq) { if((freq < 868.0) || (freq > 915.0)) { return(ERR_INVALID_FREQUENCY); } - + // set frequency return(SX127x::setFrequencyRaw(freq)); } diff --git a/src/modules/RFM9x/RFM96.cpp b/src/modules/RFM9x/RFM96.cpp index e346ee7c..1e045536 100644 --- a/src/modules/RFM9x/RFM96.cpp +++ b/src/modules/RFM9x/RFM96.cpp @@ -7,47 +7,30 @@ RFM96::RFM96(Module* mod) : SX1278(mod) { int16_t RFM96::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint8_t currentLimit, uint16_t preambleLength, uint8_t gain) { // execute common part int16_t state = SX127x::begin(RFM9X_CHIP_VERSION, syncWord, currentLimit, preambleLength); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + // configure settings not accessible by API state = config(); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + // configure publicly accessible settings state = setFrequency(freq); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setBandwidth(bw); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setSpreadingFactor(sf); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setCodingRate(cr); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setOutputPower(power); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setGain(gain); - if(state != ERR_NONE) { - return(state); - } - + return(state); } @@ -56,7 +39,7 @@ int16_t RFM96::setFrequency(float freq) { if((freq < 433.0) || (freq > 470.0)) { return(ERR_INVALID_FREQUENCY); } - + // set frequency return(SX127x::setFrequencyRaw(freq)); } diff --git a/src/modules/RFM9x/RFM97.cpp b/src/modules/RFM9x/RFM97.cpp index 8299d654..102fecf9 100644 --- a/src/modules/RFM9x/RFM97.cpp +++ b/src/modules/RFM9x/RFM97.cpp @@ -11,7 +11,7 @@ int16_t RFM97::setSpreadingFactor(uint8_t sf) { } uint8_t newSpreadingFactor; - + // check allowed spreading factor values switch(sf) { case 6: @@ -29,7 +29,7 @@ int16_t RFM97::setSpreadingFactor(uint8_t sf) { default: return(ERR_INVALID_SPREADING_FACTOR); } - + // set spreading factor and if successful, save the new setting int16_t state = SX1278::setSpreadingFactorRaw(newSpreadingFactor); if(state == ERR_NONE) { diff --git a/src/modules/SX1231/SX1231.cpp b/src/modules/SX1231/SX1231.cpp index 7e1e93d4..b7412bcb 100644 --- a/src/modules/SX1231/SX1231.cpp +++ b/src/modules/SX1231/SX1231.cpp @@ -6,7 +6,9 @@ SX1231::SX1231(Module* mod) : RF69(mod) { int16_t SX1231::begin(float freq, float br, float rxBw, float freqDev, int8_t power) { // set module properties - _mod->init(RADIOLIB_USE_SPI, RADIOLIB_INT_BOTH); + _mod->init(RADIOLIB_USE_SPI); + Module::pinMode(_mod->getIrq(), INPUT); + Module::pinMode(_mod->getRst(), OUTPUT); // try to find the SX1231 chip uint8_t i = 0; @@ -35,7 +37,7 @@ int16_t SX1231::begin(float freq, float br, float rxBw, float freqDev, int8_t po if(!flagFound) { RADIOLIB_DEBUG_PRINTLN(F("No SX1231 found!")); - SPI.end(); + _mod->term(); return(ERR_CHIP_NOT_FOUND); } else { RADIOLIB_DEBUG_PRINTLN(F("Found SX1231!")); @@ -43,47 +45,33 @@ int16_t SX1231::begin(float freq, float br, float rxBw, float freqDev, int8_t po // configure settings not accessible by API int16_t state = config(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure publicly accessible settings state = setFrequency(freq); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure bitrate _rxBw = 125.0; state = setBitRate(br); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure default RX bandwidth state = setRxBandwidth(rxBw); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure default frequency deviation state = setFrequencyDeviation(freqDev); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure default TX output power state = setOutputPower(power); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // default sync word values 0x2D01 is the same as the default in LowPowerLab RFM69 library uint8_t syncWord[] = {0x2D, 0x01}; state = setSyncWord(syncWord, 2); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set default packet length mode state = variablePacketLengthMode(); @@ -95,15 +83,11 @@ int16_t SX1231::begin(float freq, float br, float rxBw, float freqDev, int8_t po if(_chipRevision == SX1231_CHIP_REVISION_2_A) { // modify default OOK threshold value state = _mod->SPIsetRegValue(SX1231_REG_TEST_OOK, SX1231_OOK_DELTA_THRESHOLD); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // enable OCP with 95 mA limit state = _mod->SPIsetRegValue(RF69_REG_OCP, RF69_OCP_ON | RF69_OCP_TRIM, 4, 0); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); } return(ERR_NONE); diff --git a/src/modules/SX126x/SX1261.cpp b/src/modules/SX126x/SX1261.cpp index 06e37d73..dcf34fac 100644 --- a/src/modules/SX126x/SX1261.cpp +++ b/src/modules/SX126x/SX1261.cpp @@ -1,11 +1,10 @@ #include "SX1261.h" -SX1261::SX1261(Module* mod) - : SX1262(mod) { +SX1261::SX1261(Module* mod): SX1262(mod) { } -int16_t SX1261::setOutputPower(int8_t power) { +int16_t SX1261::setOutputPower(int8_t power) { // check allowed power range if (!((power >= -17) && (power <= 14))) { return(ERR_INVALID_OUTPUT_POWER); @@ -14,37 +13,17 @@ int16_t SX1261::setOutputPower(int8_t power) { // get current OCP configuration uint8_t ocp = 0; int16_t state = readRegister(SX126X_REG_OCP_CONFIGURATION, &ocp, 1); - if (state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); - state = setOptimalLowPowerPaConfig(&power); - if (state != ERR_NONE) { - return(state); - } + // set PA config + state = SX126x::setPaConfig(0x04, SX126X_PA_CONFIG_SX1261, 0x00); + RADIOLIB_ASSERT(state); // set output power // TODO power ramp time configuration state = SX126x::setTxParams(power); - if (state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // restore OCP configuration - return writeRegister(SX126X_REG_OCP_CONFIGURATION, &ocp, 1); -} - -int16_t SX1261::setOptimalLowPowerPaConfig(int8_t* inOutPower) -{ - int16_t state; - if (*inOutPower > 10) { - state = SX126x::setPaConfig(0x04, SX126X_PA_CONFIG_SX1261, 0x00); - } - else { - state = SX126x::setPaConfig(0x01, SX126X_PA_CONFIG_SX1261, 0x00); - // changing the PaConfig means output power is now scaled so we get 3 dB less than requested. - // see datasheet table 13-21 and comments in setOptimalHiPowerPaConfig. - *inOutPower -= 3; - } - return state; + return(writeRegister(SX126X_REG_OCP_CONFIGURATION, &ocp, 1)); } diff --git a/src/modules/SX126x/SX1261.h b/src/modules/SX126x/SX1261.h index 3331c1bc..2dca7ed1 100644 --- a/src/modules/SX126x/SX1261.h +++ b/src/modules/SX126x/SX1261.h @@ -35,7 +35,7 @@ class SX1261 : public SX1262 { #ifndef RADIOLIB_GODMODE private: #endif - int16_t setOptimalLowPowerPaConfig(int8_t* inOutPower); + }; diff --git a/src/modules/SX126x/SX1262.cpp b/src/modules/SX126x/SX1262.cpp index 345c7db6..3a9d211a 100644 --- a/src/modules/SX126x/SX1262.cpp +++ b/src/modules/SX126x/SX1262.cpp @@ -4,54 +4,36 @@ SX1262::SX1262(Module* mod) : SX126x(mod) { } -int16_t SX1262::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint16_t syncWord, int8_t power, float currentLimit, uint16_t preambleLength) { +int16_t SX1262::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, float currentLimit, uint16_t preambleLength, float tcxoVoltage, bool useRegulatorLDO) { // execute common part - int16_t state = SX126x::begin(bw, sf, cr, syncWord, currentLimit, preambleLength); - if(state != ERR_NONE) { - return(state); - } + int16_t state = SX126x::begin(bw, sf, cr, syncWord, currentLimit, preambleLength, tcxoVoltage, useRegulatorLDO); + RADIOLIB_ASSERT(state); // configure publicly accessible settings state = setFrequency(freq); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setOutputPower(power); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = SX126x::fixPaClamping(); - if (state != ERR_NONE) { - return state; - } return(state); } -int16_t SX1262::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t power, float currentLimit, uint16_t preambleLength, float dataShaping) { +int16_t SX1262::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t power, float currentLimit, uint16_t preambleLength, float dataShaping, float tcxoVoltage, bool useRegulatorLDO) { // execute common part - int16_t state = SX126x::beginFSK(br, freqDev, rxBw, currentLimit, preambleLength, dataShaping); - if(state != ERR_NONE) { - return(state); - } + int16_t state = SX126x::beginFSK(br, freqDev, rxBw, currentLimit, preambleLength, dataShaping, tcxoVoltage, useRegulatorLDO); + RADIOLIB_ASSERT(state); // configure publicly accessible settings state = setFrequency(freq); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setOutputPower(power); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = SX126x::fixPaClamping(); - if (state != ERR_NONE) { - return state; - } return(state); } @@ -84,9 +66,7 @@ int16_t SX1262::setFrequency(float freq, bool calibrate) { data[1] = SX126X_CAL_IMG_430_MHZ_2; } state = SX126x::calibrateImage(data); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); } // set frequency @@ -102,25 +82,17 @@ int16_t SX1262::setOutputPower(int8_t power) { // get current OCP configuration uint8_t ocp = 0; int16_t state = readRegister(SX126X_REG_OCP_CONFIGURATION, &ocp, 1); - if (state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); - // this function sets the optimal PA settings - // and adjusts power based on the PA settings chosen - // so that output power matches requested power. - state = SX126x::setOptimalHiPowerPaConfig(&power); - if (state != ERR_NONE) { - return(state); - } + // set PA config + state = SX126x::setPaConfig(0x04, SX126X_PA_CONFIG_SX1262); + RADIOLIB_ASSERT(state); // set output power // TODO power ramp time configuration state = SX126x::setTxParams(power); - if (state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // restore OCP configuration - return writeRegister(SX126X_REG_OCP_CONFIGURATION, &ocp, 1); + return(writeRegister(SX126X_REG_OCP_CONFIGURATION, &ocp, 1)); } diff --git a/src/modules/SX126x/SX1262.h b/src/modules/SX126x/SX1262.h index de0b5888..e33f568b 100644 --- a/src/modules/SX126x/SX1262.h +++ b/src/modules/SX126x/SX1262.h @@ -5,6 +5,9 @@ #include "../../Module.h" #include "SX126x.h" +//SX126X_CMD_SET_PA_CONFIG +#define SX126X_PA_CONFIG_SX1262 0x00 + /*! \class SX1262 @@ -32,7 +35,7 @@ class SX1262: public SX126x { \param cr LoRa coding rate denominator. Defaults to 7 (coding rate 4/7). - \param syncWord 2-byte LoRa sync word. Defaults to SX126X_SYNC_WORD_PRIVATE (0x1424). + \param syncWord 2-byte LoRa sync word. Defaults to SX126X_SYNC_WORD_PRIVATE (0x12). \param power Output power in dBm. Defaults to 14 dBm. @@ -40,9 +43,11 @@ class SX1262: public SX126x { \param preambleLength LoRa preamble length in symbols.Defaults to 8 symbols. + \param tcxoVoltage TCXO reference voltage to be set on DIO3. Defaults to 1.6 V, set to 0 to skip. + \returns \ref status_codes */ - int16_t begin(float freq = 434.0, float bw = 125.0, uint8_t sf = 9, uint8_t cr = 7, uint16_t syncWord = SX126X_SYNC_WORD_PRIVATE, int8_t power = 14, float currentLimit = 60.0, uint16_t preambleLength = 8); + int16_t begin(float freq = 434.0, float bw = 125.0, uint8_t sf = 9, uint8_t cr = 7, uint8_t syncWord = SX126X_SYNC_WORD_PRIVATE, int8_t power = 14, float currentLimit = 60.0, uint16_t preambleLength = 8, float tcxoVoltage = 1.6, bool useRegulatorLDO = false); /*! \brief Initialization method for FSK modem. @@ -63,9 +68,11 @@ class SX1262: public SX126x { \param dataShaping Time-bandwidth product of the Gaussian filter to be used for shaping. Defaults to 0.5. + \param tcxoVoltage TCXO reference voltage to be set on DIO3. Defaults to 1.6 V, set to 0 to skip. + \returns \ref status_codes */ - int16_t beginFSK(float freq = 434.0, float br = 48.0, float freqDev = 50.0, float rxBw = 156.2, int8_t power = 14, float currentLimit = 60.0, uint16_t preambleLength = 16, float dataShaping = 0.5); + int16_t beginFSK(float freq = 434.0, float br = 48.0, float freqDev = 50.0, float rxBw = 156.2, int8_t power = 14, float currentLimit = 60.0, uint16_t preambleLength = 16, float dataShaping = 0.5, float tcxoVoltage = 1.6, bool useRegulatorLDO = false); // configuration methods diff --git a/src/modules/SX126x/SX1268.cpp b/src/modules/SX126x/SX1268.cpp index 44a671a7..b6daa489 100644 --- a/src/modules/SX126x/SX1268.cpp +++ b/src/modules/SX126x/SX1268.cpp @@ -4,53 +4,35 @@ SX1268::SX1268(Module* mod) : SX126x(mod) { } -int16_t SX1268::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint16_t syncWord, int8_t power, float currentLimit, uint16_t preambleLength) { +int16_t SX1268::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, float currentLimit, uint16_t preambleLength, float tcxoVoltage, bool useRegulatorLDO) { // execute common part - int16_t state = SX126x::begin(bw, sf, cr, syncWord, currentLimit, preambleLength); - if(state != ERR_NONE) { - return(state); - } + int16_t state = SX126x::begin(bw, sf, cr, syncWord, currentLimit, preambleLength, tcxoVoltage, useRegulatorLDO); + RADIOLIB_ASSERT(state); // configure publicly accessible settings state = setFrequency(freq); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setOutputPower(power); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = SX126x::fixPaClamping(); - if (state != ERR_NONE) { - return state; - } return(state); } -int16_t SX1268::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t power, float currentLimit, uint16_t preambleLength, float dataShaping) { +int16_t SX1268::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t power, float currentLimit, uint16_t preambleLength, float dataShaping, float tcxoVoltage, bool useRegulatorLDO) { // execute common part - int16_t state = SX126x::beginFSK(br, freqDev, rxBw, currentLimit, preambleLength, dataShaping); - if(state != ERR_NONE) { - return(state); - } + int16_t state = SX126x::beginFSK(br, freqDev, rxBw, currentLimit, preambleLength, dataShaping, tcxoVoltage, useRegulatorLDO); + RADIOLIB_ASSERT(state); // configure publicly accessible settings state = setFrequency(freq); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setOutputPower(power); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = SX126x::fixPaClamping(); - if (state != ERR_NONE) { - return state; - } return(state); } @@ -77,9 +59,7 @@ int16_t SX1268::setFrequency(float freq, bool calibrate) { data[1] = SX126X_CAL_IMG_430_MHZ_2; } state = SX126x::calibrateImage(data); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); } // set frequency @@ -95,23 +75,17 @@ int16_t SX1268::setOutputPower(int8_t power) { // get current OCP configuration uint8_t ocp = 0; int16_t state = readRegister(SX126X_REG_OCP_CONFIGURATION, &ocp, 1); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); - // enable optimal PA - this changes the value of power. - state = SX126x::setOptimalHiPowerPaConfig(&power); - if (state != ERR_NONE) { - return(state); - } + // set PA config + state = SX126x::setPaConfig(0x04, SX126X_PA_CONFIG_SX1268); + RADIOLIB_ASSERT(state); // set output power // TODO power ramp time configuration state = SX126x::setTxParams(power); - if (state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // restore OCP configuration - return writeRegister(SX126X_REG_OCP_CONFIGURATION, &ocp, 1); + return(writeRegister(SX126X_REG_OCP_CONFIGURATION, &ocp, 1)); } diff --git a/src/modules/SX126x/SX1268.h b/src/modules/SX126x/SX1268.h index 8f989498..5b203b72 100644 --- a/src/modules/SX126x/SX1268.h +++ b/src/modules/SX126x/SX1268.h @@ -35,17 +35,19 @@ class SX1268: public SX126x { \param cr LoRa coding rate denominator. Defaults to 7 (coding rate 4/7). - \param syncWord 2-byte LoRa sync word. Defaults to SX126X_SYNC_WORD_PRIVATE (0x1424). + \param syncWord 2-byte LoRa sync word. Defaults to SX126X_SYNC_WORD_PRIVATE (0x12). \param power Output power in dBm. Defaults to 14 dBm. \param currentLimit Current protection limit in mA. Defaults to 60.0 mA. - \param preambleLength LoRa preamble length in symbols.Defaults to 8 symbols. + \param preambleLength LoRa preamble length in symbols. Defaults to 8 symbols. + + \param tcxoVoltage TCXO reference voltage to be set on DIO3. Defaults to 1.6 V, set to 0 to skip. \returns \ref status_codes */ - int16_t begin(float freq = 434.0, float bw = 125.0, uint8_t sf = 9, uint8_t cr = 7, uint16_t syncWord = SX126X_SYNC_WORD_PRIVATE, int8_t power = 14, float currentLimit = 60.0, uint16_t preambleLength = 8); + int16_t begin(float freq = 434.0, float bw = 125.0, uint8_t sf = 9, uint8_t cr = 7, uint8_t syncWord = SX126X_SYNC_WORD_PRIVATE, int8_t power = 14, float currentLimit = 60.0, uint16_t preambleLength = 8, float tcxoVoltage = 1.6, bool useRegulatorLDO = false); /*! \brief Initialization method for FSK modem. @@ -66,14 +68,16 @@ class SX1268: public SX126x { \param dataShaping Time-bandwidth product of the Gaussian filter to be used for shaping. Defaults to 0.5. + \param tcxoVoltage TCXO reference voltage to be set on DIO3. Defaults to 1.6 V, set to 0 to skip. + \returns \ref status_codes */ - int16_t beginFSK(float freq = 434.0, float br = 48.0, float freqDev = 50.0, float rxBw = 156.2, int8_t power = 14, float currentLimit = 60.0, uint16_t preambleLength = 16, float dataShaping = 0.5); + int16_t beginFSK(float freq = 434.0, float br = 48.0, float freqDev = 50.0, float rxBw = 156.2, int8_t power = 14, float currentLimit = 60.0, uint16_t preambleLength = 16, float dataShaping = 0.5, float tcxoVoltage = 1.6, bool useRegulatorLDO = false); // configuration methods /*! - \brief Sets carrier frequency. Allowed values are in range from 150.0 to 960.0 MHz. + \brief Sets carrier frequency. Allowed values are in range from 410.0 to 810.0 MHz. \param freq Carrier frequency to be set in MHz. @@ -84,7 +88,7 @@ class SX1268: public SX126x { int16_t setFrequency(float freq, bool calibrate = true); /*! - \brief Sets output power. Allowed values are in range from -17 to 22 dBm. + \brief Sets output power. Allowed values are in range from -9 to 22 dBm. \param power Output power to be set in dBm. diff --git a/src/modules/SX126x/SX126x.cpp b/src/modules/SX126x/SX126x.cpp index 3df6d8d0..b089f247 100644 --- a/src/modules/SX126x/SX126x.cpp +++ b/src/modules/SX126x/SX126x.cpp @@ -1,13 +1,14 @@ #include "SX126x.h" -SX126x::SX126x(Module* mod) : PhysicalLayer(SX126X_CRYSTAL_FREQ, SX126X_DIV_EXPONENT, SX126X_MAX_PACKET_LENGTH) { +SX126x::SX126x(Module* mod) : PhysicalLayer(SX126X_FREQUENCY_STEP_SIZE, SX126X_MAX_PACKET_LENGTH) { _mod = mod; } -int16_t SX126x::begin(float bw, uint8_t sf, uint8_t cr, uint16_t syncWord, float currentLimit, uint16_t preambleLength) { +int16_t SX126x::begin(float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, float currentLimit, uint16_t preambleLength, float tcxoVoltage, bool useRegulatorLDO) { // set module properties - _mod->init(RADIOLIB_USE_SPI, RADIOLIB_INT_BOTH); - pinMode(_mod->getRx(), INPUT); + _mod->init(RADIOLIB_USE_SPI); + Module::pinMode(_mod->getIrq(), INPUT); + Module::pinMode(_mod->getGpio(), INPUT); // BW in kHz and SF are required in order to calculate LDRO for setModulationParams _bwKhz = bw; @@ -19,60 +20,64 @@ int16_t SX126x::begin(float bw, uint8_t sf, uint8_t cr, uint16_t syncWord, float _ldro = 0x00; _crcType = SX126X_LORA_CRC_ON; _preambleLength = preambleLength; + _tcxoDelay = 0; + _headerType = SX126X_LORA_HEADER_EXPLICIT; + _implicitLen = 0xFF; + + // reset the module and verify startup + int16_t state = reset(); + RADIOLIB_ASSERT(state); // set mode to standby - int16_t state = standby(); - if(state != ERR_NONE) { - return(state); - } + state = standby(); + RADIOLIB_ASSERT(state); // configure settings not accessible by API state = config(SX126X_PACKET_TYPE_LORA); - if(state != ERR_NONE) { - return(state); + RADIOLIB_ASSERT(state); + + // set TCXO control, if requested + if(tcxoVoltage > 0.0) { + state = setTCXO(tcxoVoltage); + RADIOLIB_ASSERT(state); } // configure publicly accessible settings state = setSpreadingFactor(sf); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setBandwidth(bw); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setCodingRate(cr); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setSyncWord(syncWord); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setCurrentLimit(currentLimit); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setPreambleLength(preambleLength); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set publicly accessible settings that are not a part of begin method - state = setDio2AsRfSwitch(false); + state = setDio2AsRfSwitch(true); + RADIOLIB_ASSERT(state); + + if (useRegulatorLDO) { + state = setRegulatorLDO(); + } else { + state = setRegulatorDCDC(); + } return(state); } -int16_t SX126x::beginFSK(float br, float freqDev, float rxBw, float currentLimit, uint16_t preambleLength, float dataShaping) { +int16_t SX126x::beginFSK(float br, float freqDev, float rxBw, float currentLimit, uint16_t preambleLength, float dataShaping, float tcxoVoltage, bool useRegulatorLDO) { // set module properties - _mod->init(RADIOLIB_USE_SPI, RADIOLIB_INT_BOTH); - pinMode(_mod->getRx(), INPUT); + _mod->init(RADIOLIB_USE_SPI); + Module::pinMode(_mod->getIrq(), INPUT); // initialize configuration variables (will be overwritten during public settings configuration) _br = 21333; // 48.0 kbps @@ -84,72 +89,103 @@ int16_t SX126x::beginFSK(float br, float freqDev, float rxBw, float currentLimit _preambleLengthFSK = preambleLength; _addrComp = SX126X_GFSK_ADDRESS_FILT_OFF; + // reset the module and verify startup + int16_t state = reset(); + RADIOLIB_ASSERT(state); + // set mode to standby - int16_t state = standby(); - if(state != ERR_NONE) { - return(state); - } + state = standby(); + RADIOLIB_ASSERT(state); // configure settings not accessible by API state = config(SX126X_PACKET_TYPE_GFSK); - if(state != ERR_NONE) { - return(state); + RADIOLIB_ASSERT(state); + + // set TCXO control, if requested + if(tcxoVoltage > 0.0) { + state = setTCXO(tcxoVoltage); + RADIOLIB_ASSERT(state); } // configure publicly accessible settings state = setBitRate(br); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setFrequencyDeviation(freqDev); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setRxBandwidth(rxBw); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setCurrentLimit(currentLimit); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setDataShaping(dataShaping); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setPreambleLength(preambleLength); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set publicly accessible settings that are not a part of begin method uint8_t sync[] = {0x2D, 0x01}; state = setSyncWord(sync, 2); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setWhitening(true, 0x0100); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); + + state = variablePacketLengthMode(SX126X_MAX_PACKET_LENGTH); + RADIOLIB_ASSERT(state); state = setDio2AsRfSwitch(false); + RADIOLIB_ASSERT(state); + + if (useRegulatorLDO) { + state = setRegulatorLDO(); + } else { + state = setRegulatorDCDC(); + } return(state); } +int16_t SX126x::reset(bool verify) { + // run the reset sequence + Module::pinMode(_mod->getRst(), OUTPUT); + Module::digitalWrite(_mod->getRst(), LOW); + delayMicroseconds(150); + Module::digitalWrite(_mod->getRst(), HIGH); + + // return immediately when verification is disabled + if(!verify) { + return(ERR_NONE); + } + + // set mode to standby - SX126x often refuses first few commands after reset + uint32_t start = millis(); + while(true) { + // try to set mode to standby + int16_t state = standby(); + if(state == ERR_NONE) { + // standby command successful + return(ERR_NONE); + } + + // standby command failed, check timeout and try again + if(millis() - start >= 3000) { + // timed out, possibly incorrect wiring + return(state); + } + + // wait a bit to not spam the module + delay(10); + } +} + int16_t SX126x::transmit(uint8_t* data, size_t len, uint8_t addr) { // set mode to standby int16_t state = standby(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // check packet length if(len > SX126X_MAX_PACKET_LENGTH) { @@ -162,11 +198,11 @@ int16_t SX126x::transmit(uint8_t* data, size_t len, uint8_t addr) { uint8_t modem = getPacketType(); if(modem == SX126X_PACKET_TYPE_LORA) { // calculate timeout (150% of expected time-on-air) - timeout = (float)getTimeOnAir(len) * 1.5; + timeout = (getTimeOnAir(len) * 3) / 2; } else if(modem == SX126X_PACKET_TYPE_GFSK) { // calculate timeout (500% of expected time-on-air) - timeout = (float)getTimeOnAir(len) * 5.0; + timeout = getTimeOnAir(len) * 5; } else { return(ERR_UNKNOWN); @@ -178,15 +214,14 @@ int16_t SX126x::transmit(uint8_t* data, size_t len, uint8_t addr) { // start transmission state = startTransmit(data, len, addr); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // wait for packet transmission or timeout uint32_t start = micros(); - while(!digitalRead(_mod->getInt0())) { + while(!digitalRead(_mod->getIrq())) { if(micros() - start > timeout) { clearIrqStatus(); + standby(); return(ERR_TX_TIMEOUT); } } @@ -197,9 +232,7 @@ int16_t SX126x::transmit(uint8_t* data, size_t len, uint8_t addr) { // clear interrupt flags state = clearIrqStatus(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set mode to standby to disable transmitter state = standby(); @@ -210,9 +243,7 @@ int16_t SX126x::transmit(uint8_t* data, size_t len, uint8_t addr) { int16_t SX126x::receive(uint8_t* data, size_t len) { // set mode to standby int16_t state = standby(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); uint32_t timeout = 0; @@ -222,7 +253,6 @@ int16_t SX126x::receive(uint8_t* data, size_t len) { // calculate timeout (100 LoRa symbols, the default for SX127x series) float symbolLength = (float)(uint32_t(1) << _sf) / (float)_bwKhz; timeout = (uint32_t)(symbolLength * 100.0 * 1000.0); - } else if(modem == SX126X_PACKET_TYPE_GFSK) { // calculate timeout (500 % of expected time-one-air) size_t maxLen = len; @@ -243,24 +273,23 @@ int16_t SX126x::receive(uint8_t* data, size_t len) { // start reception uint32_t timeoutValue = (uint32_t)((float)timeout / 15.625); state = startReceive(timeoutValue); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // wait for packet reception or timeout uint32_t start = micros(); - while(!digitalRead(_mod->getInt0())) { + while(!digitalRead(_mod->getIrq())) { if(micros() - start > timeout) { fixImplicitTimeout(); clearIrqStatus(); + standby(); return(ERR_RX_TIMEOUT); } } - // timeout fix is recommended after any reception with active timeout - state = fixImplicitTimeout(); - if(state != ERR_NONE) { - return(state); + // fix timeout in implicit LoRa mode + if(((_headerType == SX126X_LORA_HEADER_IMPLICIT) && (getPacketType() == SX126X_PACKET_TYPE_LORA))) { + state = fixImplicitTimeout(); + RADIOLIB_ASSERT(state); } // read the received data @@ -273,9 +302,7 @@ int16_t SX126x::transmitDirect(uint32_t frf) { if(frf != 0) { state = setRfFrequency(frf); } - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // start transmitting uint8_t data[] = {SX126X_CMD_NOP}; @@ -295,30 +322,24 @@ int16_t SX126x::scanChannel() { // set mode to standby int16_t state = standby(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set DIO pin mapping state = setDioIrqParams(SX126X_IRQ_CAD_DETECTED | SX126X_IRQ_CAD_DONE, SX126X_IRQ_CAD_DETECTED | SX126X_IRQ_CAD_DONE); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // clear interrupt flags state = clearIrqStatus(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set mode to CAD state = setCad(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // wait for channel activity detected or timeout - while(!digitalRead(_mod->getInt0())); + while(!digitalRead(_mod->getIrq())) { + yield(); + } // check CAD result uint16_t cadResult = getIrqStatus(); @@ -335,9 +356,12 @@ int16_t SX126x::scanChannel() { return(ERR_UNKNOWN); } -int16_t SX126x::sleep() { - uint8_t data[] = {SX126X_SLEEP_START_COLD | SX126X_SLEEP_RTC_OFF}; - int16_t state = SPIwriteCommand(SX126X_CMD_SET_SLEEP, data, 1, false); +int16_t SX126x::sleep(bool retainConfig) { + uint8_t sleepMode = SX126X_SLEEP_START_WARM | SX126X_SLEEP_RTC_OFF; + if(!retainConfig) { + sleepMode = SX126X_SLEEP_START_COLD | SX126X_SLEEP_RTC_OFF; + } + int16_t state = SPIwriteCommand(SX126X_CMD_SET_SLEEP, &sleepMode, 1, false); // wait for SX126x to safely enter sleep mode delayMicroseconds(500); @@ -355,11 +379,11 @@ int16_t SX126x::standby(uint8_t mode) { } void SX126x::setDio1Action(void (*func)(void)) { - attachInterrupt(digitalPinToInterrupt(_mod->getInt0()), func, RISING); + attachInterrupt(digitalPinToInterrupt(_mod->getIrq()), func, RISING); } -void SX126x::setDio2Action(void (*func)(void)) { - attachInterrupt(digitalPinToInterrupt(_mod->getInt1()), func, RISING); +void SX126x::clearDio1Action() { + detachInterrupt(digitalPinToInterrupt(_mod->getIrq())); } int16_t SX126x::startTransmit(uint8_t* data, size_t len, uint8_t addr) { @@ -380,76 +404,49 @@ int16_t SX126x::startTransmit(uint8_t* data, size_t len, uint8_t addr) { int16_t state = ERR_NONE; uint8_t modem = getPacketType(); if(modem == SX126X_PACKET_TYPE_LORA) { - state = setPacketParams(_preambleLength, _crcType, len); + state = setPacketParams(_preambleLength, _crcType, len, _headerType); } else if(modem == SX126X_PACKET_TYPE_GFSK) { state = setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp, _whitening, _packetType, len); } else { return(ERR_UNKNOWN); } - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set DIO mapping state = setDioIrqParams(SX126X_IRQ_TX_DONE | SX126X_IRQ_TIMEOUT, SX126X_IRQ_TX_DONE); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set buffer pointers state = setBufferBaseAddress(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // write packet to buffer state = writeBuffer(data, len); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // clear interrupt flags state = clearIrqStatus(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // fix sensitivity state = fixSensitivity(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // start transmission state = setTx(SX126X_TX_TIMEOUT_NONE); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // wait for BUSY to go low (= PA ramp up done) - while(digitalRead(_mod->getRx())); + while(digitalRead(_mod->getGpio())) { + yield(); + } return(state); } int16_t SX126x::startReceive(uint32_t timeout) { - // set DIO mapping - int16_t state = setDioIrqParams(SX126X_IRQ_RX_DONE | SX126X_IRQ_TIMEOUT, SX126X_IRQ_RX_DONE); - if(state != ERR_NONE) { - return(state); - } - - // set buffer pointers - state = setBufferBaseAddress(); - if(state != ERR_NONE) { - return(state); - } - - // clear interrupt flags - state = clearIrqStatus(); - if(state != ERR_NONE) { - return(state); - } + int16_t state = startReceiveCommon(); + RADIOLIB_ASSERT(state); // set mode to receive state = setRx(timeout); @@ -457,18 +454,105 @@ int16_t SX126x::startReceive(uint32_t timeout) { return(state); } +int16_t SX126x::startReceiveDutyCycle(uint32_t rxPeriod, uint32_t sleepPeriod) { + // datasheet claims time to go to sleep is ~500us, same to wake up, compensate for that with 1 ms + TCXO delay + uint32_t transitionTime = _tcxoDelay + 1000; + sleepPeriod -= transitionTime; + + // divide by 15.625 + uint32_t rxPeriodRaw = (rxPeriod * 8) / 125; + uint32_t sleepPeriodRaw = (sleepPeriod * 8) / 125; + + // check 24 bit limit and zero value (likely not intended) + if((rxPeriodRaw & 0xFF000000) || (rxPeriodRaw == 0)) { + return(ERR_INVALID_RX_PERIOD); + } + + // this check of the high byte also catches underflow when we subtracted transitionTime + if((sleepPeriodRaw & 0xFF000000) || (sleepPeriodRaw == 0)) { + return(ERR_INVALID_SLEEP_PERIOD); + } + + int16_t state = startReceiveCommon(); + RADIOLIB_ASSERT(state); + + uint8_t data[6] = {(uint8_t)((rxPeriodRaw >> 16) & 0xFF), (uint8_t)((rxPeriodRaw >> 8) & 0xFF), (uint8_t)(rxPeriodRaw & 0xFF), + (uint8_t)((sleepPeriodRaw >> 16) & 0xFF), (uint8_t)((sleepPeriodRaw >> 8) & 0xFF), (uint8_t)(sleepPeriodRaw & 0xFF)}; + return(SPIwriteCommand(SX126X_CMD_SET_RX_DUTY_CYCLE, data, 6)); +} + +int16_t SX126x::startReceiveDutyCycleAuto(uint16_t senderPreambleLength, uint16_t minSymbols) { + if(senderPreambleLength == 0) { + senderPreambleLength = _preambleLength; + } + + // worst case is that the sender starts transmiting when we're just less than minSymbols from going back to sleep. + // in this case, we don't catch minSymbols before going to sleep, + // so we must be awake for at least that long before the sender stops transmitting. + uint16_t sleepSymbols = senderPreambleLength - 2 * minSymbols; + + // if we're not to sleep at all, just use the standard startReceive. + if(2 * minSymbols > senderPreambleLength) { + return(startReceive()); + } + + uint32_t symbolLength = ((uint32_t)(10 * 1000) << _sf) / (10 * _bwKhz); + uint32_t sleepPeriod = symbolLength * sleepSymbols; + RADIOLIB_DEBUG_PRINT(F("Auto sleep period: ")); + RADIOLIB_DEBUG_PRINTLN(sleepPeriod); + + // when the unit detects a preamble, it starts a timer that will timeout if it doesn't receive a header in time. + // the duration is sleepPeriod + 2 * wakePeriod. + // The sleepPeriod doesn't take into account shutdown and startup time for the unit (~1ms) + // We need to ensure that the timout is longer than senderPreambleLength. + // So we must satisfy: wakePeriod > (preamblePeriod - (sleepPeriod - 1000)) / 2. (A) + // we also need to ensure the unit is awake to see at least minSymbols. (B) + uint32_t wakePeriod = max( + (symbolLength * (senderPreambleLength + 1) - (sleepPeriod - 1000)) / 2, // (A) + symbolLength * (minSymbols + 1)); //(B) + RADIOLIB_DEBUG_PRINT(F("Auto wake period: ")); + RADIOLIB_DEBUG_PRINTLN(wakePeriod); + + //If our sleep period is shorter than our transition time, just use the standard startReceive + if(sleepPeriod < _tcxoDelay + 1016) { + return(startReceive()); + } + + return(startReceiveDutyCycle(wakePeriod, sleepPeriod)); +} + +int16_t SX126x::startReceiveCommon() { + // set DIO mapping + int16_t state = setDioIrqParams(SX126X_IRQ_RX_DONE | SX126X_IRQ_TIMEOUT | SX126X_IRQ_CRC_ERR | SX126X_IRQ_HEADER_ERR, SX126X_IRQ_RX_DONE); + RADIOLIB_ASSERT(state); + + // set buffer pointers + state = setBufferBaseAddress(); + RADIOLIB_ASSERT(state); + + // clear interrupt flags + state = clearIrqStatus(); + + // set implicit mode and expected len if applicable + if(_headerType == SX126X_LORA_HEADER_IMPLICIT && getPacketType() == SX126X_PACKET_TYPE_LORA) { + state = setPacketParams(_preambleLength, _crcType, _implicitLen, _headerType); + RADIOLIB_ASSERT(state); + } + + return(state); +} + int16_t SX126x::readData(uint8_t* data, size_t len) { // set mode to standby int16_t state = standby(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // check integrity CRC uint16_t irq = getIrqStatus(); + int16_t crcState = ERR_NONE; if((irq & SX126X_IRQ_CRC_ERR) || (irq & SX126X_IRQ_HEADER_ERR)) { clearIrqStatus(); - return(ERR_CRC_MISMATCH); + crcState = ERR_CRC_MISMATCH; } // get packet length @@ -479,13 +563,14 @@ int16_t SX126x::readData(uint8_t* data, size_t len) { // read packet data state = readBuffer(data, length); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // clear interrupt flags state = clearIrqStatus(); + // check if CRC failed - this is done after reading data to give user the option to keep them + RADIOLIB_ASSERT(crcState); + return(state); } @@ -495,31 +580,48 @@ int16_t SX126x::setBandwidth(float bw) { return(ERR_WRONG_MODEM); } - // check alowed bandwidth values - if(abs(bw - 7.8) <= 0.001) { - _bw = SX126X_LORA_BW_7_8; - } else if(abs(bw - 10.4) <= 0.001) { - _bw = SX126X_LORA_BW_10_4; - } else if(abs(bw - 15.6) <= 0.001) { - _bw = SX126X_LORA_BW_15_6; - } else if(abs(bw - 20.8) <= 0.001) { - _bw = SX126X_LORA_BW_20_8; - } else if(abs(bw - 31.25) <= 0.001) { - _bw = SX126X_LORA_BW_31_25; - } else if(abs(bw - 41.7) <= 0.001) { - _bw = SX126X_LORA_BW_41_7; - } else if(abs(bw - 62.5) <= 0.001) { - _bw = SX126X_LORA_BW_62_5; - } else if(abs(bw - 125.0) <= 0.001) { - _bw = SX126X_LORA_BW_125_0; - } else if(abs(bw - 250.0) <= 0.001) { - _bw = SX126X_LORA_BW_250_0; - } else if(abs(bw - 500.0) <= 0.001) { - _bw = SX126X_LORA_BW_500_0; - } else { + // ensure byte conversion doesn't overflow: + if(!((bw > 0) && (bw < 510))) { return(ERR_INVALID_BANDWIDTH); } + // check alowed bandwidth values + uint8_t bw_div2 = bw / 2 + 0.01; + switch (bw_div2) { + case 3: // 7.8: + _bw = SX126X_LORA_BW_7_8; + break; + case 5: // 10.4: + _bw = SX126X_LORA_BW_10_4; + break; + case 7: // 15.6: + _bw = SX126X_LORA_BW_15_6; + break; + case 10: // 20.8: + _bw = SX126X_LORA_BW_20_8; + break; + case 15: // 31.25: + _bw = SX126X_LORA_BW_31_25; + break; + case 20: // 41.7: + _bw = SX126X_LORA_BW_41_7; + break; + case 31: // 62.5: + _bw = SX126X_LORA_BW_62_5; + break; + case 62: // 125.0: + _bw = SX126X_LORA_BW_125_0; + break; + case 125: // 250.0 + _bw = SX126X_LORA_BW_250_0; + break; + case 250: // 500.0 + _bw = SX126X_LORA_BW_500_0; + break; + default: + return(ERR_INVALID_BANDWIDTH); + } + // update modulation parameters _bwKhz = bw; return(setModulationParams(_sf, _bw, _cr)); @@ -557,18 +659,23 @@ int16_t SX126x::setCodingRate(uint8_t cr) { return(setModulationParams(_sf, _bw, _cr)); } -int16_t SX126x::setSyncWord(uint16_t syncWord) { +int16_t SX126x::setSyncWord(uint8_t syncWord, uint8_t controlBits) { // check active modem if(getPacketType() != SX126X_PACKET_TYPE_LORA) { return(ERR_WRONG_MODEM); } // update register - uint8_t data[2] = {(uint8_t)((syncWord >> 8) & 0xFF), (uint8_t)(syncWord & 0xFF)}; + uint8_t data[2] = {(uint8_t)((syncWord & 0xF0) | ((controlBits & 0xF0) >> 4)), (uint8_t)(((syncWord & 0x0F) << 4) | (controlBits & 0x0F))}; return(writeRegister(SX126X_REG_LORA_SYNC_WORD_MSB, data, 2)); } int16_t SX126x::setCurrentLimit(float currentLimit) { + // check allowed range + if(!((currentLimit >= 0) && (currentLimit <= 140))) { + return(ERR_INVALID_CURRENT_LIMIT); + } + // calculate raw value uint8_t rawLimit = (uint8_t)(currentLimit / 2.5); @@ -576,11 +683,20 @@ int16_t SX126x::setCurrentLimit(float currentLimit) { return(writeRegister(SX126X_REG_OCP_CONFIGURATION, &rawLimit, 1)); } +float SX126x::getCurrentLimit() { + // get the raw value + uint8_t ocp = 0; + readRegister(SX126X_REG_OCP_CONFIGURATION, &ocp, 1); + + // return the actual value + return((float)ocp * 2.5); +} + int16_t SX126x::setPreambleLength(uint16_t preambleLength) { uint8_t modem = getPacketType(); if(modem == SX126X_PACKET_TYPE_LORA) { _preambleLength = preambleLength; - return(setPacketParams(_preambleLength, _crcType)); + return(setPacketParams(_preambleLength, _crcType, _implicitLen, _headerType)); } else if(modem == SX126X_PACKET_TYPE_GFSK) { _preambleLengthFSK = preambleLength; return(setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp, _whitening, _packetType)); @@ -739,9 +855,7 @@ int16_t SX126x::setSyncWord(uint8_t* syncWord, uint8_t len) { // write sync word int16_t state = writeRegister(SX126X_REG_SYNC_WORD_0, syncWord, len); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // update packet parameters _syncWordLength = len * 8; @@ -768,9 +882,7 @@ int16_t SX126x::setSyncBits(uint8_t *syncWord, uint8_t bitsLen) { // write sync word int16_t state = writeRegister(SX126X_REG_SYNC_WORD_0, syncWord, bytesLen); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // update packet parameters _syncWordLength = bitsLen; @@ -788,9 +900,7 @@ int16_t SX126x::setNodeAddress(uint8_t nodeAddr) { // enable address filtering (node only) _addrComp = SX126X_GFSK_ADDRESS_FILT_NODE; int16_t state = setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp, _whitening, _packetType); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set node address state = writeRegister(SX126X_REG_NODE_ADDRESS, &nodeAddr, 1); @@ -807,9 +917,7 @@ int16_t SX126x::setBroadcastAddress(uint8_t broadAddr) { // enable address filtering (node and broadcast) _addrComp = SX126X_GFSK_ADDRESS_FILT_NODE_BROADCAST; int16_t state = setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp, _whitening, _packetType); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set broadcast address state = writeRegister(SX126X_REG_BROADCAST_ADDRESS, &broadAddr, 1); @@ -857,16 +965,12 @@ int16_t SX126x::setCRC(uint8_t len, uint16_t initial, uint16_t polynomial, bool } int16_t state = setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp, _whitening, _packetType); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // write initial CRC value uint8_t data[2] = {(uint8_t)((initial >> 8) & 0xFF), (uint8_t)(initial & 0xFF)}; state = writeRegister(SX126X_REG_CRC_INITIAL_MSB, data, 2); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // write CRC polynomial value data[0] = (uint8_t)((polynomial >> 8) & 0xFF); @@ -885,7 +989,7 @@ int16_t SX126x::setCRC(uint8_t len, uint16_t initial, uint16_t polynomial, bool _crcType = SX126X_LORA_CRC_OFF; } - return(setPacketParams(_preambleLength, _crcType)); + return(setPacketParams(_preambleLength, _crcType, _implicitLen, _headerType)); } return(ERR_UNKNOWN); @@ -903,9 +1007,7 @@ int16_t SX126x::setWhitening(bool enabled, uint16_t initial) { _whitening = SX126X_GFSK_WHITENING_OFF; state = setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp, _whitening, _packetType); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); } else { // enable whitening _whitening = SX126X_GFSK_WHITENING_ON; @@ -916,21 +1018,15 @@ int16_t SX126x::setWhitening(bool enabled, uint16_t initial) { // first read the actual value and mask 7 MSB which we can not change // if different value is written in 7 MSB, the Rx won't even work (tested on HW) state = readRegister(SX126X_REG_WHITENING_INITIAL_MSB, data, 1); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); + data[0] = (data[0] & 0xFE) | (uint8_t)((initial >> 8) & 0x01); data[1] = (uint8_t)(initial & 0xFF); state = writeRegister(SX126X_REG_WHITENING_INITIAL_MSB, data, 2); - - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp, _whitening, _packetType); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); } return(state); } @@ -955,12 +1051,16 @@ float SX126x::getSNR() { // get last packet SNR from packet status uint32_t packetStatus = getPacketStatus(); uint8_t snrPkt = (packetStatus >> 8) & 0xFF; - return(snrPkt/4.0); + if(snrPkt < 128) { + return(snrPkt/4.0); + } else { + return((snrPkt - 256)/4.0); + } } size_t SX126x::getPacketLength(bool update) { (void)update; - uint8_t rxBufStatus[2]; + uint8_t rxBufStatus[2] = {0, 0}; SPIreadCommand(SX126X_CMD_GET_RX_BUFFER_STATUS, rxBufStatus, 2); return((size_t)rxBufStatus[0]); } @@ -974,26 +1074,60 @@ int16_t SX126x::variablePacketLengthMode(uint8_t maxLen) { } uint32_t SX126x::getTimeOnAir(size_t len) { + // everything is in microseconds to allow integer arithmetic + // some constants have .25, these are multiplied by 4, and have _x4 postfix to indicate that fact if(getPacketType() == SX126X_PACKET_TYPE_LORA) { - float symbolLength = (float)((uint32_t)(1) << _sf) / (float)_bwKhz; - float sfCoeff1 = 4.25; - float sfCoeff2 = 8.0; + uint32_t symbolLength_us = ((uint32_t)(1000 * 10) << _sf) / (_bwKhz * 10) ; + uint8_t sfCoeff1_x4 = 17; // (4.25 * 4) + uint8_t sfCoeff2 = 8; if(_sf == 5 || _sf == 6) { - sfCoeff1 = 6.25; - sfCoeff2 = 0.0; + sfCoeff1_x4 = 25; // 6.25 * 4 + sfCoeff2 = 0; } uint8_t sfDivisor = 4*_sf; - if(symbolLength >= 16.0) { + if(symbolLength_us >= 16000) { sfDivisor = 4*(_sf - 2); } - float nSymbol = _preambleLength + sfCoeff1 + 8 + ceil(max(8.0 * len + (_crcType * 16.0) - 4.0 * _sf + sfCoeff2 + 20.0, 0.0) / sfDivisor) * (_cr + 4); - return((uint32_t)(symbolLength * nSymbol * 1000.0)); + const int8_t bitsPerCrc = 16; + const int8_t N_symbol_header = _headerType == SX126X_LORA_HEADER_EXPLICIT ? 20 : 0; + + // numerator of equation in section 6.1.4 of SX1268 datasheet v1.1 (might not actually be bitcount, but it has len * 8) + int16_t bitCount = (int16_t) 8 * len + _crcType * bitsPerCrc - 4 * _sf + sfCoeff2 + N_symbol_header; + if(bitCount < 0) { + bitCount = 0; + } + // add (sfDivisor) - 1 to the numerator to give integer CEIL(...) + uint16_t nPreCodedSymbols = (bitCount + (sfDivisor - 1)) / (sfDivisor); + + // preamble can be 65k, therefore nSymbol_x4 needs to be 32 bit + uint32_t nSymbol_x4 = (_preambleLength + 8) * 4 + sfCoeff1_x4 + nPreCodedSymbols * (_cr + 4) * 4; + + return((symbolLength_us * nSymbol_x4) / 4); } else { - float brBps = ((float)(SX126X_CRYSTAL_FREQ) * 1000000.0 * 32.0) / (float)_br; - return((uint32_t)(((len * 8.0) / brBps) * 1000000.0)); + return((len * 8 * _br) / (SX126X_CRYSTAL_FREQ * 32)); } } +int16_t SX126x::implicitHeader(size_t len) { + return(setHeaderType(SX126X_LORA_HEADER_IMPLICIT, len)); +} + +int16_t SX126x::explicitHeader() { + return(setHeaderType(SX126X_LORA_HEADER_EXPLICIT)); +} + +int16_t SX126x::setRegulatorLDO() { + return(setRegulatorMode(SX126X_REGULATOR_LDO)); +} + +int16_t SX126x::setRegulatorDCDC() { + return(setRegulatorMode(SX126X_REGULATOR_DC_DC)); +} + +int16_t SX126x::setEncoding(uint8_t encoding) { + return(setWhitening(encoding)); +} + int16_t SX126x::setTCXO(float voltage, uint32_t delay) { // set mode to standby standby(); @@ -1031,10 +1165,10 @@ int16_t SX126x::setTCXO(float voltage, uint32_t delay) { data[2] = (uint8_t)((delayValue >> 8) & 0xFF); data[3] = (uint8_t)(delayValue & 0xFF); - // enable TCXO control on DIO3 - SPIwriteCommand(SX126X_CMD_SET_DIO3_AS_TCXO_CTRL, data, 4); + _tcxoDelay = delay; - return(ERR_NONE); + // enable TCXO control on DIO3 + return(SPIwriteCommand(SX126X_CMD_SET_DIO3_AS_TCXO_CTRL, data, 4)); } int16_t SX126x::setDio2AsRfSwitch(bool enable) { @@ -1099,7 +1233,7 @@ int16_t SX126x::setDioIrqParams(uint16_t irqMask, uint16_t dio1Mask, uint16_t di } uint16_t SX126x::getIrqStatus() { - uint8_t data[2]; + uint8_t data[2] = {0, 0};; SPIreadCommand(SX126X_CMD_GET_IRQ_STATUS, data, 2); return(((uint16_t)(data[0]) << 8) | data[1]); } @@ -1129,30 +1263,6 @@ int16_t SX126x::setTxParams(uint8_t power, uint8_t rampTime) { return(SPIwriteCommand(SX126X_CMD_SET_TX_PARAMS, data, 2)); } -int16_t SX126x::setOptimalHiPowerPaConfig(int8_t * inOutPower) { - // set PA config for optimal consumption as described in section 13-21 of SX1268 datasheet v1.1 - // the final column of Table 13-21 suggests that the value passed in SetTxParams - // is actually scaled depending on the parameters of setPaConfig - int16_t state; - if (*inOutPower >= 21) { - state = SX126x::setPaConfig(0x04, SX126X_PA_CONFIG_SX1262_8, SX126X_PA_CONFIG_HP_MAX/*0x07*/); - } - else if (*inOutPower >= 18) { - state = SX126x::setPaConfig(0x03, SX126X_PA_CONFIG_SX1262_8, 0x05); - // datasheet instructs request 22 dBm for 20 dBm actual output power - *inOutPower += 2; - } else if (*inOutPower >= 15) { - state = SX126x::setPaConfig(0x02, SX126X_PA_CONFIG_SX1262_8, 0x03); - // datasheet instructs request 22 dBm for 17 dBm actual output power - *inOutPower += 5; - } else { - state = SX126x::setPaConfig(0x02, SX126X_PA_CONFIG_SX1262_8, 0x02); - // datasheet instructs request 22 dBm for 14 dBm actual output power. - *inOutPower += 8; - } - return state; -} - int16_t SX126x::setPacketMode(uint8_t mode, uint8_t len) { // check active modem if(getPacketType() != SX126X_PACKET_TYPE_GFSK) { @@ -1161,15 +1271,30 @@ int16_t SX126x::setPacketMode(uint8_t mode, uint8_t len) { // set requested packet mode int16_t state = setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp, _whitening, mode, len); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // update cached value _packetType = mode; return(state); } +int16_t SX126x::setHeaderType(uint8_t headerType, size_t len) { + // check active modem + if(getPacketType() != SX126X_PACKET_TYPE_LORA) { + return(ERR_WRONG_MODEM); + } + + // set requested packet mode + int16_t state = setPacketParams(_preambleLength, _crcType, len, headerType); + RADIOLIB_ASSERT(state); + + // update cached value + _headerType = headerType; + _implicitLen = len; + + return(state); +} + int16_t SX126x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, uint8_t ldro) { // calculate symbol length and enable low data rate optimization, if needed if(ldro == 0xFF) { @@ -1198,7 +1323,8 @@ int16_t SX126x::setModulationParamsFSK(uint32_t br, uint8_t pulseShape, uint8_t } int16_t SX126x::setPacketParams(uint16_t preambleLength, uint8_t crcType, uint8_t payloadLength, uint8_t headerType, uint8_t invertIQ) { - fixInvertedIQ(invertIQ); + int16_t state = fixInvertedIQ(invertIQ); + RADIOLIB_ASSERT(state); uint8_t data[6] = {(uint8_t)((preambleLength >> 8) & 0xFF), (uint8_t)(preambleLength & 0xFF), headerType, payloadLength, crcType, invertIQ}; return(SPIwriteCommand(SX126X_CMD_SET_PACKET_PARAMS, data, 6)); } @@ -1215,20 +1341,25 @@ int16_t SX126x::setBufferBaseAddress(uint8_t txBaseAddress, uint8_t rxBaseAddres return(SPIwriteCommand(SX126X_CMD_SET_BUFFER_BASE_ADDRESS, data, 2)); } +int16_t SX126x::setRegulatorMode(uint8_t mode) { + uint8_t data[1] = {mode}; + return(SPIwriteCommand(SX126X_CMD_SET_REGULATOR_MODE, data, 1)); +} + uint8_t SX126x::getStatus() { - uint8_t data[1]; - SPIreadCommand(SX126X_CMD_GET_STATUS, data, 1); - return(data[0]); + uint8_t data = 0; + SPIreadCommand(SX126X_CMD_GET_STATUS, &data, 1); + return(data); } uint32_t SX126x::getPacketStatus() { - uint8_t data[3]; + uint8_t data[3] = {0, 0, 0}; SPIreadCommand(SX126X_CMD_GET_PACKET_STATUS, data, 3); return((((uint32_t)data[0]) << 16) | (((uint32_t)data[1]) << 8) | (uint32_t)data[2]); } uint16_t SX126x::getDeviceErrors() { - uint8_t data[2]; + uint8_t data[2] = {0, 0}; SPIreadCommand(SX126X_CMD_GET_DEVICE_ERRORS, data, 2); uint16_t opError = (((uint16_t)data[0] & 0xFF) << 8) & ((uint16_t)data[1]); return(opError); @@ -1242,8 +1373,7 @@ int16_t SX126x::clearDeviceErrors() { int16_t SX126x::setFrequencyRaw(float freq) { // calculate raw value uint32_t frf = (freq * (uint32_t(1) << SX126X_DIV_EXPONENT)) / SX126X_CRYSTAL_FREQ; - setRfFrequency(frf); - return(ERR_NONE); + return(setRfFrequency(frf)); } int16_t SX126x::fixSensitivity() { @@ -1253,9 +1383,7 @@ int16_t SX126x::fixSensitivity() { // read current sensitivity configuration uint8_t sensitivityConfig = 0; int16_t state = readRegister(SX126X_REG_SENSITIVITY_CONFIG, &sensitivityConfig, 1); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // fix the value for LoRa with 500 kHz bandwidth if((getPacketType() == SX126X_PACKET_TYPE_LORA) && (abs(_bwKhz - 500.0) <= 0.001)) { @@ -1273,9 +1401,7 @@ int16_t SX126x::fixPaClamping() { // read current clamping configuration uint8_t clampConfig = 0; int16_t state = readRegister(SX126X_REG_TX_CLAMP_CONFIG, &clampConfig, 1); - if (state != ERR_NONE) { - return state; - } + RADIOLIB_ASSERT(state); // update with the new value clampConfig |= 0x1E; @@ -1286,19 +1412,20 @@ int16_t SX126x::fixImplicitTimeout() { // fixes timeout in implicit header mode // see SX1262/SX1268 datasheet, chapter 15 Known Limitations, section 15.3 for details + //check if we're in implicit LoRa mode + if(!((_headerType == SX126X_LORA_HEADER_IMPLICIT) && (getPacketType() == SX126X_PACKET_TYPE_LORA))) { + return(ERR_WRONG_MODEM); + } + // stop RTC counter uint8_t rtcStop = 0x00; int16_t state = writeRegister(SX126X_REG_RTC_STOP, &rtcStop, 1); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // read currently active event uint8_t rtcEvent = 0; state = readRegister(SX126X_REG_RTC_EVENT, &rtcEvent, 1); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // clear events rtcEvent |= 0x02; @@ -1312,9 +1439,7 @@ int16_t SX126x::fixInvertedIQ(uint8_t iqConfig) { // read current IQ configuration uint8_t iqConfigCurrent = 0; int16_t state = readRegister(SX126X_REG_IQ_CONFIG, &iqConfigCurrent, 1); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set correct IQ configuration if(iqConfig == SX126X_LORA_IQ_STANDARD) { @@ -1328,33 +1453,20 @@ int16_t SX126x::fixInvertedIQ(uint8_t iqConfig) { } int16_t SX126x::config(uint8_t modem) { - // set regulator mode - uint8_t data[7]; - data[0] = SX126X_REGULATOR_DC_DC; - int16_t state = SPIwriteCommand(SX126X_CMD_SET_REGULATOR_MODE, data, 1); - if(state != ERR_NONE) { - return(state); - } - // reset buffer base address - state = setBufferBaseAddress(); - if(state != ERR_NONE) { - return(state); - } + int16_t state = setBufferBaseAddress(); + RADIOLIB_ASSERT(state); // set modem + uint8_t data[7]; data[0] = modem; state = SPIwriteCommand(SX126X_CMD_SET_PACKET_TYPE, data, 1); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set Rx/Tx fallback mode to STDBY_RC data[0] = SX126X_RX_TX_FALLBACK_MODE_STDBY_RC; state = SPIwriteCommand(SX126X_CMD_SET_RX_TX_FALLBACK_MODE, data, 1); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set CAD parameters data[0] = SX126X_CAD_ON_8_SYMB; @@ -1365,27 +1477,23 @@ int16_t SX126x::config(uint8_t modem) { data[5] = 0x00; data[6] = 0x00; state = SPIwriteCommand(SX126X_CMD_SET_CAD_PARAMS, data, 7); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // clear IRQ state = clearIrqStatus(); state |= setDioIrqParams(SX126X_IRQ_NONE, SX126X_IRQ_NONE); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // calibrate all blocks data[0] = SX126X_CALIBRATE_ALL; state = SPIwriteCommand(SX126X_CMD_CALIBRATE, data, 1); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // wait for calibration completion - delayMicroseconds(1); - while(digitalRead(_mod->getRx())); + delay(5); + while(digitalRead(_mod->getGpio())) { + yield(); + } return(ERR_NONE); } @@ -1419,9 +1527,8 @@ int16_t SX126x::SPItransfer(uint8_t* cmd, uint8_t cmdLen, bool write, uint8_t* d digitalWrite(_mod->getCs(), LOW); // ensure BUSY is low (state meachine ready) - RADIOLIB_VERBOSE_PRINTLN(F("Wait for BUSY ... ")); uint32_t start = millis(); - while(digitalRead(_mod->getRx())) { + while(digitalRead(_mod->getGpio())) { if(millis() - start >= timeout) { return(ERR_SPI_CMD_TIMEOUT); } @@ -1488,11 +1595,11 @@ int16_t SX126x::SPItransfer(uint8_t* cmd, uint8_t cmdLen, bool write, uint8_t* d if(waitForBusy) { delayMicroseconds(1); start = millis(); - while(digitalRead(_mod->getRx())) { - if(millis() - start >= timeout) { - status = SX126X_STATUS_CMD_TIMEOUT; - break; - } + while(digitalRead(_mod->getGpio())) { + if(millis() - start >= timeout) { + status = SX126X_STATUS_CMD_TIMEOUT; + break; + } } } @@ -1534,6 +1641,13 @@ int16_t SX126x::SPItransfer(uint8_t* cmd, uint8_t cmdLen, bool write, uint8_t* d RADIOLIB_VERBOSE_PRINTLN(); } RADIOLIB_VERBOSE_PRINTLN(); + #else + // some faster platforms require a short delay here + // not sure why, but it seems that long enough SPI transaction + // (e.g. setPacketParams for GFSK) will fail without it + #if defined(ARDUINO_ARCH_STM32) + delay(1); + #endif #endif // parse status diff --git a/src/modules/SX126x/SX126x.h b/src/modules/SX126x/SX126x.h index 9bfac2fd..df6e8028 100644 --- a/src/modules/SX126x/SX126x.h +++ b/src/modules/SX126x/SX126x.h @@ -7,9 +7,10 @@ #include "../../protocols/PhysicalLayer/PhysicalLayer.h" // SX126X physical layer properties +#define SX126X_FREQUENCY_STEP_SIZE 0.9536743164 +#define SX126X_MAX_PACKET_LENGTH 255 #define SX126X_CRYSTAL_FREQ 32.0 #define SX126X_DIV_EXPONENT 25 -#define SX126X_MAX_PACKET_LENGTH 255 // SX126X SPI commands // operational modes commands @@ -325,8 +326,8 @@ // SX126X SPI register variables //SX126X_REG_LORA_SYNC_WORD_MSB + LSB -#define SX126X_SYNC_WORD_PUBLIC 0x3444 -#define SX126X_SYNC_WORD_PRIVATE 0x1424 +#define SX126X_SYNC_WORD_PUBLIC 0x34 // actually 0x3444 NOTE: The low nibbles in each byte (0x_4_4) are masked out since apparently, they're reserved. +#define SX126X_SYNC_WORD_PRIVATE 0x12 // actually 0x1424 You couldn't make this up if you tried. /*! @@ -361,15 +362,19 @@ class SX126x: public PhysicalLayer { \param cr LoRa coding rate denominator. Allowed values range from 5 to 8. - \param syncWord 2-byte LoRa sync word. + \param syncWord 1-byte LoRa sync word. \param currentLimit Current protection limit in mA. \param preambleLength LoRa preamble length in symbols. Allowed values range from 1 to 65535. + \param tcxoVoltage TCXO reference voltage to be set on DIO3. Defaults to 1.6 V, set to 0 to skip. + + \param useRegulatorLDO use the LDO instead of DC-DC converter (default false). This is necessary for some modules such as the LAMBDA from RF solutions. + \returns \ref status_codes */ - int16_t begin(float bw, uint8_t sf, uint8_t cr, uint16_t syncWord, float currentLimit, uint16_t preambleLength); + int16_t begin(float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, float currentLimit, uint16_t preambleLength, float tcxoVoltage, bool useRegulatorLDO = false); /*! \brief Initialization method for FSK modem. @@ -386,9 +391,23 @@ class SX126x: public PhysicalLayer { \param dataShaping Time-bandwidth product of the Gaussian filter to be used for shaping. Allowed values are 0.3, 0.5, 0.7 and 1.0. Set to 0 to disable shaping. + \param tcxoVoltage TCXO reference voltage to be set on DIO3. Defaults to 1.6 V, set to 0 to skip. + + \param useRegulatorLDO use the LDO instead of DC-DC converter (default false). This is necessary for some modules such as the LAMBDA from RF solutions. + \returns \ref status_codes */ - int16_t beginFSK(float br, float freqDev, float rxBw, float currentLimit, uint16_t preambleLength, float dataShaping); + int16_t beginFSK(float br, float freqDev, float rxBw, float currentLimit, uint16_t preambleLength, float dataShaping, float tcxoVoltage, bool useRegulatorLDO = false); + + /*! + \brief Reset method. Will reset the chip to the default state using RST pin. + + \param verify Whether correct module startup should be verified. When set to true, RadioLib will attempt to verify the module has started correctly + by repeatedly issuing setStandby command. Enabled by default. + + \returns \ref status_codes + */ + int16_t reset(bool verify = true); /*! \brief Blocking binary transmit method. @@ -443,9 +462,11 @@ class SX126x: public PhysicalLayer { /*! \brief Sets the module to sleep mode. + \param retainConfig Set to true to retain configuration of the currently active modem ("warm start") or to false to discard current configuration ("cold start"). Defaults to true. + \returns \ref status_codes */ - int16_t sleep(); + int16_t sleep(bool retainConfig = true); /*! \brief Sets the module to standby mode (overload for PhysicalLayer compatibility, uses 13 MHz RC oscillator). @@ -473,11 +494,9 @@ class SX126x: public PhysicalLayer { void setDio1Action(void (*func)(void)); /*! - \brief Sets interrupt service routine to call when DIO2 activates. - - \param func ISR to call. + \brief Clears interrupt service routine to call when DIO1 activates. */ - void setDio2Action(void (*func)(void)); + void clearDio1Action(); /*! \brief Interrupt-driven binary transmit method. @@ -502,6 +521,31 @@ class SX126x: public PhysicalLayer { */ int16_t startReceive(uint32_t timeout = SX126X_RX_TIMEOUT_INF); + /*! + \brief Interrupt-driven receive method where the device mostly sleeps and periodically wakes to listen. + Note that this function assumes the unit will take 500us + TCXO_delay to change state. See datasheet section 13.1.7, version 1.2. + + \param rxPeriod The duration the receiver will be in Rx mode, in microseconds. + + \param sleepPeriod The duration the receiver will not be in Rx mode, in microseconds. + + \returns \ref status_codes + */ + int16_t startReceiveDutyCycle(uint32_t rxPeriod, uint32_t sleepPeriod); + + /*! + \brief Calls \ref startReceiveDutyCycle with rxPeriod and sleepPeriod set so the unit shouldn't miss any messages. + + \param senderPreambleLength Expected preamble length of the messages to receive. + If set to zero, the currently configured preamble length will be used. Defaults to zero. + + \param minSymbols Parameters will be chosen to ensure that the unit will catch at least this many symbols of any preamble of the specified length. Defaults to 8. + According to Semtech, receiver requires 8 symbols to reliably latch a preamble. This makes this method redundant when transmitter preamble length is less than 17 (2*minSymbols + 1). + + \returns \ref status_codes + */ + int16_t startReceiveDutyCycleAuto(uint16_t senderPreambleLength = 0, uint16_t minSymbols = 8); + /*! \brief Reads data received after calling startReceive method. @@ -547,9 +591,11 @@ class SX126x: public PhysicalLayer { \param syncWord LoRa sync word to be set. + \param controlBits Undocumented control bits, required for compatibility purposes. + \returns \ref status_codes */ - int16_t setSyncWord(uint16_t syncWord); + int16_t setSyncWord(uint8_t syncWord, uint8_t controlBits = 0x44); /*! \brief Sets current protection limit. Can be set in 0.25 mA steps. @@ -560,6 +606,13 @@ class SX126x: public PhysicalLayer { */ int16_t setCurrentLimit(float currentLimit); + /*! + \brief Reads current protection limit. + + \returns Currently configured overcurrent protection limit in mA. + */ + float getCurrentLimit(); + /*! \brief Sets preamble length for LoRa or FSK modem. Allowed values range from 1 to 65535. @@ -751,6 +804,45 @@ class SX126x: public PhysicalLayer { */ uint32_t getTimeOnAir(size_t len); + /*! + \brief Set implicit header mode for future reception/transmission. + + \returns \ref status_codes + */ + int16_t implicitHeader(size_t len); + + /*! + \brief Set explicit header mode for future reception/transmission. + + \param len Payload length in bytes. + + \returns \ref status_codes + */ + int16_t explicitHeader(); + + /*! + \brief Set regulator mode to LDO. + + \returns \ref status_codes + */ + int16_t setRegulatorLDO(); + + /*! + \brief Set regulator mode to DC-DC. + + \returns \ref status_codes + */ + int16_t setRegulatorDCDC(); + + /*! + \brief Sets transmission encoding. Available in FSK mode only. Serves only as alias for PhysicalLayer compatibility. + + \param encoding Encoding to be used. Set to 0 for NRZ, and 2 for whitening. + + \returns \ref status_codes + */ + int16_t setEncoding(uint8_t encoding); + #ifndef RADIOLIB_GODMODE protected: #endif @@ -772,17 +864,19 @@ class SX126x: public PhysicalLayer { int16_t setTxParams(uint8_t power, uint8_t rampTime = SX126X_PA_RAMP_200U); int16_t setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, uint8_t ldro = 0xFF); int16_t setModulationParamsFSK(uint32_t br, uint8_t pulseShape, uint8_t rxBw, uint32_t freqDev); - int16_t setPacketParams(uint16_t preambleLength, uint8_t crcType, uint8_t payloadLength = 0xFF, uint8_t headerType = SX126X_LORA_HEADER_EXPLICIT, uint8_t invertIQ = SX126X_LORA_IQ_STANDARD); + int16_t setPacketParams(uint16_t preambleLength, uint8_t crcType, uint8_t payloadLength, uint8_t headerType, uint8_t invertIQ = SX126X_LORA_IQ_STANDARD); int16_t setPacketParamsFSK(uint16_t preambleLength, uint8_t crcType, uint8_t syncWordLength, uint8_t addrComp, uint8_t whitening, uint8_t packetType = SX126X_GFSK_PACKET_VARIABLE, uint8_t payloadLength = 0xFF, uint8_t preambleDetectorLength = SX126X_GFSK_PREAMBLE_DETECT_16); int16_t setBufferBaseAddress(uint8_t txBaseAddress = 0x00, uint8_t rxBaseAddress = 0x00); + int16_t setRegulatorMode(uint8_t mode); uint8_t getStatus(); uint32_t getPacketStatus(); uint16_t getDeviceErrors(); int16_t clearDeviceErrors(); + int16_t startReceiveCommon(); int16_t setFrequencyRaw(float freq); - int16_t setOptimalHiPowerPaConfig(int8_t* inOutPower); int16_t setPacketMode(uint8_t mode, uint8_t len); + int16_t setHeaderType(uint8_t headerType, size_t len = 0xFF); // fixes to errata int16_t fixSensitivity(); @@ -795,7 +889,7 @@ class SX126x: public PhysicalLayer { #endif Module* _mod; - uint8_t _bw, _sf, _cr, _ldro, _crcType; + uint8_t _bw, _sf, _cr, _ldro, _crcType, _headerType; uint16_t _preambleLength; float _bwKhz; @@ -806,6 +900,10 @@ class SX126x: public PhysicalLayer { float _dataRate; + uint32_t _tcxoDelay; + + size_t _implicitLen; + int16_t config(uint8_t modem); // common low-level SPI interface diff --git a/src/modules/SX127x/SX1272.cpp b/src/modules/SX127x/SX1272.cpp index 1221bb27..d5bb554c 100644 --- a/src/modules/SX127x/SX1272.cpp +++ b/src/modules/SX127x/SX1272.cpp @@ -7,53 +7,34 @@ SX1272::SX1272(Module* mod) : SX127x(mod) { int16_t SX1272::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint8_t currentLimit, uint16_t preambleLength, uint8_t gain) { // execute common part int16_t state = SX127x::begin(SX1272_CHIP_VERSION, syncWord, currentLimit, preambleLength); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure settings not accessible by API state = config(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // mitigation of receiver spurious response // see SX1272/73 Errata, section 2.2 for details state = _mod->SPIsetRegValue(0x31, 0b10000000, 7, 7); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure publicly accessible settings state = setFrequency(freq); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setBandwidth(bw); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setSpreadingFactor(sf); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setCodingRate(cr); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setOutputPower(power); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setGain(gain); - if(state != ERR_NONE) { - return(state); - } return(state); } @@ -61,26 +42,17 @@ int16_t SX1272::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t sync int16_t SX1272::beginFSK(float freq, float br, float rxBw, float freqDev, int8_t power, uint8_t currentLimit, uint16_t preambleLength, bool enableOOK) { // execute common part int16_t state = SX127x::beginFSK(SX1272_CHIP_VERSION, br, rxBw, freqDev, currentLimit, preambleLength, enableOOK); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure settings not accessible by API state = configFSK(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure publicly accessible settings state = setFrequency(freq); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setOutputPower(power); - if(state != ERR_NONE) { - return(state); - } return(state); } @@ -326,13 +298,13 @@ int16_t SX1272::setDataShapingOOK(uint8_t sh) { // set data shaping switch(sh) { case 0: - state |= _mod->SPIsetRegValue(SX127X_REG_PA_RAMP, SX1272_NO_SHAPING, 4, 3); + state |= _mod->SPIsetRegValue(SX127X_REG_OP_MODE, SX1272_NO_SHAPING, 4, 3); break; case 1: - state |= _mod->SPIsetRegValue(SX127X_REG_PA_RAMP, SX1272_OOK_FILTER_BR, 4, 3); + state |= _mod->SPIsetRegValue(SX127X_REG_OP_MODE, SX1272_OOK_FILTER_BR, 4, 3); break; case 2: - state |= _mod->SPIsetRegValue(SX127X_REG_PA_RAMP, SX1272_OOK_FILTER_2BR, 4, 3); + state |= _mod->SPIsetRegValue(SX127X_REG_OP_MODE, SX1272_OOK_FILTER_2BR, 4, 3); break; default: state = ERR_INVALID_DATA_SHAPING; @@ -429,9 +401,7 @@ int16_t SX1272::setCodingRateRaw(uint8_t newCodingRate) { int16_t SX1272::configFSK() { // configure common registers int16_t state = SX127x::configFSK(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set fast PLL hop state = _mod->SPIsetRegValue(SX1272_REG_PLL_HOP, SX127X_FAST_HOP_ON, 7, 7); diff --git a/src/modules/SX127x/SX1273.cpp b/src/modules/SX127x/SX1273.cpp index a6b7a99a..ee0a48fc 100644 --- a/src/modules/SX127x/SX1273.cpp +++ b/src/modules/SX127x/SX1273.cpp @@ -1,66 +1,47 @@ #include "SX1273.h" SX1273::SX1273(Module* mod) : SX1272(mod) { - + } int16_t SX1273::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint8_t currentLimit, uint16_t preambleLength, uint8_t gain) { // execute common part int16_t state = SX127x::begin(SX1272_CHIP_VERSION, syncWord, currentLimit, preambleLength); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + // configure settings not accessible by API state = config(); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + // mitigation of receiver spurious response // see SX1272/73 Errata, section 2.2 for details state = _mod->SPIsetRegValue(0x31, 0b10000000, 7, 7); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + // configure publicly accessible settings state = setFrequency(freq); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setBandwidth(bw); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setSpreadingFactor(sf); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setCodingRate(cr); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setOutputPower(power); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setGain(gain); - if(state != ERR_NONE) { - return(state); - } - + return(state); } int16_t SX1273::setSpreadingFactor(uint8_t sf) { uint8_t newSpreadingFactor; - + // check allowed spreading factor values switch(sf) { case 6: @@ -78,12 +59,12 @@ int16_t SX1273::setSpreadingFactor(uint8_t sf) { default: return(ERR_INVALID_SPREADING_FACTOR); } - + // set spreading factor and if successful, save the new setting int16_t state = setSpreadingFactorRaw(newSpreadingFactor); if(state == ERR_NONE) { SX127x::_sf = sf; } - + return(state); } diff --git a/src/modules/SX127x/SX1276.cpp b/src/modules/SX127x/SX1276.cpp index eb7da074..40a141a4 100644 --- a/src/modules/SX127x/SX1276.cpp +++ b/src/modules/SX127x/SX1276.cpp @@ -1,53 +1,36 @@ #include "SX1276.h" SX1276::SX1276(Module* mod) : SX1278(mod) { - + } int16_t SX1276::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint8_t currentLimit, uint16_t preambleLength, uint8_t gain) { // execute common part int16_t state = SX127x::begin(SX1278_CHIP_VERSION, syncWord, currentLimit, preambleLength); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + // configure settings not accessible by API state = config(); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + // configure publicly accessible settings state = setFrequency(freq); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setBandwidth(bw); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setSpreadingFactor(sf); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setCodingRate(cr); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setOutputPower(power); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setGain(gain); - if(state != ERR_NONE) { - return(state); - } - + return(state); } @@ -56,7 +39,7 @@ int16_t SX1276::setFrequency(float freq) { if((freq < 137.0) || (freq > 1020.0)) { return(ERR_INVALID_FREQUENCY); } - + // SX1276/77/78 Errata fixes if(getActiveModem() == SX127X_LORA) { // sensitivity optimization for 500kHz bandwidth @@ -70,7 +53,7 @@ int16_t SX1276::setFrequency(float freq) { _mod->SPIwriteRegister(0x3a, 0x7F); } } - + // mitigation of receiver spurious response // see SX1276/77/78 Errata, section 2.3 for details if(abs(_bw - 7.8) <= 0.001) { @@ -119,7 +102,7 @@ int16_t SX1276::setFrequency(float freq) { _mod->SPIsetRegValue(0x31, 0b1000000, 7, 7); } } - + // set frequency return(SX127x::setFrequencyRaw(freq)); } diff --git a/src/modules/SX127x/SX1277.cpp b/src/modules/SX127x/SX1277.cpp index 406bf2c6..bed5a84e 100644 --- a/src/modules/SX127x/SX1277.cpp +++ b/src/modules/SX127x/SX1277.cpp @@ -1,53 +1,36 @@ #include "SX1277.h" SX1277::SX1277(Module* mod) : SX1278(mod) { - + } int16_t SX1277::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint8_t currentLimit, uint16_t preambleLength, uint8_t gain) { // execute common part int16_t state = SX127x::begin(SX1278_CHIP_VERSION, syncWord, currentLimit, preambleLength); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + // configure settings not accessible by API state = config(); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + // configure publicly accessible settings state = setFrequency(freq); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setBandwidth(bw); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setSpreadingFactor(sf); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setCodingRate(cr); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setOutputPower(power); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setGain(gain); - if(state != ERR_NONE) { - return(state); - } - + return(state); } @@ -56,7 +39,7 @@ int16_t SX1277::setFrequency(float freq) { if((freq < 137.0) || (freq > 1020.0)) { return(ERR_INVALID_FREQUENCY); } - + // SX1276/77/78 Errata fixes if(getActiveModem() == SX127X_LORA) { // sensitivity optimization for 500kHz bandwidth @@ -70,7 +53,7 @@ int16_t SX1277::setFrequency(float freq) { _mod->SPIwriteRegister(0x3a, 0x7F); } } - + // mitigation of receiver spurious response // see SX1276/77/78 Errata, section 2.3 for details if(abs(_bw - 7.8) <= 0.001) { @@ -119,14 +102,14 @@ int16_t SX1277::setFrequency(float freq) { _mod->SPIsetRegValue(0x31, 0b1000000, 7, 7); } } - + // set frequency and if successful, save the new setting return(SX127x::setFrequencyRaw(freq)); } int16_t SX1277::setSpreadingFactor(uint8_t sf) { uint8_t newSpreadingFactor; - + // check allowed spreading factor values switch(sf) { case 6: @@ -144,12 +127,12 @@ int16_t SX1277::setSpreadingFactor(uint8_t sf) { default: return(ERR_INVALID_SPREADING_FACTOR); } - + // set spreading factor and if successful, save the new setting int16_t state = SX1278::setSpreadingFactorRaw(newSpreadingFactor); if(state == ERR_NONE) { SX127x::_sf = sf; } - + return(state); } diff --git a/src/modules/SX127x/SX1278.cpp b/src/modules/SX127x/SX1278.cpp index f56392be..b2ab0b3c 100644 --- a/src/modules/SX127x/SX1278.cpp +++ b/src/modules/SX127x/SX1278.cpp @@ -7,46 +7,29 @@ SX1278::SX1278(Module* mod) : SX127x(mod) { int16_t SX1278::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint8_t currentLimit, uint16_t preambleLength, uint8_t gain) { // execute common part int16_t state = SX127x::begin(SX1278_CHIP_VERSION, syncWord, currentLimit, preambleLength); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure settings not accessible by API state = config(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure publicly accessible settings state = setFrequency(freq); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setBandwidth(bw); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setSpreadingFactor(sf); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setCodingRate(cr); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setOutputPower(power); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setGain(gain); - if(state != ERR_NONE) { - return(state); - } return(state); } @@ -54,26 +37,17 @@ int16_t SX1278::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t sync int16_t SX1278::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t power, uint8_t currentLimit, uint16_t preambleLength, bool enableOOK) { // execute common part int16_t state = SX127x::beginFSK(SX1278_CHIP_VERSION, br, freqDev, rxBw, currentLimit, preambleLength, enableOOK); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure settings not accessible by API state = configFSK(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // configure publicly accessible settings state = setFrequency(freq); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); state = setOutputPower(power); - if(state != ERR_NONE) { - return(state); - } return(state); } @@ -505,9 +479,7 @@ int16_t SX1278::setCodingRateRaw(uint8_t newCodingRate) { int16_t SX1278::configFSK() { // configure common registers int16_t state = SX127x::configFSK(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set fast PLL hop state = _mod->SPIsetRegValue(SX1278_REG_PLL_HOP, SX127X_FAST_HOP_ON, 7, 7); diff --git a/src/modules/SX127x/SX1279.cpp b/src/modules/SX127x/SX1279.cpp index 5e7fd2ea..3468b0b1 100644 --- a/src/modules/SX127x/SX1279.cpp +++ b/src/modules/SX127x/SX1279.cpp @@ -1,53 +1,36 @@ #include "SX1279.h" SX1279::SX1279(Module* mod) : SX1278(mod) { - + } int16_t SX1279::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint8_t currentLimit, uint16_t preambleLength, uint8_t gain) { // execute common part int16_t state = SX127x::begin(SX1278_CHIP_VERSION, syncWord, currentLimit, preambleLength); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + // configure settings not accessible by API state = config(); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + // configure publicly accessible settings state = setFrequency(freq); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setBandwidth(bw); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setSpreadingFactor(sf); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setCodingRate(cr); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setOutputPower(power); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); + state = setGain(gain); - if(state != ERR_NONE) { - return(state); - } - + return(state); } @@ -56,7 +39,7 @@ int16_t SX1279::setFrequency(float freq) { if((freq < 137.0) || (freq > 960.0)) { return(ERR_INVALID_FREQUENCY); } - + // set frequency return(SX127x::setFrequencyRaw(freq)); } diff --git a/src/modules/SX127x/SX127x.cpp b/src/modules/SX127x/SX127x.cpp index 9347f44c..36e4f857 100644 --- a/src/modules/SX127x/SX127x.cpp +++ b/src/modules/SX127x/SX127x.cpp @@ -1,13 +1,15 @@ #include "SX127x.h" -SX127x::SX127x(Module* mod) : PhysicalLayer(SX127X_CRYSTAL_FREQ, SX127X_DIV_EXPONENT, SX127X_MAX_PACKET_LENGTH) { +SX127x::SX127x(Module* mod) : PhysicalLayer(SX127X_FREQUENCY_STEP_SIZE, SX127X_MAX_PACKET_LENGTH) { _mod = mod; _packetLengthQueried = false; } int16_t SX127x::begin(uint8_t chipVersion, uint8_t syncWord, uint8_t currentLimit, uint16_t preambleLength) { // set module properties - _mod->init(RADIOLIB_USE_SPI, RADIOLIB_INT_BOTH); + _mod->init(RADIOLIB_USE_SPI); + Module::pinMode(_mod->getIrq(), INPUT); + Module::pinMode(_mod->getGpio(), INPUT); // try to find the SX127x chip if(!SX127x::findChip(chipVersion)) { @@ -18,33 +20,28 @@ int16_t SX127x::begin(uint8_t chipVersion, uint8_t syncWord, uint8_t currentLimi RADIOLIB_DEBUG_PRINTLN(F("Found SX127x!")); } + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + // check active modem - int16_t state; if(getActiveModem() != SX127X_LORA) { // set LoRa mode state = setActiveModem(SX127X_LORA); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); } // set LoRa sync word state = SX127x::setSyncWord(syncWord); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set over current protection state = SX127x::setCurrentLimit(currentLimit); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set preamble length state = SX127x::setPreambleLength(preambleLength); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // initalize internal variables _dataRate = 0.0; @@ -54,7 +51,11 @@ int16_t SX127x::begin(uint8_t chipVersion, uint8_t syncWord, uint8_t currentLimi int16_t SX127x::beginFSK(uint8_t chipVersion, float br, float freqDev, float rxBw, uint8_t currentLimit, uint16_t preambleLength, bool enableOOK) { // set module properties - _mod->init(RADIOLIB_USE_SPI, RADIOLIB_INT_BOTH); + _mod->init(RADIOLIB_USE_SPI); + Module::pinMode(_mod->getIrq(), INPUT); + + // reset the module + reset(); // try to find the SX127x chip if(!SX127x::findChip(chipVersion)) { @@ -70,81 +71,64 @@ int16_t SX127x::beginFSK(uint8_t chipVersion, float br, float freqDev, float rxB if(getActiveModem() != SX127X_FSK_OOK) { // set FSK mode state = setActiveModem(SX127X_FSK_OOK); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); } // set bit rate state = SX127x::setBitRate(br); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set frequency deviation state = SX127x::setFrequencyDeviation(freqDev); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set receiver bandwidth state = SX127x::setRxBandwidth(rxBw); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set over current protection state = SX127x::setCurrentLimit(currentLimit); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set preamble length state = SX127x::setPreambleLength(preambleLength); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // default sync word value 0x2D01 is the same as the default in LowPowerLab RFM69 library uint8_t syncWord[] = {0x2D, 0x01}; state = setSyncWord(syncWord, 2); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // disable address filtering state = disableAddressFiltering(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // enable/disable OOK state = setOOK(enableOOK); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set default RSSI measurement config state = setRSSIConfig(2); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set default encoding state = setEncoding(0); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set default packet length mode state = variablePacketLengthMode(); - if (state != ERR_NONE) { - return(state); - } return(state); } +void SX127x::reset() { + Module::pinMode(_mod->getRst(), OUTPUT); + Module::digitalWrite(_mod->getRst(), LOW); + delayMicroseconds(100); + Module::digitalWrite(_mod->getRst(), HIGH); + delay(5); +} + int16_t SX127x::transmit(uint8_t* data, size_t len, uint8_t addr) { // set mode to standby int16_t state = setMode(SX127X_STANDBY); @@ -166,13 +150,11 @@ int16_t SX127x::transmit(uint8_t* data, size_t len, uint8_t addr) { // start transmission state = startTransmit(data, len, addr); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // wait for packet transmission or timeout start = micros(); - while(!digitalRead(_mod->getInt0())) { + while(!digitalRead(_mod->getIrq())) { if(micros() - start > timeout) { clearIRQFlags(); return(ERR_TX_TIMEOUT); @@ -185,13 +167,11 @@ int16_t SX127x::transmit(uint8_t* data, size_t len, uint8_t addr) { // start transmission state = startTransmit(data, len, addr); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // wait for transmission end or timeout start = micros(); - while(!digitalRead(_mod->getInt0())) { + while(!digitalRead(_mod->getIrq())) { if(micros() - start > timeout) { clearIRQFlags(); standby(); @@ -221,13 +201,11 @@ int16_t SX127x::receive(uint8_t* data, size_t len) { if(modem == SX127X_LORA) { // set mode to receive state = startReceive(len, SX127X_RXSINGLE); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // wait for packet reception or timeout (100 LoRa symbols) - while(!digitalRead(_mod->getInt0())) { - if(digitalRead(_mod->getInt1())) { + while(!digitalRead(_mod->getIrq())) { + if(digitalRead(_mod->getGpio())) { clearIRQFlags(); return(ERR_RX_TIMEOUT); } @@ -239,13 +217,11 @@ int16_t SX127x::receive(uint8_t* data, size_t len) { // set mode to receive state = startReceive(len, SX127X_RX); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // wait for packet reception or timeout uint32_t start = micros(); - while(!digitalRead(_mod->getInt0())) { + while(!digitalRead(_mod->getIrq())) { if(micros() - start > timeout) { clearIRQFlags(); return(ERR_RX_TIMEOUT); @@ -267,22 +243,22 @@ int16_t SX127x::scanChannel() { // set mode to standby int16_t state = setMode(SX127X_STANDBY); + RADIOLIB_ASSERT(state); // set DIO pin mapping - state |= _mod->SPIsetRegValue(SX127X_REG_DIO_MAPPING_1, SX127X_DIO0_CAD_DONE | SX127X_DIO1_CAD_DETECTED, 7, 4); + state = _mod->SPIsetRegValue(SX127X_REG_DIO_MAPPING_1, SX127X_DIO0_CAD_DONE | SX127X_DIO1_CAD_DETECTED, 7, 4); + RADIOLIB_ASSERT(state); // clear interrupt flags clearIRQFlags(); // set mode to CAD - state |= setMode(SX127X_CAD); - if(state != ERR_NONE) { - return(state); - } + state = setMode(SX127X_CAD); + RADIOLIB_ASSERT(state); // wait for channel activity detected or timeout - while(!digitalRead(_mod->getInt0())) { - if(digitalRead(_mod->getInt1())) { + while(!digitalRead(_mod->getIrq())) { + if(digitalRead(_mod->getGpio())) { clearIRQFlags(); return(PREAMBLE_DETECTED); } @@ -321,9 +297,7 @@ int16_t SX127x::transmitDirect(uint32_t FRF) { // activate direct mode int16_t state = directMode(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // start transmitting return(setMode(SX127X_TX)); @@ -337,9 +311,7 @@ int16_t SX127x::receiveDirect() { // activate direct mode int16_t state = directMode(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // start receiving return(setMode(SX127X_RX)); @@ -348,16 +320,14 @@ int16_t SX127x::receiveDirect() { int16_t SX127x::directMode() { // set mode to standby int16_t state = setMode(SX127X_STANDBY); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set DIO mapping state = _mod->SPIsetRegValue(SX127X_REG_DIO_MAPPING_1, SX127X_DIO1_CONT_DCLK | SX127X_DIO2_CONT_DATA, 5, 2); + RADIOLIB_ASSERT(state); // set continuous mode - state |= _mod->SPIsetRegValue(SX127X_REG_PACKET_CONFIG_2, SX127X_DATA_MODE_CONTINUOUS, 6, 6); - return(state); + return(_mod->SPIsetRegValue(SX127X_REG_PACKET_CONFIG_2, SX127X_DATA_MODE_CONTINUOUS, 6, 6)); } int16_t SX127x::packetMode() { @@ -389,9 +359,7 @@ int16_t SX127x::startReceive(uint8_t len, uint8_t mode) { // set FIFO pointers state |= _mod->SPIsetRegValue(SX127X_REG_FIFO_RX_BASE_ADDR, SX127X_FIFO_RX_BASE_ADDR_MAX); state |= _mod->SPIsetRegValue(SX127X_REG_FIFO_ADDR_PTR, SX127X_FIFO_RX_BASE_ADDR_MAX); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); } else if(modem == SX127X_FSK_OOK) { // set DIO pin mapping @@ -411,11 +379,25 @@ int16_t SX127x::startReceive(uint8_t len, uint8_t mode) { } void SX127x::setDio0Action(void (*func)(void)) { - attachInterrupt(digitalPinToInterrupt(_mod->getInt0()), func, RISING); + attachInterrupt(digitalPinToInterrupt(_mod->getIrq()), func, RISING); +} + +void SX127x::clearDio0Action() { + detachInterrupt(digitalPinToInterrupt(_mod->getIrq())); } void SX127x::setDio1Action(void (*func)(void)) { - attachInterrupt(digitalPinToInterrupt(_mod->getInt1()), func, RISING); + if(_mod->getGpio() != NC) { + return; + } + attachInterrupt(digitalPinToInterrupt(_mod->getGpio()), func, RISING); +} + +void SX127x::clearDio1Action() { + if(_mod->getGpio() != NC) { + return; + } + detachInterrupt(digitalPinToInterrupt(_mod->getGpio())); } int16_t SX127x::startTransmit(uint8_t* data, size_t len, uint8_t addr) { @@ -447,9 +429,7 @@ int16_t SX127x::startTransmit(uint8_t* data, size_t len, uint8_t addr) { // start transmission state |= setMode(SX127X_TX); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); return(ERR_NONE); @@ -479,9 +459,7 @@ int16_t SX127x::startTransmit(uint8_t* data, size_t len, uint8_t addr) { // start transmission state |= setMode(SX127X_TX); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); return(ERR_NONE); } @@ -579,9 +557,7 @@ int16_t SX127x::setCurrentLimit(uint8_t currentLimit) { int16_t SX127x::setPreambleLength(uint16_t preambleLength) { // set mode to standby int16_t state = setMode(SX127X_STANDBY); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // check active modem uint8_t modem = getActiveModem(); @@ -693,9 +669,7 @@ int16_t SX127x::setBitRate(float br) { // set mode to STANDBY int16_t state = setMode(SX127X_STANDBY); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set bit rate uint16_t bitRate = (SX127X_CRYSTAL_FREQ * 1000.0) / br; @@ -722,9 +696,7 @@ int16_t SX127x::setFrequencyDeviation(float freqDev) { // set mode to STANDBY int16_t state = setMode(SX127X_STANDBY); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set allowed frequency deviation uint32_t base = 1; @@ -747,9 +719,7 @@ int16_t SX127x::setRxBandwidth(float rxBw) { // set mode to STANDBY int16_t state = setMode(SX127X_STANDBY); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // calculate exponent and mantissa values for(uint8_t e = 7; e >= 1; e--) { @@ -758,9 +728,7 @@ int16_t SX127x::setRxBandwidth(float rxBw) { if(abs(rxBw - ((point / 1000.0) + 0.05)) <= 0.5) { // set Rx bandwidth during AFC state = _mod->SPIsetRegValue(SX127X_REG_AFC_BW, (m << 3) | e, 4, 0); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set Rx bandwidth state = _mod->SPIsetRegValue(SX127X_REG_RX_BW, (m << 3) | e, 4, 0); @@ -796,9 +764,7 @@ int16_t SX127x::setSyncWord(uint8_t* syncWord, size_t len) { // enable sync word recognition int16_t state = _mod->SPIsetRegValue(SX127X_REG_SYNC_CONFIG, SX127X_SYNC_ON, 4, 4); state |= _mod->SPIsetRegValue(SX127X_REG_SYNC_CONFIG, len - 1, 2, 0); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set sync word _mod->SPIwriteRegisterBurst(SX127X_REG_SYNC_VALUE_1, syncWord, len); @@ -813,9 +779,7 @@ int16_t SX127x::setNodeAddress(uint8_t nodeAddr) { // enable address filtering (node only) int16_t state = _mod->SPIsetRegValue(SX127X_REG_PACKET_CONFIG_1, SX127X_ADDRESS_FILTERING_NODE, 2, 1); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set node address return(_mod->SPIsetRegValue(SX127X_REG_NODE_ADRS, nodeAddr)); @@ -829,9 +793,7 @@ int16_t SX127x::setBroadcastAddress(uint8_t broadAddr) { // enable address filtering (node + broadcast) int16_t state = _mod->SPIsetRegValue(SX127X_REG_PACKET_CONFIG_1, SX127X_ADDRESS_FILTERING_NODE_BROADCAST, 2, 1); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set broadcast address return(_mod->SPIsetRegValue(SX127X_REG_BROADCAST_ADRS, broadAddr)); @@ -845,15 +807,11 @@ int16_t SX127x::disableAddressFiltering() { // disable address filtering int16_t state = _mod->SPIsetRegValue(SX127X_REG_PACKET_CONFIG_1, SX127X_ADDRESS_FILTERING_OFF, 2, 1); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set node address to default (0x00) state = _mod->SPIsetRegValue(SX127X_REG_NODE_ADRS, 0x00); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set broadcast address to default (0x00) return(_mod->SPIsetRegValue(SX127X_REG_BROADCAST_ADRS, 0x00)); @@ -933,9 +891,7 @@ int16_t SX127x::setRSSIConfig(uint8_t smoothingSamples, int8_t offset) { // set mode to standby int16_t state = standby(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // check provided values if(!(smoothingSamples <= 7)) { @@ -980,9 +936,7 @@ int16_t SX127x::config() { int16_t SX127x::configFSK() { // set RSSI threshold int16_t state = _mod->SPIsetRegValue(SX127X_REG_RSSI_THRESH, SX127X_RSSI_THRESHOLD); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // reset FIFO flag _mod->SPIwriteRegister(SX127X_REG_IRQ_FLAGS_2, SX127X_FLAG_FIFO_OVERRUN); @@ -990,38 +944,27 @@ int16_t SX127x::configFSK() { // set packet configuration state = _mod->SPIsetRegValue(SX127X_REG_PACKET_CONFIG_1, SX127X_PACKET_VARIABLE | SX127X_DC_FREE_WHITENING | SX127X_CRC_ON | SX127X_CRC_AUTOCLEAR_ON | SX127X_ADDRESS_FILTERING_OFF | SX127X_CRC_WHITENING_TYPE_CCITT, 7, 0); state |= _mod->SPIsetRegValue(SX127X_REG_PACKET_CONFIG_2, SX127X_DATA_MODE_PACKET | SX127X_IO_HOME_OFF, 6, 5); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set preamble polarity state =_mod->SPIsetRegValue(SX127X_REG_SYNC_CONFIG, SX127X_PREAMBLE_POLARITY_55, 5, 5); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set FIFO threshold state = _mod->SPIsetRegValue(SX127X_REG_FIFO_THRESH, SX127X_TX_START_FIFO_NOT_EMPTY, 7, 7); state |= _mod->SPIsetRegValue(SX127X_REG_FIFO_THRESH, SX127X_FIFO_THRESH, 5, 0); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // disable Rx timeouts state = _mod->SPIsetRegValue(SX127X_REG_RX_TIMEOUT_1, SX127X_TIMEOUT_RX_RSSI_OFF); state |= _mod->SPIsetRegValue(SX127X_REG_RX_TIMEOUT_2, SX127X_TIMEOUT_RX_PREAMBLE_OFF); state |= _mod->SPIsetRegValue(SX127X_REG_RX_TIMEOUT_3, SX127X_TIMEOUT_SIGNAL_SYNC_OFF); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // enable preamble detector and set preamble length state = _mod->SPIsetRegValue(SX127X_REG_PREAMBLE_DETECT, SX127X_PREAMBLE_DETECTOR_ON | SX127X_PREAMBLE_DETECTOR_2_BYTE | SX127X_PREAMBLE_DETECTOR_TOL); state |= _mod->SPIsetRegValue(SX127X_REG_PREAMBLE_MSB_FSK, SX127X_PREAMBLE_SIZE_MSB); state |= _mod->SPIsetRegValue(SX127X_REG_PREAMBLE_LSB_FSK, SX127X_PREAMBLE_SIZE_LSB); - if(state != ERR_NONE) { - return(state); - } return(state); } @@ -1039,15 +982,11 @@ int16_t SX127x::setPacketMode(uint8_t mode, uint8_t len) { // set to fixed packet length int16_t state = _mod->SPIsetRegValue(SX127X_REG_PACKET_CONFIG_1, mode, 7, 7); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set length to register state = _mod->SPIsetRegValue(SX127X_REG_PAYLOAD_LENGTH_FSK, len); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // update cached value _packetLengthConfig = mode; @@ -1058,6 +997,10 @@ bool SX127x::findChip(uint8_t ver) { uint8_t i = 0; bool flagFound = false; while((i < 10) && !flagFound) { + // reset the module + reset(); + + // check version register uint8_t version = _mod->SPIreadRegister(SX127X_REG_VERSION); if(version == ver) { flagFound = true; diff --git a/src/modules/SX127x/SX127x.h b/src/modules/SX127x/SX127x.h index c805f5f9..d48e9e30 100644 --- a/src/modules/SX127x/SX127x.h +++ b/src/modules/SX127x/SX127x.h @@ -7,10 +7,11 @@ #include "../../protocols/PhysicalLayer/PhysicalLayer.h" // SX127x physical layer properties -#define SX127X_CRYSTAL_FREQ 32.0 -#define SX127X_DIV_EXPONENT 19 +#define SX127X_FREQUENCY_STEP_SIZE 61.03515625 #define SX127X_MAX_PACKET_LENGTH 255 #define SX127X_MAX_PACKET_LENGTH_FSK 64 +#define SX127X_CRYSTAL_FREQ 32.0 +#define SX127X_DIV_EXPONENT 19 // SX127x series common LoRa registers #define SX127X_REG_FIFO 0x00 @@ -563,6 +564,11 @@ class SX127x: public PhysicalLayer { */ int16_t begin(uint8_t chipVersion, uint8_t syncWord, uint8_t currentLimit, uint16_t preambleLength); + /*! + \brief Reset method. Will reset the chip to the default state using RST pin. + */ + void reset(); + /*! \brief Initialization method for FSK modem. Will be called with appropriate parameters when calling FSK initialization method from derived class. @@ -667,6 +673,11 @@ class SX127x: public PhysicalLayer { */ void setDio0Action(void (*func)(void)); + /*! + \brief Clears interrupt service routine to call when DIO0 activates. + */ + void clearDio0Action(); + /*! \brief Set interrupt service routine function to call when DIO1 activates. @@ -674,6 +685,11 @@ class SX127x: public PhysicalLayer { */ void setDio1Action(void (*func)(void)); + /*! + \brief Clears interrupt service routine to call when DIO1 activates. + */ + void clearDio1Action(); + /*! \brief Interrupt-driven binary transmit method. Will start transmitting arbitrary binary data up to 255 bytes long using %LoRa or up to 63 bytes using FSK modem. diff --git a/src/modules/XBee/XBee.cpp b/src/modules/XBee/XBee.cpp index 87a93a8d..1e5d7a15 100644 --- a/src/modules/XBee/XBee.cpp +++ b/src/modules/XBee/XBee.cpp @@ -10,7 +10,7 @@ XBee::XBee(Module* mod) { int16_t XBee::begin(long speed) { // set module properties _mod->baudrate = speed; - _mod->init(RADIOLIB_USE_UART, RADIOLIB_INT_1); + _mod->init(RADIOLIB_USE_UART); // reset module reset(); @@ -52,11 +52,10 @@ int16_t XBee::begin(long speed) { } void XBee::reset() { - pinMode(_mod->getInt1(), OUTPUT); - digitalWrite(_mod->getInt1(), LOW); + pinMode(_mod->getRst(), OUTPUT); + digitalWrite(_mod->getRst(), LOW); delayMicroseconds(200); - digitalWrite(_mod->getInt1(), HIGH); - pinMode(_mod->getInt1(), INPUT); + digitalWrite(_mod->getRst(), HIGH); } int16_t XBee::transmit(uint8_t* dest, const char* payload, uint8_t radius) { @@ -173,9 +172,7 @@ int16_t XBee::setPanId(uint8_t* panId) { // get response code int16_t state = readApiFrame(frameID, 4); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // confirm changes return(confirmChanges()); @@ -189,7 +186,10 @@ int16_t XBeeSerial::begin(long speed) { // set module properties _mod->AtLineFeed = "\r"; _mod->baudrate = speed; - _mod->init(RADIOLIB_USE_UART, RADIOLIB_INT_NONE); + _mod->init(RADIOLIB_USE_UART); + + // reset module + reset(); // empty UART buffer (garbage data) _mod->ATemptyBuffer(); @@ -216,11 +216,11 @@ int16_t XBeeSerial::begin(long speed) { } void XBeeSerial::reset() { - pinMode(_mod->getInt1(), OUTPUT); - digitalWrite(_mod->getInt1(), LOW); + pinMode(_mod->getRst(), OUTPUT); + digitalWrite(_mod->getRst(), LOW); delayMicroseconds(200); - digitalWrite(_mod->getInt1(), HIGH); - pinMode(_mod->getInt1(), INPUT); + digitalWrite(_mod->getRst(), HIGH); + pinMode(_mod->getRst(), INPUT); } int16_t XBeeSerial::setDestinationAddress(const char* destinationAddressHigh, const char* destinationAddressLow) { @@ -340,9 +340,7 @@ int16_t XBee::confirmChanges() { // get response code int16_t state = readApiFrame(frameID, 4); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // apply changes frameID = _frameID++; @@ -350,9 +348,6 @@ int16_t XBee::confirmChanges() { // get response code state = readApiFrame(frameID, 4); - if(state != ERR_NONE) { - return(state); - } return(state); } diff --git a/src/modules/nRF24/nRF24.cpp b/src/modules/nRF24/nRF24.cpp index 8fb205fc..3a91862d 100644 --- a/src/modules/nRF24/nRF24.cpp +++ b/src/modules/nRF24/nRF24.cpp @@ -1,6 +1,6 @@ #include "nRF24.h" -nRF24::nRF24(Module* mod) : PhysicalLayer(NRF24_CRYSTAL_FREQ, NRF24_DIV_EXPONENT, NRF24_MAX_PACKET_LENGTH) { +nRF24::nRF24(Module* mod) : PhysicalLayer(NRF24_FREQUENCY_STEP_SIZE, NRF24_MAX_PACKET_LENGTH) { _mod = mod; } @@ -8,18 +8,18 @@ int16_t nRF24::begin(int16_t freq, int16_t dataRate, int8_t power, uint8_t addrW // set module properties _mod->SPIreadCommand = NRF24_CMD_READ; _mod->SPIwriteCommand = NRF24_CMD_WRITE; - _mod->init(RADIOLIB_USE_SPI, RADIOLIB_INT_BOTH); + _mod->init(RADIOLIB_USE_SPI); - // override pin mode on INT0 (connected to nRF24 CE pin) - pinMode(_mod->getInt0(), OUTPUT); - digitalWrite(_mod->getInt0(), LOW); + // set pin mode on RST (connected to nRF24 CE pin) + Module::pinMode(_mod->getRst(), OUTPUT); + Module::digitalWrite(_mod->getRst(), LOW); // wait for minimum power-on reset duration delay(100); // check SPI connection int16_t val = _mod->SPIgetRegValue(NRF24_REG_SETUP_AW); - if(!((val >= 1) && (val <= 3))) { + if(!((val >= 0) && (val <= 3))) { RADIOLIB_DEBUG_PRINTLN(F("No nRF24 found!")); _mod->term(); return(ERR_CHIP_NOT_FOUND); @@ -27,39 +27,25 @@ int16_t nRF24::begin(int16_t freq, int16_t dataRate, int8_t power, uint8_t addrW // configure settings inaccessible by public API int16_t state = config(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set mode to standby state = standby(); - if(state != ERR_NONE) { - return(state); - } - + RADIOLIB_ASSERT(state); // set frequency state = setFrequency(freq); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set data rate state = setDataRate(dataRate); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set output power state = setOutputPower(power); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set address width state = setAddressWidth(addrWidth); - if(state != ERR_NONE) { - return(state); - } return(state); } @@ -72,7 +58,7 @@ int16_t nRF24::standby() { // make sure carrier output is disabled _mod->SPIsetRegValue(NRF24_REG_RF_SETUP, NRF24_CONT_WAVE_OFF, 7, 7); _mod->SPIsetRegValue(NRF24_REG_RF_SETUP, NRF24_PLL_LOCK_OFF, 4, 4); - digitalWrite(_mod->getInt0(), LOW); + digitalWrite(_mod->getRst(), LOW); // use standby-1 mode return(_mod->SPIsetRegValue(NRF24_REG_CONFIG, NRF24_POWER_UP, 1, 1)); @@ -81,13 +67,11 @@ int16_t nRF24::standby() { int16_t nRF24::transmit(uint8_t* data, size_t len, uint8_t addr) { // start transmission int16_t state = startTransmit(data, len, addr); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // wait until transmission is finished uint32_t start = micros(); - while(digitalRead(_mod->getInt1())) { + while(digitalRead(_mod->getIrq())) { // check maximum number of retransmits if(getStatus(NRF24_MAX_RT)) { standby(); @@ -112,13 +96,11 @@ int16_t nRF24::transmit(uint8_t* data, size_t len, uint8_t addr) { int16_t nRF24::receive(uint8_t* data, size_t len) { // start reception int16_t state = startReceive(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // wait for Rx_DataReady or timeout uint32_t start = micros(); - while(digitalRead(_mod->getInt1())) { + while(digitalRead(_mod->getIrq())) { // check timeout: 15 retries * 4ms (max Tx time as per datasheet) if(micros() - start >= 60000) { standby(); @@ -142,7 +124,7 @@ int16_t nRF24::transmitDirect(uint32_t frf) { int16_t state = _mod->SPIsetRegValue(NRF24_REG_CONFIG, NRF24_PTX, 0, 0); state |= _mod->SPIsetRegValue(NRF24_REG_RF_SETUP, NRF24_CONT_WAVE_ON, 7, 7); state |= _mod->SPIsetRegValue(NRF24_REG_RF_SETUP, NRF24_PLL_LOCK_ON, 4, 4); - digitalWrite(_mod->getInt0(), HIGH); + digitalWrite(_mod->getRst(), HIGH); return(state); } @@ -153,7 +135,7 @@ int16_t nRF24::receiveDirect() { } void nRF24::setIrqAction(void (*func)(void)) { - attachInterrupt(digitalPinToInterrupt(_mod->getInt1()), func, FALLING); + attachInterrupt(digitalPinToInterrupt(_mod->getIrq()), func, FALLING); } int16_t nRF24::startTransmit(uint8_t* data, size_t len, uint8_t addr) { @@ -167,9 +149,7 @@ int16_t nRF24::startTransmit(uint8_t* data, size_t len, uint8_t addr) { // set mode to standby int16_t state = standby(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // enable primary Tx mode state = _mod->SPIsetRegValue(NRF24_REG_CONFIG, NRF24_PTX, 0, 0); @@ -179,9 +159,7 @@ int16_t nRF24::startTransmit(uint8_t* data, size_t len, uint8_t addr) { // enable Tx_DataSent interrupt state |= _mod->SPIsetRegValue(NRF24_REG_CONFIG, NRF24_MASK_TX_DS_IRQ_ON, 5, 5); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // flush Tx FIFO SPItransfer(NRF24_CMD_FLUSH_TX); @@ -193,9 +171,9 @@ int16_t nRF24::startTransmit(uint8_t* data, size_t len, uint8_t addr) { SPIwriteTxPayload(data, len); // CE high to start transmitting - digitalWrite(_mod->getInt0(), HIGH); + digitalWrite(_mod->getRst(), HIGH); delayMicroseconds(10); - digitalWrite(_mod->getInt0(), LOW); + digitalWrite(_mod->getRst(), LOW); return(state); } @@ -203,28 +181,22 @@ int16_t nRF24::startTransmit(uint8_t* data, size_t len, uint8_t addr) { int16_t nRF24::startReceive() { // set mode to standby int16_t state = standby(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // enable primary Rx mode state = _mod->SPIsetRegValue(NRF24_REG_CONFIG, NRF24_PRX, 0, 0); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // enable Rx_DataReady interrupt clearIRQ(); - state |= _mod->SPIsetRegValue(NRF24_REG_CONFIG, NRF24_MASK_RX_DR_IRQ_ON, 6, 6); - if(state != ERR_NONE) { - return(state); - } + state = _mod->SPIsetRegValue(NRF24_REG_CONFIG, NRF24_MASK_RX_DR_IRQ_ON, 6, 6); + RADIOLIB_ASSERT(state); // flush Rx FIFO SPItransfer(NRF24_CMD_FLUSH_RX); // CE high to start receiving - digitalWrite(_mod->getInt0(), HIGH); + digitalWrite(_mod->getRst(), HIGH); // wait to enter Rx state delayMicroseconds(130); @@ -235,9 +207,7 @@ int16_t nRF24::startReceive() { int16_t nRF24::readData(uint8_t* data, size_t len) { // set mode to standby int16_t state = standby(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // get packet length size_t length = len; @@ -248,9 +218,6 @@ int16_t nRF24::readData(uint8_t* data, size_t len) { // read packet data SPIreadRxPayload(data, length); - // add terminating null - data[length] = 0; - // clear interrupt clearIRQ(); @@ -265,9 +232,7 @@ int16_t nRF24::setFrequency(int16_t freq) { // set mode to standby int16_t state = standby(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set frequency uint8_t freqRaw = freq - 2400; @@ -278,9 +243,7 @@ int16_t nRF24::setFrequency(int16_t freq) { int16_t nRF24::setDataRate(int16_t dataRate) { // set mode to standby int16_t state = standby(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set data rate if(dataRate == 250) { @@ -302,9 +265,7 @@ int16_t nRF24::setDataRate(int16_t dataRate) { int16_t nRF24::setOutputPower(int8_t power) { // set mode to standby int16_t state = standby(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // check allowed values uint8_t powerRaw = 0; @@ -333,12 +294,15 @@ int16_t nRF24::setOutputPower(int8_t power) { int16_t nRF24::setAddressWidth(uint8_t addrWidth) { // set mode to standby int16_t state = standby(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set address width switch(addrWidth) { + case 2: + // Even if marked as 'Illegal' on the datasheet this will work: + // http://travisgoodspeed.blogspot.com/2011/02/promiscuity-is-nrf24l01s-duty.html + state = _mod->SPIsetRegValue(NRF24_REG_SETUP_AW, NRF24_ADDRESS_2_BYTES, 1, 0); + break; case 3: state = _mod->SPIsetRegValue(NRF24_REG_SETUP_AW, NRF24_ADDRESS_3_BYTES, 1, 0); break; @@ -361,9 +325,7 @@ int16_t nRF24::setAddressWidth(uint8_t addrWidth) { int16_t nRF24::setTransmitPipe(uint8_t* addr) { // set mode to standby int16_t state = standby(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set transmit address _mod->SPIwriteRegisterBurst(NRF24_REG_TX_ADDR, addr, _addrWidth); @@ -377,9 +339,7 @@ int16_t nRF24::setTransmitPipe(uint8_t* addr) { int16_t nRF24::setReceivePipe(uint8_t pipeNum, uint8_t* addr) { // set mode to standby int16_t state = standby(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // write full pipe 0 - 1 address and enable the pipe switch(pipeNum) { @@ -401,9 +361,7 @@ int16_t nRF24::setReceivePipe(uint8_t pipeNum, uint8_t* addr) { int16_t nRF24::setReceivePipe(uint8_t pipeNum, uint8_t addrByte) { // set mode to standby int16_t state = standby(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // write unique pipe 2 - 5 address and enable the pipe switch(pipeNum) { @@ -433,9 +391,7 @@ int16_t nRF24::setReceivePipe(uint8_t pipeNum, uint8_t addrByte) { int16_t nRF24::disablePipe(uint8_t pipeNum) { // set mode to standby int16_t state = standby(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); switch(pipeNum) { case 0: @@ -481,6 +437,53 @@ size_t nRF24::getPacketLength(bool update) { return((size_t)length); } +int16_t nRF24::setCrcFiltering(bool crcOn) { + return _mod->SPIsetRegValue(NRF24_REG_CONFIG, crcOn ? NRF24_CRC_ON : NRF24_CRC_OFF, 3, 3); +} + +int16_t nRF24::setAutoAck(bool autoAckOn){ + return _mod->SPIsetRegValue(NRF24_REG_EN_AA, autoAckOn ? NRF24_AA_ALL_ON : NRF24_AA_ALL_OFF, 5, 0); +} + +int16_t nRF24::setAutoAck(uint8_t pipeNum, bool autoAckOn){ + switch(pipeNum) { + case 0: + return _mod->SPIsetRegValue(NRF24_REG_EN_AA, autoAckOn ? NRF24_AA_P0_ON : NRF24_AA_P0_OFF, 0, 0); + break; + case 1: + return _mod->SPIsetRegValue(NRF24_REG_EN_AA, autoAckOn ? NRF24_AA_P1_ON : NRF24_AA_P1_OFF, 1, 1); + break; + case 2: + return _mod->SPIsetRegValue(NRF24_REG_EN_AA, autoAckOn ? NRF24_AA_P2_ON : NRF24_AA_P2_OFF, 2, 2); + break; + case 3: + return _mod->SPIsetRegValue(NRF24_REG_EN_AA, autoAckOn ? NRF24_AA_P3_ON : NRF24_AA_P3_OFF, 3, 3); + break; + case 4: + return _mod->SPIsetRegValue(NRF24_REG_EN_AA, autoAckOn ? NRF24_AA_P4_ON : NRF24_AA_P4_OFF, 4, 4); + break; + case 5: + return _mod->SPIsetRegValue(NRF24_REG_EN_AA, autoAckOn ? NRF24_AA_P5_ON : NRF24_AA_P5_OFF, 5, 5); + break; + default: + return (ERR_INVALID_PIPE_NUMBER); + } +} + +int16_t nRF24::setDataShaping(float sh) { + // nRF24 is unable to set data shaping + // this method is implemented only for PhysicalLayer compatibility + (void)sh; + return(ERR_NONE); +} + +int16_t nRF24::setEncoding(uint8_t encoding) { + // nRF24 is unable to set encoding + // this method is implemented only for PhysicalLayer compatibility + (void)encoding; + return(ERR_NONE); +} + void nRF24::clearIRQ() { // clear status bits _mod->SPIsetRegValue(NRF24_REG_STATUS, NRF24_RX_DR | NRF24_TX_DS | NRF24_MAX_RT, 6, 4); @@ -492,27 +495,18 @@ void nRF24::clearIRQ() { int16_t nRF24::config() { // enable 16-bit CRC int16_t state = _mod->SPIsetRegValue(NRF24_REG_CONFIG, NRF24_CRC_ON | NRF24_CRC_16, 3, 2); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set 15 retries and delay 1500 (5*250) us _mod->SPIsetRegValue(NRF24_REG_SETUP_RETR, (5 << 4) | 5); - if(state != ERR_NONE) { - return(state); - } // set features: dynamic payload on, payload with ACK packets off, dynamic ACK off state = _mod->SPIsetRegValue(NRF24_REG_FEATURE, NRF24_DPL_ON | NRF24_ACK_PAY_OFF | NRF24_DYN_ACK_OFF, 2, 0); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // enable dynamic payloads state = _mod->SPIsetRegValue(NRF24_REG_DYNPD, NRF24_DPL_ALL_ON, 5, 0); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // reset IRQ clearIRQ(); diff --git a/src/modules/nRF24/nRF24.h b/src/modules/nRF24/nRF24.h index 1ca8a080..6eafc342 100644 --- a/src/modules/nRF24/nRF24.h +++ b/src/modules/nRF24/nRF24.h @@ -6,9 +6,8 @@ #include "../../protocols/PhysicalLayer/PhysicalLayer.h" -// nRF24 physical layer properties (dummy only) -#define NRF24_CRYSTAL_FREQ 1.0 -#define NRF24_DIV_EXPONENT 0 +// nRF24 physical layer properties +#define NRF24_FREQUENCY_STEP_SIZE 1000000.0 #define NRF24_MAX_PACKET_LENGTH 32 // nRF24 SPI commands @@ -69,6 +68,8 @@ #define NRF24_PRX 0b00000001 // 0 0 enable primary Rx // NRF24_REG_EN_AA +#define NRF24_AA_ALL_OFF 0b00000000 // 5 0 auto-ACK on all pipes: disabled +#define NRF24_AA_ALL_ON 0b00111111 // 5 0 enabled (default) #define NRF24_AA_P5_OFF 0b00000000 // 5 5 auto-ACK on pipe 5: disabled #define NRF24_AA_P5_ON 0b00100000 // 5 5 enabled (default) #define NRF24_AA_P4_OFF 0b00000000 // 4 4 auto-ACK on pipe 4: disabled @@ -97,7 +98,8 @@ #define NRF24_P0_ON 0b00000001 // 0 0 enabled (default) // NRF24_REG_SETUP_AW -#define NRF24_ADDRESS_3_BYTES 0b00000001 // 1 0 address width: 3 bytes +#define NRF24_ADDRESS_2_BYTES 0b00000000 // 1 0 address width: 2 bytes +#define NRF24_ADDRESS_3_BYTES 0b00000001 // 1 0 3 bytes #define NRF24_ADDRESS_4_BYTES 0b00000010 // 1 0 4 bytes #define NRF24_ADDRESS_5_BYTES 0b00000011 // 1 0 5 bytes (default) @@ -408,6 +410,54 @@ class nRF24: public PhysicalLayer { */ size_t getPacketLength(bool update = true); + + /*! + \brief Enable CRC filtering and generation. + + \param crcOn Set or unset CRC check. + + \returns \ref status_codes + */ + int16_t setCrcFiltering(bool crcOn = true); + + /*! + \brief Enable or disable auto-acknowlede packets on all pipes + + \param autoAckOn Enable (true) or disable (false) auto-acks. + + \returns \ref status_codes + */ + int16_t setAutoAck(bool autoAckOn = true); + + /*! + \brief Enable or disable auto-acknowlede packets on given pipe. + + \param pipeNum Number of pipe to which enable / disable auto-acks. + + \param autoAckOn Enable (true) or disable (false) auto-acks. + + \returns \ref status_codes + */ + int16_t setAutoAck(uint8_t pipeNum, bool autoAckOn = true); + + /*! + \brief Dummy data shaping configuration method, to ensure PhysicalLayer compatibility. + + \param sh Ignored. + + \returns \ref status_codes + */ + int16_t setDataShaping(float sh); + + /*! + \brief Dummy encoding configuration method, to ensure PhysicalLayer compatibility. + + \param sh Ignored. + + \returns \ref status_codes + */ + int16_t setEncoding(uint8_t encoding); + #ifndef RADIOLIB_GODMODE private: #endif diff --git a/src/protocols/AX25/AX25.cpp b/src/protocols/AX25/AX25.cpp new file mode 100644 index 00000000..7c6a7933 --- /dev/null +++ b/src/protocols/AX25/AX25.cpp @@ -0,0 +1,382 @@ +#include "AX25.h" + +AX25Frame::AX25Frame(const char* destCallsign, uint8_t destSSID, const char* srcCallsign, uint8_t srcSSID, uint8_t control) +: AX25Frame(destCallsign, destSSID, srcCallsign, srcSSID, control, 0, NULL, 0) { + +} + +AX25Frame::AX25Frame(const char* destCallsign, uint8_t destSSID, const char* srcCallsign, uint8_t srcSSID, uint8_t control, uint8_t protocolID, const char* info) + : AX25Frame(destCallsign, destSSID, srcCallsign, srcSSID, control, protocolID, (uint8_t*)info, strlen(info)) { + +} + +AX25Frame::AX25Frame(const char* destCallsign, uint8_t destSSID, const char* srcCallsign, uint8_t srcSSID, uint8_t control, uint8_t protocolID, uint8_t* info, uint16_t infoLen) { + // destination callsign/SSID + memcpy(this->destCallsign, destCallsign, strlen(destCallsign)); + this->destCallsign[strlen(destCallsign)] = '\0'; + this->destSSID = destSSID; + + // source callsign/SSID + memcpy(this->srcCallsign, srcCallsign, strlen(srcCallsign)); + this->srcCallsign[strlen(srcCallsign)] = '\0'; + this->srcSSID = srcSSID; + + // set repeaters + this->numRepeaters = 0; + #ifndef RADIOLIB_STATIC_ONLY + this->repeaterCallsigns = NULL; + this->repeaterSSIDs = NULL; + #endif + + // control field + this->control = control; + + // sequence numbers + this->rcvSeqNumber = 0; + this->sendSeqNumber = 0; + + // PID field + this->protocolID = protocolID; + + // info field + this->infoLen = infoLen; + if(infoLen > 0) { + #ifndef RADIOLIB_STATIC_ONLY + this->info = new uint8_t[infoLen]; + #endif + memcpy(this->info, info, infoLen); + } +} + +AX25Frame::~AX25Frame() { + #ifndef RADIOLIB_STATIC_ONLY + // deallocate info field + if(infoLen > 0) { + delete[] this->info; + } + + // deallocate repeaters + if(this->numRepeaters > 0) { + for(uint8_t i = 0; i < this->numRepeaters; i++) { + delete[] this->repeaterCallsigns[i]; + } + delete[] this->repeaterCallsigns; + delete[] this->repeaterSSIDs; + } + #endif +} + +int16_t AX25Frame::setRepeaters(char** repeaterCallsigns, uint8_t* repeaterSSIDs, uint8_t numRepeaters) { + // check number of repeaters + if((numRepeaters < 1) || (numRepeaters > 8)) { + return(ERR_INVALID_NUM_REPEATERS); + } + + // check repeater configuration + if(!(((repeaterCallsigns == NULL) && (repeaterSSIDs == NULL) && (numRepeaters == 0)) || + ((repeaterCallsigns != NULL) && (repeaterSSIDs != NULL) && (numRepeaters != 0)))) { + return(ERR_INVALID_NUM_REPEATERS); + } + for(uint16_t i = 0; i < numRepeaters; i++) { + if(strlen(repeaterCallsigns[i]) > AX25_MAX_CALLSIGN_LEN) { + return(ERR_INVALID_REPEATER_CALLSIGN); + } + } + + // create buffers + #ifndef RADIOLIB_STATIC_ONLY + this->repeaterCallsigns = new char*[numRepeaters]; + for(uint8_t i = 0; i < numRepeaters; i++) { + this->repeaterCallsigns[i] = new char[strlen(repeaterCallsigns[i])]; + } + this->repeaterSSIDs = new uint8_t[numRepeaters]; + #endif + + // copy data + this->numRepeaters = numRepeaters; + for(uint8_t i = 0; i < numRepeaters; i++) { + memcpy(this->repeaterCallsigns[i], repeaterCallsigns[i], strlen(repeaterCallsigns[i])); + } + memcpy(this->repeaterSSIDs, repeaterSSIDs, numRepeaters); + + return(ERR_NONE); +} + +void AX25Frame::setRecvSequence(uint8_t seqNumber) { + this->rcvSeqNumber = seqNumber; +} + +void AX25Frame::setSendSequence(uint8_t seqNumber) { + this->sendSeqNumber = seqNumber; +} + +AX25Client::AX25Client(PhysicalLayer* phy) { + _phy = phy; +} + +int16_t AX25Client::begin(const char* srcCallsign, uint8_t srcSSID, uint8_t preambleLen) { + // set source SSID + _srcSSID = srcSSID; + + // check source callsign length (6 characters max) + if(strlen(srcCallsign) > AX25_MAX_CALLSIGN_LEN) { + return(ERR_INVALID_CALLSIGN); + } + + // copy callsign + memcpy(_srcCallsign, srcCallsign, strlen(srcCallsign)); + _srcCallsign[strlen(srcCallsign)] = '\0'; + + // save preamble length + _preambleLen = preambleLen; + + // disable physical layer data shaping and set encoding to NRZ + int16_t state = _phy->setDataShaping(0.0); + RADIOLIB_ASSERT(state); + + state = _phy->setEncoding(0); + return(state); +} + +int16_t AX25Client::transmit(const char* str, const char* destCallsign, uint8_t destSSID) { + // create control field + uint8_t controlField = AX25_CONTROL_U_UNNUMBERED_INFORMATION | AX25_CONTROL_POLL_FINAL_DISABLED | AX25_CONTROL_UNNUMBERED_FRAME; + + // build the frame + AX25Frame frame(destCallsign, destSSID, _srcCallsign, _srcSSID, controlField, AX25_PID_NO_LAYER_3, (uint8_t*)str, strlen(str)); + + // send Unnumbered Information frame + return(sendFrame(&frame)); +} + +int16_t AX25Client::sendFrame(AX25Frame* frame) { + // check destination callsign length (6 characters max) + if(strlen(frame->destCallsign) > AX25_MAX_CALLSIGN_LEN) { + return(ERR_INVALID_CALLSIGN); + } + + // check repeater configuration + #ifndef RADIOLIB_STATIC_ONLY + if(!(((frame->repeaterCallsigns == NULL) && (frame->repeaterSSIDs == NULL) && (frame->numRepeaters == 0)) || + ((frame->repeaterCallsigns != NULL) && (frame->repeaterSSIDs != NULL) && (frame->numRepeaters != 0)))) { + return(ERR_INVALID_NUM_REPEATERS); + } + for(uint16_t i = 0; i < frame->numRepeaters; i++) { + if(strlen(frame->repeaterCallsigns[i]) > AX25_MAX_CALLSIGN_LEN) { + return(ERR_INVALID_REPEATER_CALLSIGN); + } + } + #endif + + // calculate frame length without FCS (destination address, source address, repeater addresses, control, PID, info) + size_t frameBuffLen = ((2 + frame->numRepeaters)*(AX25_MAX_CALLSIGN_LEN + 1)) + 1 + 1 + frame->infoLen; + // create frame buffer without preamble, start or stop flags + #ifndef RADIOLIB_STATIC_ONLY + uint8_t* frameBuff = new uint8_t[frameBuffLen + 2]; + #else + uint8_t frameBuff[RADIOLIB_STATIC_ARRAY_SIZE]; + #endif + uint8_t* frameBuffPtr = frameBuff; + + // set destination callsign - all address field bytes are shifted by one bit to make room for HDLC address extension bit + memset(frameBuffPtr, ' ' << 1, AX25_MAX_CALLSIGN_LEN); + for(uint8_t i = 0; i < strlen(frame->destCallsign); i++) { + *(frameBuffPtr + i) = frame->destCallsign[i] << 1; + } + frameBuffPtr += AX25_MAX_CALLSIGN_LEN; + + // set destination SSID + *(frameBuffPtr++) = AX25_SSID_COMMAND_DEST | AX25_SSID_RESERVED_BITS | (frame->destSSID & 0x0F) << 1 | AX25_SSID_HDLC_EXTENSION_CONTINUE; + + // set source callsign - all address field bytes are shifted by one bit to make room for HDLC address extension bit + memset(frameBuffPtr, ' ' << 1, AX25_MAX_CALLSIGN_LEN); + for(uint8_t i = 0; i < strlen(frame->srcCallsign); i++) { + *(frameBuffPtr + i) = frame->srcCallsign[i] << 1; + } + frameBuffPtr += AX25_MAX_CALLSIGN_LEN; + + // set source SSID + *(frameBuffPtr++) = AX25_SSID_COMMAND_SOURCE | AX25_SSID_RESERVED_BITS | (frame->srcSSID & 0x0F) << 1 | AX25_SSID_HDLC_EXTENSION_END; + + // set repeater callsigns + for(uint16_t i = 0; i < frame->numRepeaters; i++) { + memset(frameBuffPtr, ' ' << 1, AX25_MAX_CALLSIGN_LEN); + for(uint8_t j = 0; j < strlen(frame->repeaterCallsigns[i]); j++) { + *(frameBuffPtr + j) = frame->repeaterCallsigns[i][j] << 1; + } + frameBuffPtr += AX25_MAX_CALLSIGN_LEN; + *(frameBuffPtr++) = AX25_SSID_HAS_NOT_BEEN_REPEATED | AX25_SSID_RESERVED_BITS | (frame->repeaterSSIDs[i] & 0x0F) << 1 | AX25_SSID_HDLC_EXTENSION_CONTINUE; + } + + // set HDLC extension end bit + *frameBuffPtr |= AX25_SSID_HDLC_EXTENSION_END; + + // set sequence numbers of the frames that have it + uint8_t controlField = frame->control; + if((frame->control & 0x01) == 0) { + // information frame, set both sequence numbers + controlField |= frame->rcvSeqNumber << 5; + controlField |= frame->sendSeqNumber << 1; + } else if((frame->control & 0x02) == 0) { + // supervisory frame, set only receive sequence number + controlField |= frame->rcvSeqNumber << 5; + } + + // set control field + *(frameBuffPtr++) = controlField; + + // set PID field of the frames that have it + if(frame->protocolID != 0x00) { + *(frameBuffPtr++) = frame->protocolID; + } + + // set info field of the frames that have it + if(frame->infoLen > 0) { + memcpy(frameBuffPtr, frame->info, frame->infoLen); + frameBuffPtr += frame->infoLen; + } + + // flip bit order + for(size_t i = 0; i < frameBuffLen; i++) { + frameBuff[i] = flipBits(frameBuff[i]); + } + + // calculate FCS + uint16_t fcs = getFrameCheckSequence(frameBuff, frameBuffLen); + *(frameBuffPtr++) = (uint8_t)((fcs >> 8) & 0xFF); + *(frameBuffPtr++) = (uint8_t)(fcs & 0xFF); + + // prepare buffer for the final frame (stuffed, with added preamble + flags and NRZI-encoded) + #ifndef RADIOLIB_STATIC_ONLY + // worst-case scenario: sequence of 1s, will have 120% of the original length, stuffed frame also includes both flags + uint8_t* stuffedFrameBuff = new uint8_t[_preambleLen + 1 + (6*frameBuffLen)/5 + 2]; + #else + uint8_t stuffedFrameBuff[RADIOLIB_STATIC_ARRAY_SIZE]; + #endif + + // stuff bits (skip preamble and both flags) + uint16_t stuffedFrameBuffLenBits = 8*(_preambleLen + 1); + uint8_t count = 0; + for(uint16_t i = 0; i < frameBuffLen + 2; i++) { + for(int8_t shift = 7; shift >= 0; shift--) { + uint16_t stuffedFrameBuffPos = stuffedFrameBuffLenBits + 7 - 2*(stuffedFrameBuffLenBits%8); + if((frameBuff[i] >> shift) & 0x01) { + // copy 1 and increment counter + SET_BIT_IN_ARRAY(stuffedFrameBuff, stuffedFrameBuffPos); + stuffedFrameBuffLenBits++; + count++; + + // check 5 consecutive 1s + if(count == 5) { + // get the new position in stuffed frame + stuffedFrameBuffPos = stuffedFrameBuffLenBits + 7 - 2*(stuffedFrameBuffLenBits%8); + + // insert 0 and reset counter + CLEAR_BIT_IN_ARRAY(stuffedFrameBuff, stuffedFrameBuffPos); + stuffedFrameBuffLenBits++; + count = 0; + } + + } else { + // copy 0 and reset counter + CLEAR_BIT_IN_ARRAY(stuffedFrameBuff, stuffedFrameBuffPos); + stuffedFrameBuffLenBits++; + count = 0; + } + + } + } + + // deallocate memory + #ifndef RADIOLIB_STATIC_ONLY + delete[] frameBuff; + #endif + + // set preamble bytes and start flag field + for(uint16_t i = 0; i < _preambleLen + 1; i++) { + stuffedFrameBuff[i] = AX25_FLAG; + } + + // get stuffed frame length in bytes + size_t stuffedFrameBuffLen = stuffedFrameBuffLenBits/8 + 1; + uint8_t trailingLen = stuffedFrameBuffLenBits % 8; + + // set end flag field (may be split into two bytes due to misalignment caused by extra stuffing bits) + if(trailingLen != 0) { + stuffedFrameBuffLen++; + stuffedFrameBuff[stuffedFrameBuffLen - 2] = AX25_FLAG >> trailingLen; + stuffedFrameBuff[stuffedFrameBuffLen - 1] = AX25_FLAG << (8 - trailingLen); + } else { + stuffedFrameBuff[stuffedFrameBuffLen - 1] = AX25_FLAG; + } + + // convert to NRZI + for(size_t i = _preambleLen + 1; i < stuffedFrameBuffLen*8; i++) { + size_t currBitPos = i + 7 - 2*(i%8); + size_t prevBitPos = (i - 1) + 7 - 2*((i - 1)%8); + if(TEST_BIT_IN_ARRAY(stuffedFrameBuff, currBitPos)) { + // bit is 1, no change, copy previous bit + if(TEST_BIT_IN_ARRAY(stuffedFrameBuff, prevBitPos)) { + SET_BIT_IN_ARRAY(stuffedFrameBuff, currBitPos); + } else { + CLEAR_BIT_IN_ARRAY(stuffedFrameBuff, currBitPos); + } + + } else { + // bit is 0, transition, copy inversion of the previous bit + if(TEST_BIT_IN_ARRAY(stuffedFrameBuff, prevBitPos)) { + CLEAR_BIT_IN_ARRAY(stuffedFrameBuff, currBitPos); + } else { + SET_BIT_IN_ARRAY(stuffedFrameBuff, currBitPos); + } + } + } + + // transmit + int16_t state = _phy->transmit(stuffedFrameBuff, stuffedFrameBuffLen); + + // deallocate memory + #ifndef RADIOLIB_STATIC_ONLY + delete[] stuffedFrameBuff; + #endif + + return(state); +} + +/* + CCITT CRC implementation based on https://github.com/kicksat/ax25 + + Licensed under Creative Commons Attribution-ShareAlike 4.0 International + https://creativecommons.org/licenses/by-sa/4.0/ +*/ +uint16_t AX25Client::getFrameCheckSequence(uint8_t* buff, size_t len) { + uint8_t outBit = 0; + uint16_t mask = 0x0000; + uint16_t shiftReg = CRC_CCITT_INIT; + + for(size_t i = 0; i < len; i++) { + for(uint8_t b = 0x80; b > 0x00; b /= 2) { + outBit = (shiftReg & 0x01) ? 0x01 : 0x00; + shiftReg >>= 1; + mask = XOR((buff[i] & b), outBit) ? CRC_CCITT_POLY_REVERSED : 0x0000; + shiftReg ^= mask; + } + } + + return(flipBits16(~shiftReg)); +} + +uint8_t AX25Client::flipBits(uint8_t b) { + b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; + b = (b & 0xCC) >> 2 | (b & 0x33) << 2; + b = (b & 0xAA) >> 1 | (b & 0x55) << 1; + return b; +} + +uint16_t AX25Client::flipBits16(uint16_t i) { + i = (i & 0xFF00) >> 8 | (i & 0x00FF) << 8; + i = (i & 0xF0F0) >> 4 | (i & 0x0F0F) << 4; + i = (i & 0xCCCC) >> 2 | (i & 0x3333) << 2; + i = (i & 0xAAAA) >> 1 | (i & 0x5555) << 1; + return i; +} diff --git a/src/protocols/AX25/AX25.h b/src/protocols/AX25/AX25.h new file mode 100644 index 00000000..bfe322af --- /dev/null +++ b/src/protocols/AX25/AX25.h @@ -0,0 +1,314 @@ +#ifndef _RADIOLIB_AX25_H +#define _RADIOLIB_AX25_H + +#include "../../TypeDef.h" +#include "../PhysicalLayer/PhysicalLayer.h" + +// macros to access bits in byte array, from http://www.mathcs.emory.edu/~cheung/Courses/255/Syllabus/1-C-intro/bit-array.html +#define SET_BIT_IN_ARRAY(A, k) ( A[(k/8)] |= (1 << (k%8)) ) +#define CLEAR_BIT_IN_ARRAY(A, k) ( A[(k/8)] &= ~(1 << (k%8)) ) +#define TEST_BIT_IN_ARRAY(A, k) ( A[(k/8)] & (1 << (k%8)) ) +#define GET_BIT_IN_ARRAY(A, k) ( (A[(k/8)] & (1 << (k%8))) ? 1 : 0 ) + +// CRC-CCITT calculation macros +#define XOR(A, B) ( ((A) || (B)) && !((A) && (B)) ) +#define CRC_CCITT_POLY 0x1021 // generator polynomial +#define CRC_CCITT_POLY_REVERSED 0x8408 // CRC_CCITT_POLY in reversed bit order +#define CRC_CCITT_INIT 0xFFFF // initial value + +// maximum callsign length in bytes +#define AX25_MAX_CALLSIGN_LEN 6 + +// flag field MSB LSB DESCRIPTION +#define AX25_FLAG 0b01111110 // 7 0 AX.25 frame start/end flag + +// address field +#define AX25_SSID_COMMAND_DEST 0b10000000 // 7 7 frame type: command (set in destination SSID) +#define AX25_SSID_COMMAND_SOURCE 0b00000000 // 7 7 command (set in source SSID) +#define AX25_SSID_RESPONSE_DEST 0b00000000 // 7 7 response (set in destination SSID) +#define AX25_SSID_RESPONSE_SOURCE 0b10000000 // 7 7 response (set in source SSID) +#define AX25_SSID_HAS_NOT_BEEN_REPEATED 0b00000000 // 7 7 not repeated yet (set in repeater SSID) +#define AX25_SSID_HAS_BEEN_REPEATED 0b10000000 // 7 7 repeated (set in repeater SSID) +#define AX25_SSID_RESERVED_BITS 0b01100000 // 6 5 reserved bits in SSID +#define AX25_SSID_HDLC_EXTENSION_CONTINUE 0b00000000 // 0 0 HDLC extension bit: next octet contains more address information +#define AX25_SSID_HDLC_EXTENSION_END 0b00000001 // 0 0 address field end + +// control field +#define AX25_CONTROL_U_SET_ASYNC_BAL_MODE 0b01101100 // 7 2 U frame type: set asynchronous balanced mode (connect request) +#define AX25_CONTROL_U_SET_ASYNC_BAL_MODE_EXT 0b00101100 // 7 2 set asynchronous balanced mode extended (connect request with module 128) +#define AX25_CONTROL_U_DISCONNECT 0b01000000 // 7 2 disconnect request +#define AX25_CONTROL_U_DISCONNECT_MODE 0b00001100 // 7 2 disconnect mode (system busy or disconnected) +#define AX25_CONTROL_U_UNNUMBERED_ACK 0b01100000 // 7 2 unnumbered acknowledge +#define AX25_CONTROL_U_FRAME_REJECT 0b10000100 // 7 2 frame reject +#define AX25_CONTROL_U_UNNUMBERED_INFORMATION 0b00000000 // 7 2 unnumbered information +#define AX25_CONTROL_U_EXHANGE_IDENTIFICATION 0b10101100 // 7 2 exchange ID +#define AX25_CONTROL_U_TEST 0b11100000 // 7 2 test +#define AX25_CONTROL_POLL_FINAL_ENABLED 0b00010000 // 4 4 control field poll/final bit: enabled +#define AX25_CONTROL_POLL_FINAL_DISABLED 0b00000000 // 4 4 disabled +#define AX25_CONTROL_S_RECEIVE_READY 0b00000000 // 3 2 S frame type: receive ready (system ready to receive) +#define AX25_CONTROL_S_RECEIVE_NOT_READY 0b00000100 // 3 2 receive not ready (TNC buffer full) +#define AX25_CONTROL_S_REJECT 0b00001000 // 3 2 reject (out of sequence or duplicate) +#define AX25_CONTROL_S_SELECTIVE_REJECT 0b00001100 // 3 2 selective reject (single frame repeat request) +#define AX25_CONTROL_INFORMATION_FRAME 0b00000000 // 0 0 frame type: information (I frame) +#define AX25_CONTROL_SUPERVISORY_FRAME 0b00000001 // 1 0 supervisory (S frame) +#define AX25_CONTROL_UNNUMBERED_FRAME 0b00000011 // 1 0 unnumbered (U frame) + +// protocol identifier field +#define AX25_PID_ISO_8208 0x01 +#define AX25_PID_TCP_IP_COMPRESSED 0x06 +#define AX25_PID_TCP_IP_UNCOMPRESSED 0x07 +#define AX25_PID_SEGMENTATION_FRAGMENT 0x08 +#define AX25_PID_TEXNET_DATAGRAM_PROTOCOL 0xC3 +#define AX25_PID_LINK_QUALITY_PROTOCOL 0xC4 +#define AX25_PID_APPLETALK 0xCA +#define AX25_PID_APPLETALK_ARP 0xCB +#define AX25_PID_ARPA_INTERNET_PROTOCOL 0xCC +#define AX25_PID_ARPA_ADDRESS_RESOLUTION 0xCD +#define AX25_PID_FLEXNET 0xCE +#define AX25_PID_NET_ROM 0xCF +#define AX25_PID_NO_LAYER_3 0xF0 +#define AX25_PID_ESCAPE_CHARACTER 0xFF + +/*! + \class AX25Frame + + \brief Abstraction of AX.25 frame format. +*/ +class AX25Frame { + public: + /*! + \brief Callsign of the destination station. + */ + char destCallsign[AX25_MAX_CALLSIGN_LEN + 1]; + + /*! + \brief SSID of the destination station. + */ + uint8_t destSSID; + + /*! + \brief Callsign of the source station. + */ + char srcCallsign[AX25_MAX_CALLSIGN_LEN + 1]; + + /*! + \brief SSID of the source station. + */ + uint8_t srcSSID; + + /*! + \brief Number of repeaters to be used. + */ + uint8_t numRepeaters; + + /*! + \brief The control field. + */ + uint8_t control; + + /*! + \brief The protocol identifier (PID) field. + */ + uint8_t protocolID; + + /*! + \brief Number of bytes in the information field. + */ + uint16_t infoLen; + + /*! + \brief Receive sequence number. + */ + uint8_t rcvSeqNumber; + + /*! + \brief Send sequence number. + */ + uint16_t sendSeqNumber; + + #ifndef RADIOLIB_STATIC_ONLY + /*! + \brief The info field. + */ + uint8_t* info; + + /*! + \brief Array of repeater callsigns. + */ + char** repeaterCallsigns; + + /*! + \brief Array of repeater SSIDs. + */ + uint8_t* repeaterSSIDs; + #else + /*! + \brief The info field. + */ + uint8_t info[RADIOLIB_STATIC_ARRAY_SIZE]; + + /*! + \brief Array of repeater callsigns. + */ + char repeaterCallsigns[8][AX25_MAX_CALLSIGN_LEN + 1]; + + /*! + \brief Array of repeater SSIDs. + */ + uint8_t repeaterSSIDs[8]; + #endif + + /*! + \brief Overloaded constructor, for frames without info field. + + \param destCallsign Callsign of the destination station. + + \param destSSID SSID of the destination station. + + \param srcCallsign Callsign of the source station. + + \param srcSSID SSID of the source station. + + \param control The control field. + */ + AX25Frame(const char* destCallsign, uint8_t destSSID, const char* srcCallsign, uint8_t srcSSID, uint8_t control); + + /*! + \brief Overloaded constructor, for frames with C-string info field. + + \param destCallsign Callsign of the destination station. + + \param destSSID SSID of the destination station. + + \param srcCallsign Callsign of the source station. + + \param srcSSID SSID of the source station. + + \param control The control field. + + \param protocolID The protocol identifier (PID) field. Set to zero if the frame doesn't have this field. + + \param info Information field, in the form of null-terminated C-string. + */ + AX25Frame(const char* destCallsign, uint8_t destSSID, const char* srcCallsign, uint8_t srcSSID, uint8_t control, uint8_t protocolID, const char* info); + + /*! + \brief Default constructor. + + \param destCallsign Callsign of the destination station. + + \param destSSID SSID of the destination station. + + \param srcCallsign Callsign of the source station. + + \param srcSSID SSID of the source station. + + \param control The control field. + + \param protocolID The protocol identifier (PID) field. Set to zero if the frame doesn't have this field. + + \param info Information field, in the form of arbitrary binary buffer. + + \param infoLen Number of bytes in the information field. + */ + AX25Frame(const char* destCallsign, uint8_t destSSID, const char* srcCallsign, uint8_t srcSSID, uint8_t control, uint8_t protocolID, uint8_t* info, uint16_t infoLen); + + /*! + \brief Default destructor. + */ + ~AX25Frame(); + + /*! + \brief Method to set the repeater callsigns and SSIDs. + + \param repeaterCallsigns Array of repeater callsigns in the form of null-terminated C-strings. + + \param repeaterSSIDs Array of repeater SSIDs. + + \param numRepeaters Number of repeaters, maximum is 8. + + \returns \ref status_codes + */ + int16_t setRepeaters(char** repeaterCallsigns, uint8_t* repeaterSSIDs, uint8_t numRepeaters); + + /*! + \brief Method to set receive sequence number. + + \param seqNumber Sequence number to set, 0 to 7. + */ + void setRecvSequence(uint8_t seqNumber); + + /*! + \brief Method to set send sequence number. + + \param seqNumber Sequence number to set, 0 to 7. + */ + void setSendSequence(uint8_t seqNumber); +}; + +/*! + \class AX25Client + + \brief Client for AX25 communication. +*/ +class AX25Client { + public: + /*! + \brief Default constructor. + + \param phy Pointer to the wireless module providing PhysicalLayer communication. + */ + AX25Client(PhysicalLayer* phy); + + // basic methods + + /*! + \brief Initialization method. + + \param srcCallsign Callsign of the source station. + + \param srcSSID 4-bit SSID of the source station (in case there are more stations with the same callsign). Defaults to 0. + + \param preambleLen Number of "preamble" bytes (AX25_FLAG) sent ahead of the actual AX.25 frame. Does not include the first AX25_FLAG byte, which is considered part of the frame. Defaults to 8. + + \returns \ref status_codes + */ + int16_t begin(const char* srcCallsign, uint8_t srcSSID = 0x00, uint8_t preambleLen = 8); + + /*! + \brief Transmit unnumbered information (UI) frame. + + \param str Data to be sent. + + \param destCallsign Callsign of the destination station. + + \param destSSID 4-bit SSID of the destination station (in case there are more stations with the same callsign). Defaults to 0. + + \returns \ref status_codes + */ + int16_t transmit(const char* str, const char* destCallsign, uint8_t destSSID = 0x00); + + /*! + \brief Transmit arbitrary AX.25 frame. + + \param frame Frame to be sent. + + \returns \ref status_codes + */ + int16_t sendFrame(AX25Frame* frame); + +#ifndef RADIOLIB_GODMODE + private: +#endif + PhysicalLayer* _phy; + + char _srcCallsign[AX25_MAX_CALLSIGN_LEN + 1]; + uint8_t _srcSSID; + uint16_t _preambleLen; + + uint16_t getFrameCheckSequence(uint8_t* buff, size_t len); + uint8_t flipBits(uint8_t b); + uint16_t flipBits16(uint16_t i); +}; + +#endif diff --git a/src/protocols/Morse/Morse.cpp b/src/protocols/Morse/Morse.cpp index 83137abf..6b3e0c16 100644 --- a/src/protocols/Morse/Morse.cpp +++ b/src/protocols/Morse/Morse.cpp @@ -1,80 +1,12 @@ #include "Morse.h" -// structure to save data about character Morse code -/*! - \cond RADIOLIB_DOXYGEN_HIDDEN -*/ -struct Morse_t { - char c; // ASCII character - char m[7]; // Morse code representation -}; -/*! - \endcond -*/ - -// array of all Morse code characters -const Morse_t MorseTable[MORSE_LENGTH] PROGMEM = { - {'A', ".-"}, - {'B',"-..."}, - {'C', "-.-."}, - {'D',"-.."}, - {'E',"."}, - {'F',"..-."}, - {'G',"--."}, - {'H',"...."}, - {'I',".."}, - {'J',".---"}, - {'K',"-.-"}, - {'L',".-.."}, - {'M',"--"}, - {'N',"-."}, - {'O',"---"}, - {'P',".--."}, - {'Q',"--.-"}, - {'R',".-."}, - {'S',"..."}, - {'T',"-"}, - {'U',"..-"}, - {'V',"...-"}, - {'W',".--"}, - {'X',"-..-"}, - {'Y',"-.--"}, - {'Z',"--.."}, - {'1',".----"}, - {'2',"..---"}, - {'3',"...--"}, - {'4',"....-"}, - {'5',"....."}, - {'6',"-...."}, - {'7',"--..."}, - {'8',"---.."}, - {'9',"----."}, - {'0',"-----"}, - {'.',".-.-.-"}, - {',',"--..--"}, - {':',"---..."}, - {'?',"..--.."}, - {'\'',".----."}, - {'-',"-....-"}, - {'/',"-..-."}, - {'(',"-.--."}, - {')',"-.--.-"}, - {'\"',".-..-."}, - {'=',"-...-"}, - {'+',".-.-."}, - {'@',".--.-."}, - {' ',"_"}, // space is used to separate words - {0x01,"-.-.-"}, // ASCII SOH (start of heading) is used as alias for start signal - {0x02,".-.-."} // ASCII EOT (end of transmission) is used as alias for stop signal -}; - MorseClient::MorseClient(PhysicalLayer* phy) { _phy = phy; } int16_t MorseClient::begin(float base, uint8_t speed) { // calculate 24-bit frequency - _base = (base * (uint32_t(1) << _phy->getDivExponent())) / _phy->getCrystalFreq(); + _base = (base * 1000000.0) / _phy->getFreqStep(); // calculate dot length (assumes PARIS as typical word) _dotLength = 1200 / speed; @@ -86,7 +18,7 @@ int16_t MorseClient::begin(float base, uint8_t speed) { } size_t MorseClient::startSignal() { - return(MorseClient::write(0x01)); + return(MorseClient::write('_')); } size_t MorseClient::write(const char* str) { @@ -106,52 +38,55 @@ size_t MorseClient::write(uint8_t* buff, size_t len) { } size_t MorseClient::write(uint8_t b) { - // find the correct Morse code in array - Morse_t mc; - bool found = false; - for(uint8_t pos = 0; pos < MORSE_LENGTH; pos++) { - memcpy_P(&mc, &MorseTable[pos], sizeof(Morse_t)); - if(mc.c == toupper(b)) { - found = true; - break; - } + // check unprintable ASCII characters and boundaries + if((b < ' ') || (b == 0x60) || (b > 'z')) { + return(0); } - // check if the requested code was found in the array - if(found) { - RADIOLIB_DEBUG_PRINT(mc.c); - RADIOLIB_DEBUG_PRINT('\t'); - RADIOLIB_DEBUG_PRINTLN(mc.m); - - // iterate over Morse code representation and output appropriate tones - for(uint8_t i = 0; i < strlen(mc.m); i++) { - switch(mc.m[i]) { - case '.': - _phy->transmitDirect(_base); - delay(_dotLength); - break; - case '-': - _phy->transmitDirect(_base); - delay(_dotLength * 3); - break; - case '_': - // do nothing (word space) - break; - } - - // symbol space - _phy->standby(); - delay(_dotLength); - } - - // letter space - RADIOLIB_DEBUG_PRINTLN(); - delay(_dotLength * 3); - + // inter-word pause (space) + if(b == ' ') { + RADIOLIB_DEBUG_PRINTLN(F("space")); + _phy->standby(); + delay(4 * _dotLength); return(1); } - return(0); + // get morse code from lookup table + uint8_t code = pgm_read_byte(&MorseTable[(uint8_t)(toupper(b) - 32)]); + + // check unsupported characters + if(code == MORSE_UNSUPORTED) { + return(0); + } + + // iterate through codeword until guard bit is reached + while(code > MORSE_GUARDBIT) { + + // send dot or dash + if (code & MORSE_DASH) { + RADIOLIB_DEBUG_PRINT('-'); + _phy->transmitDirect(_base); + delay(3 * _dotLength); + } else { + RADIOLIB_DEBUG_PRINT('.'); + _phy->transmitDirect(_base); + delay(_dotLength); + } + + // symbol space + _phy->standby(); + delay(_dotLength); + + // move onto the next bit + code >>= 1; + } + + // letter space + _phy->standby(); + delay(2 * _dotLength); + RADIOLIB_DEBUG_PRINTLN(); + + return(1); } size_t MorseClient::print(__FlashStringHelper* fstr) { @@ -219,7 +154,7 @@ size_t MorseClient::print(double n, int digits) { } size_t MorseClient::println(void) { - return(MorseClient::write(0x02)); + return(MorseClient::write('^')); } size_t MorseClient::println(__FlashStringHelper* fstr) { diff --git a/src/protocols/Morse/Morse.h b/src/protocols/Morse/Morse.h index 317a8066..66df3280 100644 --- a/src/protocols/Morse/Morse.h +++ b/src/protocols/Morse/Morse.h @@ -4,7 +4,81 @@ #include "../../TypeDef.h" #include "../PhysicalLayer/PhysicalLayer.h" -#define MORSE_LENGTH 52 +#define MORSE_DOT 0b0 +#define MORSE_DASH 0b1 +#define MORSE_GUARDBIT 0b1 +#define MORSE_UNSUPORTED 0xFF + +// Morse character table: - using codes defined in ITU-R M.1677-1 +// - Morse code representation is saved LSb first, using additional bit as guard +// - position in array corresponds ASCII code minus MORSE_ASCII_OFFSET +// - ASCII characters marked MORSE_UNSUPORTED do not have ITU-R M.1677-1 equivalent +static const uint8_t MorseTable[] PROGMEM = { + 0b00, // space + 0b110101, // ! (unsupported) + 0b1010010, // " + MORSE_UNSUPORTED, // # (unsupported) + MORSE_UNSUPORTED, // $ (unsupported) + MORSE_UNSUPORTED, // % (unsupported) + MORSE_UNSUPORTED, // & (unsupported) + 0b1011110, // ' + 0b101101, // ( + 0b1101101, // ) + MORSE_UNSUPORTED, // * (unsupported) + 0b101010, // + + 0b1110011, // , + 0b1100001, // - + 0b1101010, // . + 0b101001, // / + 0b111111, // 0 + 0b111110, // 1 + 0b111100, // 2 + 0b111000, // 3 + 0b110000, // 4 + 0b100000, // 5 + 0b100001, // 6 + 0b100011, // 7 + 0b100111, // 8 + 0b101111, // 9 + 0b1000111, // : + MORSE_UNSUPORTED, // ; (unsupported) + MORSE_UNSUPORTED, // < (unsupported) + 0b110001, // = + MORSE_UNSUPORTED, // > (unsupported) + 0b1001100, // ? + 0b1010110, // @ + 0b110, // A + 0b10001, // B + 0b10101, // C + 0b1001, // D + 0b10, // E + 0b10100, // F + 0b1011, // G + 0b10000, // H + 0b100, // I + 0b11110, // J + 0b1101, // K + 0b10010, // L + 0b111, // M + 0b101, // N + 0b1111, // O + 0b10110, // P + 0b11011, // Q + 0b1010, // R + 0b1000, // S + 0b11, // T + 0b1100, // U + 0b11000, // V + 0b1110, // W + 0b11001, // X + 0b11101, // Y + 0b10011, // Z + MORSE_UNSUPORTED, // [ (unsupported) + MORSE_UNSUPORTED, // \ (unsupported) + MORSE_UNSUPORTED, // ] (unsupported) + 0b1101000, // ^ (unsupported, used as alias for end of work) + 0b110101 // _ (unsupported, used as alias for starting signal) +}; /*! \class MorseClient diff --git a/src/protocols/PhysicalLayer/PhysicalLayer.cpp b/src/protocols/PhysicalLayer/PhysicalLayer.cpp index 75425fd8..0fc34b5b 100644 --- a/src/protocols/PhysicalLayer/PhysicalLayer.cpp +++ b/src/protocols/PhysicalLayer/PhysicalLayer.cpp @@ -1,8 +1,7 @@ #include "PhysicalLayer.h" -PhysicalLayer::PhysicalLayer(float crysFreq, uint8_t divExp, size_t maxPacketLength) { - _crystalFreq = crysFreq; - _divExponent = divExp; +PhysicalLayer::PhysicalLayer(float freqStep, size_t maxPacketLength) { + _freqStep = freqStep; _maxPacketLength = maxPacketLength; } @@ -141,10 +140,6 @@ int16_t PhysicalLayer::receive(String& str, size_t len) { return(state); } -float PhysicalLayer::getCrystalFreq() { - return(_crystalFreq); -} - -uint8_t PhysicalLayer::getDivExponent() { - return(_divExponent); +float PhysicalLayer::getFreqStep() { + return(_freqStep); } diff --git a/src/protocols/PhysicalLayer/PhysicalLayer.h b/src/protocols/PhysicalLayer/PhysicalLayer.h index c6872b48..eca4f6e8 100644 --- a/src/protocols/PhysicalLayer/PhysicalLayer.h +++ b/src/protocols/PhysicalLayer/PhysicalLayer.h @@ -18,13 +18,11 @@ class PhysicalLayer { /*! \brief Default constructor. - \param crysFreq Frequency of crystal oscillator inside the module in MHz. - - \param divExp Exponent of module frequency divider. + \param freqStep Frequency step of the synthesizer in Hz. \param maxPacketLength Maximum length of packet that can be received by the module- */ - PhysicalLayer(float crysFreq, uint8_t divExp, size_t maxPacketLength); + PhysicalLayer(float freqStep, size_t maxPacketLength); // basic methods @@ -193,18 +191,29 @@ class PhysicalLayer { virtual int16_t setFrequencyDeviation(float freqDev) = 0; /*! - \brief Gets the module crystal oscillator frequency that was set in constructor. + \brief Sets GFSK data shaping. Only available in FSK mode. Must be implemented in module class. - \returns Crystal oscillator frequency in MHz. + \param sh Shaping to be set. Set to zero to disable data shaping. + + \returns \ref status_codes */ - float getCrystalFreq(); + virtual int16_t setDataShaping(float sh) = 0; /*! - \brief Gets the module frequency divider exponent that was set in constructor. + \brief Sets FSK data encoding. Only available in FSK mode. Must be implemented in module class. - \returns Frequency divider exponent. + \param enc Encoding to be used. Set to zero to for no encoding (NRZ). + + \returns \ref status_codes */ - uint8_t getDivExponent(); + virtual int16_t setEncoding(uint8_t encoding) = 0; + + /*! + \brief Gets the module frequency step size that was set in constructor. + + \returns Synthesizer frequency step size in Hz. + */ + float getFreqStep(); /*! \brief Query modem for the packet length of received payload. @@ -218,8 +227,7 @@ class PhysicalLayer { #ifndef RADIOLIB_GODMODE private: #endif - float _crystalFreq; - uint8_t _divExponent; + float _freqStep; size_t _maxPacketLength; }; diff --git a/src/protocols/RTTY/RTTY.cpp b/src/protocols/RTTY/RTTY.cpp index 0ea4e06f..c5eebe6c 100644 --- a/src/protocols/RTTY/RTTY.cpp +++ b/src/protocols/RTTY/RTTY.cpp @@ -131,7 +131,7 @@ int16_t RTTYClient::begin(float base, uint32_t shift, uint16_t rate, uint8_t enc _bitDuration = (uint32_t)1000000/rate; // calculate module carrier frequency resolution - uint32_t step = round((_phy->getCrystalFreq() * 1000000) / (uint32_t(1) << _phy->getDivExponent())); + uint32_t step = round(_phy->getFreqStep()); // check minimum shift value if(shift < step / 2) { @@ -146,7 +146,7 @@ int16_t RTTYClient::begin(float base, uint32_t shift, uint16_t rate, uint8_t enc } // calculate 24-bit frequency - _base = (base * (uint32_t(1) << _phy->getDivExponent())) / _phy->getCrystalFreq(); + _base = (base * 1000000.0) / _phy->getFreqStep(); // set module frequency deviation to 0 int16_t state = _phy->setFrequencyDeviation(0);