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/README.md b/README.md index 1c516610..b3980dee 100644 --- a/README.md +++ b/README.md @@ -29,19 +29,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: -* __AVR__ - tested on Uno, Mega and Leonardo -* __ESP8266__ - tested NodeMCU and Wemos D1 -* __ESP32__ - tested on ESP-WROOM-32 -* __STM32__ - tested on Nucleo L452RE-P +* __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. +* _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. +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 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 7dca22ae..74b2b3b1 100644 --- a/examples/CC1101/CC1101_Settings/CC1101_Settings.ino +++ b/examples/CC1101/CC1101_Settings/CC1101_Settings.ino @@ -19,16 +19,18 @@ #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: -// NSS pin: 9 +// CS pin: 9 // GDO0 pin: 4 -// GDO2 pin: 5 -CC1101 cc2 = new Module(9, 4, 5); +// RST pin: unused +// GDO2 pin: 5 (optional) +CC1101 cc2 = new Module(9, 4, NC, 53); // or using RadioShield // https://github.com/jgromes/RadioShield @@ -41,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) { @@ -57,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 1e5363ab..2fee90ba 100644 --- a/examples/RF69/RF69_Settings/RF69_Settings.ino +++ b/examples/RF69/RF69_Settings/RF69_Settings.ino @@ -19,15 +19,15 @@ #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: -// NSS pin: 9 +// CS pin: 9 // DIO0 pin: 4 -// DIO1 pin: 5 +// RESET pin: 5 RF69 rf2 = new Module(9, 4, 5); // or using RadioShield @@ -41,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(); @@ -58,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 bc26c988..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 @@ -18,8 +18,9 @@ // SX1262 has the following connections: // NSS pin: 10 // DIO1 pin: 2 +// NRST pin: 3 // BUSY pin: 9 -SX1262 lora = new Module(10, 2, 9); +SX1262 lora = new Module(10, 2, 3, 9); // or using RadioShield // https://github.com/jgromes/RadioShield @@ -34,11 +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 333e118f..e3e04627 100644 --- a/examples/SX126x/SX126x_FSK_Modem/SX126x_FSK_Modem.ino +++ b/examples/SX126x/SX126x_FSK_Modem/SX126x_FSK_Modem.ino @@ -19,8 +19,9 @@ // SX1262 has the following connections: // NSS pin: 10 // DIO1 pin: 2 +// NRST pin: 3 // BUSY pin: 9 -SX1262 fsk = new Module(10, 2, 9); +SX1262 fsk = new Module(10, 2, 3, 9); // or using RadioShield // https://github.com/jgromes/RadioShield @@ -40,6 +41,7 @@ void setup() { // 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(); diff --git a/examples/SX126x/SX126x_Receive/SX126x_Receive.ino b/examples/SX126x/SX126x_Receive/SX126x_Receive.ino index 4d6a7d34..c95592f0 100644 --- a/examples/SX126x/SX126x_Receive/SX126x_Receive.ino +++ b/examples/SX126x/SX126x_Receive/SX126x_Receive.ino @@ -23,9 +23,9 @@ // 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, 9); +SX1262 lora = new Module(10, 2, 3, 9); // or using RadioShield // https://github.com/jgromes/RadioShield @@ -40,11 +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) { diff --git a/examples/SX126x/SX126x_Receive_Interrupt/SX126x_Receive_Interrupt.ino b/examples/SX126x/SX126x_Receive_Interrupt/SX126x_Receive_Interrupt.ino index c4752735..59d49f53 100644 --- a/examples/SX126x/SX126x_Receive_Interrupt/SX126x_Receive_Interrupt.ino +++ b/examples/SX126x/SX126x_Receive_Interrupt/SX126x_Receive_Interrupt.ino @@ -24,8 +24,9 @@ // SX1262 has the following connections: // NSS pin: 10 // DIO1 pin: 2 +// NRST pin: 3 // BUSY pin: 9 -SX1262 lora = new Module(10, 2, 9); +SX1262 lora = new Module(10, 2, 3, 9); // or using RadioShield // https://github.com/jgromes/RadioShield @@ -40,11 +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 7b2e8901..3c8a3e5e 100644 --- a/examples/SX126x/SX126x_Settings/SX126x_Settings.ino +++ b/examples/SX126x/SX126x_Settings/SX126x_Settings.ino @@ -27,14 +27,16 @@ // SX1262 has the following connections: // NSS pin: 10 // DIO1 pin: 2 +// NRST pin: 3 // BUSY pin: 9 -SX1262 loraSX1262 = new Module(10, 2, 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, 6); +SX1268 loraSX1268 = new Module(8, 4, 5, 6); // or using RadioShield // https://github.com/jgromes/RadioShield @@ -49,11 +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) { @@ -73,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 2394b770..574fdff3 100644 --- a/examples/SX126x/SX126x_Transmit/SX126x_Transmit.ino +++ b/examples/SX126x/SX126x_Transmit/SX126x_Transmit.ino @@ -19,8 +19,9 @@ // SX1262 has the following connections: // NSS pin: 10 // DIO1 pin: 2 +// NRST pin: 3 // BUSY pin: 9 -SX1262 lora = new Module(10, 2, 9); +SX1262 lora = new Module(10, 2, 3, 9); // or using RadioShield // https://github.com/jgromes/RadioShield @@ -35,11 +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/SX126x/SX126x_Transmit_Interrupt/SX126x_Transmit_Interrupt.ino b/examples/SX126x/SX126x_Transmit_Interrupt/SX126x_Transmit_Interrupt.ino index 046ebbfd..157f00a1 100644 --- a/examples/SX126x/SX126x_Transmit_Interrupt/SX126x_Transmit_Interrupt.ino +++ b/examples/SX126x/SX126x_Transmit_Interrupt/SX126x_Transmit_Interrupt.ino @@ -20,8 +20,9 @@ // SX1262 has the following connections: // NSS pin: 10 // DIO1 pin: 2 +// NRST pin: 3 // BUSY pin: 9 -SX1262 lora = new Module(10, 2, 9); +SX1262 lora = new Module(10, 2, 3, 9); // save transmission state between loops int transmissionState = ERR_NONE; @@ -35,11 +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 63fbc9cc..681c76d2 100644 --- a/examples/SX127x/SX127x_Settings/SX127x_Settings.ino +++ b/examples/SX127x/SX127x_Settings/SX127x_Settings.ino @@ -23,14 +23,16 @@ // SX1278 has the following connections: // NSS pin: 10 // DIO0 pin: 2 +// RESET pin: 9 // DIO1 pin: 3 -SX1278 loraSX1278 = new Module(10, 2, 3); +SX1278 loraSX1278 = new Module(10, 2, 9, 3); // SX1272 has different connections: // NSS pin: 9 // DIO0 pin: 4 -// DIO1 pin: 5 -SX1272 loraSX1272 = new Module(9, 4, 5); +// RESET pin: 5 +// DIO1 pin: 6 +SX1272 loraSX1272 = new Module(9, 4, 5, 6); // or using RadioShield // https://github.com/jgromes/RadioShield 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 329fcc9d..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,16 +111,23 @@ setAmbientTemperature KEYWORD2 # CC1101-specific getLQI KEYWORD2 setGdo0Action KEYWORD2 -setGdo1Action KEYWORD2 +setGdo2Action KEYWORD2 +clearGdo0Action KEYWORD2 +clearGdo2Action KEYWORD2 # SX126x-specific 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 @@ -167,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 @@ -250,3 +270,7 @@ 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 e7bb32d4..bee08568 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=RadioLib -version=2.0.1 +version=3.3.1 author=Jan Gromes maintainer=Jan Gromes sentence=Universal wireless communication library for Arduino diff --git a/src/Module.cpp b/src/Module.cpp index 1f62f29e..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: - setPin(_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: - setPin(_int0, INPUT); - break; - case RADIOLIB_INT_1: - setPin(_int1, INPUT); - break; - case RADIOLIB_INT_BOTH: - setPin(_int0, INPUT); - setPin(_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() { @@ -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,29 +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::setPin(int16_t pin, uint8_t mode) { - if(pin != -1) { - pinMode(pin, mode); +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 f23df0ec..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,21 +317,37 @@ 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; uint32_t _ATtimeout = 15000; - - void setPin(int16_t pin, uint8_t mode); }; #endif diff --git a/src/RadioLib.h b/src/RadioLib.h index 538b9228..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" diff --git a/src/TypeDef.h b/src/TypeDef.h index 2f65269b..8ed5b4ee 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 (0x01) +#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. @@ -61,10 +70,22 @@ /* * 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 @@ -86,26 +107,6 @@ */ #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 - /*! \} */ @@ -539,6 +540,29 @@ */ #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 285e5d64..6b01a9d8 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; @@ -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,16 +122,13 @@ 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 rx queue to exceed threshold. + while (!digitalRead(_mod->getIrq())) { + yield(); } - // wait for sync word - while(!digitalRead(_mod->getInt0())); - - // wait for packet end - while(digitalRead(_mod->getInt0())); - // read packet data return(readData(data, len)); } @@ -157,9 +150,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 +160,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 +175,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,29 +210,56 @@ 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); + + // data put on FIFO. + uint8_t dataSent = 0; // optionally write packet length if (_packetLengthConfig == CC1101_LENGTH_CONFIG_VARIABLE) { + + // enforce variable len limit. + if (len > 254) { + return (ERR_PACKET_TOO_LONG); + } + SPIwriteRegister(CC1101_REG_FIFO, len); + dataSent += 1; } // check address filtering uint8_t filter = SPIgetRegValue(CC1101_REG_PKTCTRL1, 1, 0); if(filter != CC1101_ADR_CHK_NONE) { SPIwriteRegister(CC1101_REG_FIFO, addr); + dataSent += 1; } - // write packet to FIFO - SPIwriteRegisterBurst(CC1101_REG_FIFO, data, len); + // fill the FIFO. + uint8_t initialWrite = min((uint8_t)len, (uint8_t)(CC1101_FIFO_SIZE - dataSent)); + SPIwriteRegisterBurst(CC1101_REG_FIFO, data, initialWrite); + dataSent += initialWrite; // set mode to transmit SPIsendCommand(CC1101_CMD_TX); - return(state); + // keep feeding the FIFO until the packet is over. + while (dataSent < len) { + // get number of bytes in FIFO. + uint8_t bytesInFIFO = SPIgetRegValue(CC1101_REG_TXBYTES, 6, 0); + + // if there's room then put other data. + if (bytesInFIFO < CC1101_FIFO_SIZE) { + uint8_t bytesToWrite = min((uint8_t)(CC1101_FIFO_SIZE - bytesInFIFO), (uint8_t)(len - dataSent)); + SPIwriteRegisterBurst(CC1101_REG_FIFO, &data[dataSent], bytesToWrite); + dataSent += bytesToWrite; + } else { + // wait for radio to send some data. + delay(1); + } + } + + return (state); } int16_t CC1101::startReceive() { @@ -238,11 +269,10 @@ int16_t CC1101::startReceive() { // flush Rx FIFO SPIsendCommand(CC1101_CMD_FLUSH_RX); - // set GDO0 mapping - int state = SPIsetRegValue(CC1101_REG_IOCFG0, CC1101_GDOX_SYNC_WORD_SENT_OR_RECEIVED); - if(state != ERR_NONE) { - return(state); - } + // set GDO0 mapping: Asserted when RX FIFO > 4 bytes. + int16_t state = SPIsetRegValue(CC1101_REG_IOCFG0, CC1101_GDOX_RX_FIFO_FULL_OR_PKT_END); + state |= SPIsetRegValue(CC1101_REG_FIFOTHR, CC1101_FIFO_THR_TX_61_RX_4, 3, 0); + RADIOLIB_ASSERT(state); // set mode to receive SPIsendCommand(CC1101_CMD_RX); @@ -253,8 +283,8 @@ int16_t CC1101::startReceive() { int16_t CC1101::readData(uint8_t* data, size_t len) { // get packet length size_t length = len; - if(len == CC1101_MAX_PACKET_LENGTH) { - length = getPacketLength(); + if (len == CC1101_MAX_PACKET_LENGTH) { + length = getPacketLength(true); } // check address filtering @@ -263,31 +293,66 @@ int16_t CC1101::readData(uint8_t* data, size_t len) { SPIreadRegister(CC1101_REG_FIFO); } - // read packet data - SPIreadRegisterBurst(CC1101_REG_FIFO, length, data); + uint8_t bytesInFIFO = SPIgetRegValue(CC1101_REG_RXBYTES, 6, 0); + uint16_t readBytes = 0; + uint32_t lastPop = millis(); - // read RSSI byte - _rawRSSI = SPIgetRegValue(CC1101_REG_FIFO); + // keep reading from FIFO until we get all the packet. + while (readBytes < length) { + if (bytesInFIFO == 0) { + if (millis() - lastPop > 5) { + // readData was required to read a packet longer than the one received. + RADIOLIB_DEBUG_PRINTLN(F("No data for more than 5mS. Stop here.")); + break; + } else { + delay(1); + bytesInFIFO = SPIgetRegValue(CC1101_REG_RXBYTES, 6, 0); + continue; + } + } + + // read the minimum between "remaining length" and bytesInFifo + uint8_t bytesToRead = min((uint8_t)(length - readBytes), bytesInFIFO); + SPIreadRegisterBurst(CC1101_REG_FIFO, bytesToRead, &(data[readBytes])); + readBytes += bytesToRead; + lastPop = millis(); + + // Get how many bytes are left in FIFO. + bytesInFIFO = SPIgetRegValue(CC1101_REG_RXBYTES, 6, 0); + } + + // add terminating null + data[readBytes] = 0; + + // check if status bytes are enabled (default: CC1101_APPEND_STATUS_ON) + bool isAppendStatus = SPIgetRegValue(CC1101_REG_PKTCTRL1, 2, 2) == CC1101_APPEND_STATUS_ON; + + // If status byte is enabled at least 2 bytes (2 status bytes + any following packet) will remain in FIFO. + if (bytesInFIFO >= 2 && isAppendStatus) { + // read RSSI byte + _rawRSSI = SPIgetRegValue(CC1101_REG_FIFO); // read LQI and CRC byte uint8_t val = SPIgetRegValue(CC1101_REG_FIFO); _rawLQI = val & 0x7F; - // add terminating null - data[length] = 0; - - // flush Rx FIFO - SPIsendCommand(CC1101_CMD_FLUSH_RX); + // check CRC + if (_crcOn && (val & 0b10000000) == 0b00000000) { + return (ERR_CRC_MISMATCH); + } + } // clear internal flag so getPacketLength can return the new packet length _packetLengthQueried = false; - // set mode to standby - standby(); + // Flush then standby according to RXOFF_MODE (default: CC1101_RXOFF_IDLE) + if (SPIgetRegValue(CC1101_REG_MCSM1, 3, 2) == CC1101_RXOFF_IDLE) { - // check CRC - if (_crcOn && (val & 0b10000000) == 0b00000000) { - return (ERR_CRC_MISMATCH); + // flush Rx FIFO + SPIsendCommand(CC1101_CMD_FLUSH_RX); + + // set mode to standby + standby(); } return(ERR_NONE); @@ -316,7 +381,7 @@ int16_t CC1101::setFrequency(float freq) { } // Update the TX power accordingly to new freq. (PA values depend on chosen freq) - return setOutputPower(_power); + return(setOutputPower(_power)); } int16_t CC1101::setBitRate(float br) { @@ -336,7 +401,12 @@ int16_t CC1101::setBitRate(float br) { // set bit rate value int16_t state = SPIsetRegValue(CC1101_REG_MDMCFG4, e, 3, 0); state |= SPIsetRegValue(CC1101_REG_MDMCFG3, m); - return(state); + + if (state == ERR_NONE) { + _br = br; + } + + return (state); } int16_t CC1101::setRxBandwidth(float rxBw) { @@ -352,14 +422,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) { @@ -390,9 +460,6 @@ int16_t CC1101::setFrequencyDeviation(float freqDev) { } int16_t CC1101::setOutputPower(int8_t power) { - // Store the value. - _power = power; - // round to the known frequency settings uint8_t f; if(_freq < 374.0) { @@ -449,11 +516,26 @@ 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) { +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); } @@ -468,21 +550,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 state = SPIsetRegValue(CC1101_REG_SYNC1, syncWord[0]); state |= SPIsetRegValue(CC1101_REG_SYNC0, syncWord[1]); - return (state); + 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) { @@ -529,9 +609,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)); @@ -540,14 +618,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) { @@ -584,21 +692,24 @@ int16_t CC1101::variablePacketLengthMode(uint8_t maxLen) { return(setPacketMode(CC1101_LENGTH_CONFIG_VARIABLE, maxLen)); } -int16_t CC1101::enableSyncWordFiltering(uint8_t maxErrBits) { +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, CC1101_SYNC_MODE_16_16, 2, 0)); + 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, CC1101_SYNC_MODE_15_16, 2, 0)); + 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); } } -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) { @@ -621,18 +732,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); @@ -641,6 +748,47 @@ 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); @@ -650,9 +798,7 @@ int16_t CC1101::config() { // 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,17 +853,14 @@ int16_t CC1101::setPacketMode(uint8_t mode, uint8_t len) { // set PKTCTRL0.LENGTH_CONFIG int16_t state = _mod->SPIsetRegValue(CC1101_REG_PKTCTRL0, mode, 1, 0); - if (state != ERR_NONE) { - return(state); - } + 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); } @@ -767,9 +910,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 701abbe9..56062f73 100644 --- a/src/modules/CC1101/CC1101.h +++ b/src/modules/CC1101/CC1101.h @@ -7,9 +7,11 @@ #include "../../protocols/PhysicalLayer/PhysicalLayer.h" // CC1101 physical layer properties +#define CC1101_FREQUENCY_STEP_SIZE 396.7285156 +#define CC1101_MAX_PACKET_LENGTH 255 #define CC1101_CRYSTAL_FREQ 26.0 #define CC1101_DIV_EXPONENT 16 -#define CC1101_MAX_PACKET_LENGTH 63 +#define CC1101_FIFO_SIZE 64 // CC1101 SPI commands #define CC1101_CMD_READ 0b10000000 @@ -167,7 +169,7 @@ #define CC1101_RX_ATTEN_6_DB 0b00010000 // 5 4 6 dB #define CC1101_RX_ATTEN_12_DB 0b00100000 // 5 4 12 dB #define CC1101_RX_ATTEN_18_DB 0b00110000 // 5 4 18 dB -#define CC1101_FIFO_THR 0b00000111 // 5 4 Rx FIFO threshold [bytes] = CC1101_FIFO_THR * 4; Tx FIFO threshold [bytes] = 65 - (CC1101_FIFO_THR * 4) +#define CC1101_FIFO_THR_TX_61_RX_4 0b00000000 // 3 0 TX fifo threshold: 61, RX fifo threshold: 4 // CC1101_REG_SYNC1 #define CC1101_SYNC_WORD_MSB 0xD3 // 7 0 sync word MSB @@ -524,17 +526,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. @@ -597,9 +599,14 @@ class CC1101: public PhysicalLayer { \param func ISR to call. - \param dir Signal change direction. Defaults to FALLING. + \param dir Signal change direction. Defaults to RISING. */ - void setGdo0Action(void (*func)(void), uint8_t dir = FALLING); + void setGdo0Action(void (*func)(void), uint8_t dir = RISING); + + /*! + \brief Clears interrupt service routine to call when GDO0 activates. + */ + void clearGdo0Action(); /*! \brief Sets interrupt service routine to call when GDO2 activates. @@ -610,6 +617,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 +710,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 +725,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 +758,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 +813,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,14 +846,35 @@ 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 Module* _mod; float _freq; + float _br; uint8_t _rawRSSI; uint8_t _rawLQI; + uint8_t _modulation; size_t _packetLength; bool _packetLengthQueried; 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 12e1c4b8..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, float tcxoVoltage) { +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, tcxoVoltage); - 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, float tcxoVoltage) { +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, tcxoVoltage); - 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 513bc766..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. @@ -44,7 +47,7 @@ class SX1262: public SX126x { \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, float tcxoVoltage = 1.6); + 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. @@ -69,7 +72,7 @@ class SX1262: public SX126x { \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, float tcxoVoltage = 1.6); + 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 cb064702..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, float tcxoVoltage) { +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, tcxoVoltage); - 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, float tcxoVoltage) { +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, tcxoVoltage); - 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 ccc0ce50..5b203b72 100644 --- a/src/modules/SX126x/SX1268.h +++ b/src/modules/SX126x/SX1268.h @@ -35,7 +35,7 @@ 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. @@ -47,7 +47,7 @@ class SX1268: public SX126x { \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, float tcxoVoltage = 1.6); + 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. @@ -72,7 +72,7 @@ class SX1268: public SX126x { \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, float tcxoVoltage = 1.6); + 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/SX126x.cpp b/src/modules/SX126x/SX126x.cpp index 2e28ada5..b089f247 100644 --- a/src/modules/SX126x/SX126x.cpp +++ b/src/modules/SX126x/SX126x.cpp @@ -1,12 +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, float tcxoVoltage) { +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); + _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,67 +21,63 @@ int16_t SX126x::begin(float bw, uint8_t sf, uint8_t cr, uint16_t syncWord, float _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); - if(state != ERR_NONE) { - return(state); - } + 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, float tcxoVoltage) { +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); + _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 @@ -91,80 +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); - if(state != ERR_NONE) { - return(state); - } + 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) { @@ -193,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); } } @@ -212,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(); @@ -225,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; @@ -237,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; @@ -258,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 @@ -288,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}; @@ -310,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(); @@ -350,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); @@ -370,7 +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::clearDio1Action() { + detachInterrupt(digitalPinToInterrupt(_mod->getIrq())); } int16_t SX126x::startTransmit(uint8_t* data, size_t len, uint8_t addr) { @@ -391,63 +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->getInt1())); + while(digitalRead(_mod->getGpio())) { + yield(); + } return(state); } int16_t SX126x::startReceive(uint32_t timeout) { int16_t state = startReceiveCommon(); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set mode to receive state = setRx(timeout); @@ -475,9 +474,7 @@ int16_t SX126x::startReceiveDutyCycle(uint32_t rxPeriod, uint32_t sleepPeriod) { } int16_t state = startReceiveCommon(); - if(state != ERR_NONE) { - return(state); - } + 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)}; @@ -527,34 +524,35 @@ int16_t SX126x::startReceiveDutyCycleAuto(uint16_t senderPreambleLength, uint16_ 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); - if(state != ERR_NONE) { - return(state); - } + RADIOLIB_ASSERT(state); // set buffer pointers state = setBufferBaseAddress(); - if(state != ERR_NONE) { - return(state); - } + 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 @@ -565,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); } @@ -660,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); @@ -679,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)); @@ -842,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; @@ -871,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; @@ -891,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); @@ -910,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); @@ -960,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); @@ -988,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); @@ -1006,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; @@ -1019,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); } @@ -1058,7 +1051,11 @@ 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) { @@ -1092,7 +1089,7 @@ uint32_t SX126x::getTimeOnAir(size_t len) { sfDivisor = 4*(_sf - 2); } const int8_t bitsPerCrc = 16; - const int8_t N_symbol_header = 20; + 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; @@ -1111,6 +1108,26 @@ uint32_t SX126x::getTimeOnAir(size_t len) { } } +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(); @@ -1246,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) { @@ -1278,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) { @@ -1315,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)); } @@ -1332,6 +1341,11 @@ 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 = 0; SPIreadCommand(SX126X_CMD_GET_STATUS, &data, 1); @@ -1369,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)) { @@ -1389,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; @@ -1402,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; @@ -1428,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) { @@ -1444,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; @@ -1481,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 delay(5); - while(digitalRead(_mod->getInt1())); + while(digitalRead(_mod->getGpio())) { + yield(); + } return(ERR_NONE); } @@ -1535,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->getInt1())) { + while(digitalRead(_mod->getGpio())) { if(millis() - start >= timeout) { return(ERR_SPI_CMD_TIMEOUT); } @@ -1604,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->getInt1())) { - if(millis() - start >= timeout) { - status = SX126X_STATUS_CMD_TIMEOUT; - break; - } + while(digitalRead(_mod->getGpio())) { + if(millis() - start >= timeout) { + status = SX126X_STATUS_CMD_TIMEOUT; + break; + } } } @@ -1650,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 10e1184d..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,7 +362,7 @@ 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. @@ -369,9 +370,11 @@ class SX126x: public PhysicalLayer { \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, float tcxoVoltage); + 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. @@ -390,9 +393,21 @@ class SX126x: public PhysicalLayer { \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, float tcxoVoltage); + 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. @@ -447,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). @@ -476,6 +493,11 @@ class SX126x: 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. @@ -569,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. @@ -582,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. @@ -772,6 +803,46 @@ class SX126x: public PhysicalLayer { \returns Expected time-on-air in microseconds. */ 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 @@ -793,9 +864,10 @@ 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(); @@ -803,8 +875,8 @@ class SX126x: public PhysicalLayer { 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(); @@ -817,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; @@ -830,6 +902,8 @@ class SX126x: public PhysicalLayer { 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..73d796bf 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); - } - } - - // set bit rate - state = SX127x::setBitRate(br); - if(state != ERR_NONE) { - return(state); - } - - // set frequency deviation - state = SX127x::setFrequencyDeviation(freqDev); - if(state != ERR_NONE) { - return(state); - } - - // set receiver bandwidth - state = SX127x::setRxBandwidth(rxBw); - if(state != ERR_NONE) { - return(state); - } - - // set over current protection - state = SX127x::setCurrentLimit(currentLimit); - if(state != ERR_NONE) { - return(state); - } - - // set preamble length - state = SX127x::setPreambleLength(preambleLength); - if(state != ERR_NONE) { - return(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); - } - - // 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 bit rate + state = SX127x::setBitRate(br); + RADIOLIB_ASSERT(state); + + // set frequency deviation + state = SX127x::setFrequencyDeviation(freqDev); + RADIOLIB_ASSERT(state); + + // set receiver bandwidth + state = SX127x::setRxBandwidth(rxBw); + RADIOLIB_ASSERT(state); + + // set over current protection + state = SX127x::setCurrentLimit(currentLimit); + RADIOLIB_ASSERT(state); + + // set preamble length + state = SX127x::setPreambleLength(preambleLength); + 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); + RADIOLIB_ASSERT(state); + + // disable address filtering + state = disableAddressFiltering(); + 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 7dd2d5c9c..732fc20b 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,11 +8,11 @@ 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); @@ -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(); @@ -263,24 +230,15 @@ int16_t nRF24::setFrequency(int16_t freq) { return(ERR_INVALID_FREQUENCY); } - // set mode to standby - int16_t state = standby(); - if(state != ERR_NONE) { - return(state); - } - // set frequency uint8_t freqRaw = freq - 2400; - state = _mod->SPIsetRegValue(NRF24_REG_RF_CH, freqRaw, 6, 0); - return(state); + return _mod->SPIsetRegValue(NRF24_REG_RF_CH, freqRaw, 6, 0); } 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 +260,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,9 +289,7 @@ 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) { @@ -366,15 +320,14 @@ 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); // set Rx pipe 0 address (for ACK) _mod->SPIwriteRegisterBurst(NRF24_REG_RX_ADDR_P0, addr, _addrWidth); + state |= _mod->SPIsetRegValue(NRF24_REG_EN_RXADDR, NRF24_P0_ON, 0, 0); return(state); } @@ -382,9 +335,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) { @@ -406,9 +357,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) { @@ -438,9 +387,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: @@ -472,6 +419,10 @@ int16_t nRF24::getStatus(uint8_t mask) { return(_mod->SPIgetRegValue(NRF24_REG_STATUS) & mask); } +bool nRF24::isCarrierDetected() { + return(_mod->SPIgetRegValue(NRF24_REG_RPD, 0,0)) == 1; +} + int16_t nRF24::setFrequencyDeviation(float freqDev) { // nRF24 is unable to set frequency deviation // this method is implemented only for PhysicalLayer compatibility @@ -487,6 +438,13 @@ size_t nRF24::getPacketLength(bool update) { } int16_t nRF24::setCrcFiltering(bool crcOn) { + // Auto Ack needs to be disabled in order to disable CRC. + if (!crcOn) { + int16_t status = setAutoAck(false); + RADIOLIB_ASSERT(status) + } + + // Disable CRC return _mod->SPIsetRegValue(NRF24_REG_CONFIG, crcOn ? NRF24_CRC_ON : NRF24_CRC_OFF, 3, 3); } @@ -519,6 +477,20 @@ int16_t nRF24::setAutoAck(uint8_t pipeNum, bool autoAckOn){ } } +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); @@ -530,27 +502,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 2e380984..bf56a317 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 @@ -128,7 +127,7 @@ // NRF24_REG_STATUS #define NRF24_RX_DR 0b01000000 // 6 6 Rx data ready #define NRF24_TX_DS 0b00100000 // 5 5 Tx data sent -#define NRF24_MAX_RT 0b00010000 // 4 4 maximum number of rentransmits reached (must be cleared to continue) +#define NRF24_MAX_RT 0b00010000 // 4 4 maximum number of retransmits reached (must be cleared to continue) #define NRF24_RX_FIFO_EMPTY 0b00001110 // 3 1 Rx FIFO is empty #define NRF24_RX_P_NO 0b00000000 // 3 1 number of data pipe that received data #define NRF24_TX_FIFO_FULL 0b00000001 // 0 0 Tx FIFO is full @@ -393,6 +392,13 @@ class nRF24: public PhysicalLayer { */ int16_t getStatus(uint8_t mask = 0xFF); + /*! + \brief Checks if carrier was detected during last RX + + \returns Whatever the carrier was above threshold. + */ + bool isCarrierDetected(); + /*! \brief Dummy configuration method, to ensure PhysicalLayer compatibility. @@ -438,7 +444,25 @@ class nRF24: public PhysicalLayer { \returns \ref status_codes */ - int16_t setAutoAck(uint8_t pipeNum, bool autoAckOn = true); + int16_t setAutoAck(uint8_t pipeNum, bool autoAckOn); + + /*! + \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: 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 38f2974d..a434bcb3 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);