From 83b713b7769d179fe8ef29787a1f1cd8469e7d23 Mon Sep 17 00:00:00 2001 From: jgromes Date: Mon, 30 Mar 2020 17:38:04 +0200 Subject: [PATCH] [Si443x] Added support for Si443x/RFM2x --- README.md | 10 +- .../Si443x/Si443x_Receive/Si443x_Receive.ino | 87 ++ .../Si443x_Receive_Interrupt.ino | 139 ++++ .../Si443x_Settings/Si443x_Settings.ino | 126 +++ .../Si443x_Transmit/Si443x_Transmit.ino | 87 ++ .../Si443x_Transmit_Interrupt.ino | 133 +++ keywords.txt | 6 + src/RadioLib.h | 6 + src/modules/RFM2x/RFM22.h | 16 + src/modules/RFM2x/RFM23.h | 16 + src/modules/Si443x/Si4430.cpp | 34 + src/modules/Si443x/Si4430.h | 74 ++ src/modules/Si443x/Si4431.cpp | 27 + src/modules/Si443x/Si4431.h | 65 ++ src/modules/Si443x/Si4432.cpp | 34 + src/modules/Si443x/Si4432.h | 74 ++ src/modules/Si443x/Si443x.cpp | 668 +++++++++++++++ src/modules/Si443x/Si443x.h | 786 ++++++++++++++++++ 18 files changed, 2384 insertions(+), 4 deletions(-) create mode 100644 examples/Si443x/Si443x_Receive/Si443x_Receive.ino create mode 100644 examples/Si443x/Si443x_Receive_Interrupt/Si443x_Receive_Interrupt.ino create mode 100644 examples/Si443x/Si443x_Settings/Si443x_Settings.ino create mode 100644 examples/Si443x/Si443x_Transmit/Si443x_Transmit.ino create mode 100644 examples/Si443x/Si443x_Transmit_Interrupt/Si443x_Transmit_Interrupt.ino create mode 100644 src/modules/RFM2x/RFM22.h create mode 100644 src/modules/RFM2x/RFM23.h create mode 100644 src/modules/Si443x/Si4430.cpp create mode 100644 src/modules/Si443x/Si4430.h create mode 100644 src/modules/Si443x/Si4431.cpp create mode 100644 src/modules/Si443x/Si4431.h create mode 100644 src/modules/Si443x/Si4432.cpp create mode 100644 src/modules/Si443x/Si4432.h create mode 100644 src/modules/Si443x/Si443x.cpp create mode 100644 src/modules/Si443x/Si443x.h diff --git a/README.md b/README.md index 61bf5c97..0a789d21 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,9 @@ RadioLib was originally created as a driver for [__RadioShield__](https://github * __JDY08__ BLE module * __nRF24L01__ 2.4 GHz module * __RF69__ FSK/OOK radio module +* __RFM2x__ series FSK modules (RFM22, RM23) * __RFM9x__ series LoRa modules (RFM95, RM96, RFM97, RFM98) +* __Si443x__ series FSK modules (Si4430, Si4431, Si4432) * __SX127x__ series LoRa modules (SX1272, SX1273, SX1276, SX1277, SX1278, SX1279) * __SX126x__ series LoRa modules (SX1261, SX1262, SX1268) * __SX1231__ FSK/OOK radio module @@ -27,9 +29,9 @@ RadioLib was originally created as a driver for [__RadioShield__](https://github ### Supported protocols: * __MQTT__ for modules: ESP8266 * __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 +* __RTTY__ for modules: SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, nRF24L01, RFM2x and Si443x +* __Morse Code__ for modules: SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, nRF24L01, RFM2x and Si443x +* __AX.25__ for modules: SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, RFM2x and Si443x ### Supported platforms: * __Arduino AVR__ - tested with hardware on Uno, Mega and Leonardo @@ -44,7 +46,7 @@ RadioLib was originally created as a driver for [__RadioShield__](https://github * _Apollo3_ - SparkFun Artemis Redboard etc. * _Arduino nRF52_ - Arduino Nano 33 BLE -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. +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 platforms 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/Si443x/Si443x_Receive/Si443x_Receive.ino b/examples/Si443x/Si443x_Receive/Si443x_Receive.ino new file mode 100644 index 00000000..345fb8a6 --- /dev/null +++ b/examples/Si443x/Si443x_Receive/Si443x_Receive.ino @@ -0,0 +1,87 @@ +/* + RadioLib Si443x Receive Example + + This example receives packets using Si443x FSK radio module. + To successfully receive data, the following settings have to be the same + on both transmitter and receiver: + - carrier frequency + - bit rate + - frequency deviation + - sync word + + Other modules from Si443x/RFM2x family can also be used. + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// Si4432 has the following connections: +// nSEL pin: 10 +// nIRQ pin: 2 +// SDN pin: 9 +Si4432 fsk = new Module(10, 2, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//Si4432 fsk = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize Si4432 with default settings + Serial.print(F("[Si4432] Initializing ... ")); + // carrier frequency: 434.0 MHz + // bit rate: 48.0 kbps + // frequency deviation: 50.0 kHz + // Rx bandwidth: 225.1 kHz + // output power: 11 dBm + // sync word: 0x2D 0x01 + int state = fsk.begin(); + if (state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } +} + +void loop() { + Serial.print(F("[Si4432] Waiting for incoming transmission ... ")); + + // you can receive data as an Arduino String + String str; + int state = fsk.receive(str); + + // you can also receive data as byte array + /* + byte byteArr[8]; + int state = rf.receive(byteArr, 8); + */ + + if (state == ERR_NONE) { + // packet was successfully received + Serial.println(F("success!")); + + // print the data of the packet + Serial.print(F("[Si4432] Data:\t\t")); + Serial.println(str); + + } else if (state == ERR_RX_TIMEOUT) { + // timeout occurred while waiting for a packet + Serial.println(F("timeout!")); + + } else if (state == ERR_CRC_MISMATCH) { + // packet was received, but is malformed + Serial.println(F("CRC error!")); + + } else { + // some other error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + + } +} diff --git a/examples/Si443x/Si443x_Receive_Interrupt/Si443x_Receive_Interrupt.ino b/examples/Si443x/Si443x_Receive_Interrupt/Si443x_Receive_Interrupt.ino new file mode 100644 index 00000000..a514980e --- /dev/null +++ b/examples/Si443x/Si443x_Receive_Interrupt/Si443x_Receive_Interrupt.ino @@ -0,0 +1,139 @@ +/* + RadioLib Si443x Receive with Interrupts Example + + This example listens for FSK transmissions and tries to + receive them. Once a packet is received, an interrupt is + triggered. + + Other modules from Si443x/RFM2x family can also be used. + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// Si4432 has the following connections: +// nSEL pin: 10 +// nIRQ pin: 2 +// SDN pin: 9 +Si4432 fsk = new Module(10, 2, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//Si4432 fsk = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize Si4432 with default settings + Serial.print(F("[Si4432] Initializing ... ")); + // carrier frequency: 434.0 MHz + // bit rate: 48.0 kbps + // frequency deviation: 50.0 kHz + // Rx bandwidth: 225.1 kHz + // output power: 11 dBm + // sync word: 0x2D 0x01 + int state = fsk.begin(); + if (state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // set the function that will be called + // when new packet is received + fsk.setIrqAction(setFlag); + + // start listening for packets + Serial.print(F("[Si4432] Starting to listen ... ")); + state = fsk.startReceive(); + if (state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // if needed, 'listen' mode can be disabled by calling + // any of the following methods: + // + // fsk.standby() + // fsk.sleep() + // fsk.transmit(); + // fsk.receive(); + // fsk.readData(); +} + +// flag to indicate that a packet was received +volatile bool receivedFlag = false; + +// disable interrupt when it's not needed +volatile bool enableInterrupt = true; + +// this function is called when a complete packet +// is received by the module +// IMPORTANT: this function MUST be 'void' type +// and MUST NOT have any arguments! +void setFlag(void) { + // check if the interrupt is enabled + if(!enableInterrupt) { + return; + } + + // we got a packet, set the flag + receivedFlag = true; +} + +void loop() { + // check if the flag is set + if(receivedFlag) { + // disable the interrupt service routine while + // processing the data + enableInterrupt = false; + + // reset flag + receivedFlag = false; + + // you can read received data as an Arduino String + String str; + int state = fsk.readData(str); + + // you can also read received data as byte array + /* + byte byteArr[8]; + int state = fsk.readData(byteArr, 8); + */ + + if (state == ERR_NONE) { + // packet was successfully received + Serial.println(F("[Si4432] Received packet!")); + + // print data of the packet + Serial.print(F("[Si4432] Data:\t\t\t")); + Serial.println(str); + + } else if (state == ERR_CRC_MISMATCH) { + // packet was received, but is malformed + Serial.println(F("CRC error!")); + + } else { + // some other error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + + } + + // put module back to listen mode + fsk.startReceive(); + + // we're ready to receive more packets, + // enable interrupt service routine + enableInterrupt = true; + } + +} diff --git a/examples/Si443x/Si443x_Settings/Si443x_Settings.ino b/examples/Si443x/Si443x_Settings/Si443x_Settings.ino new file mode 100644 index 00000000..b3d1ab82 --- /dev/null +++ b/examples/Si443x/Si443x_Settings/Si443x_Settings.ino @@ -0,0 +1,126 @@ +/* + RadioLib Si443x Settings Example + + This example shows how to change all the properties of RF69 radio. + RadioLib currently supports the following settings: + - pins (SPI slave select, nIRQ, shutdown) + - carrier frequency + - bit rate + - receiver bandwidth + - frequency deviation + - output power during transmission + - sync word + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// Si4432 has the following connections: +// nSEL pin: 10 +// nIRQ pin: 2 +// SDN pin: 9 +Si4432 fsk1 = new Module(10, 2, 9); + +// Si4432 has the following connections: +// nSEL pin: 8 +// nIRQ pin: 3 +// SDN pin: 7 +Si4432 fsk2 = new Module(8, 3, 7); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//Si4432 fsk3 = RadioShield.ModuleB; + +void setup() { + Serial.begin(9600); + + // initialize Si4432 with default settings + Serial.print(F("[Si4432] Initializing ... ")); + // carrier frequency: 434.0 MHz + // bit rate: 48.0 kbps + // frequency deviation: 50.0 kHz + // Rx bandwidth: 225.1 kHz + // output power: 11 dBm + // sync word: 0x2D 0x01 + int state = fsk1.begin(); + if (state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // initialize Si4432 with non-default settings + Serial.print(F("[Si4432] Initializing ... ")); + // carrier frequency: 868.0 MHz + // bit rate: 200.0 kbps + // frequency deviation: 60.0 kHz + // Rx bandwidth: 335.5 kHz + // output power: 17 dBm + // sync word: 0x2D 0x01 + state = fsk2.begin(868.0, 200.0, 60.0, 335.5, 17); + if (state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // you can also change the settings at runtime + // and check if the configuration was changed successfully + + // set carrier frequency to 433.5 MHz + if (fsk1.setFrequency(433.5) == ERR_INVALID_FREQUENCY) { + Serial.println(F("[Si4432] Selected frequency is invalid for this module!")); + while (true); + } + + // set bit rate to 100.0 kbps + state = fsk1.setBitRate(100.0); + if (state == ERR_INVALID_BIT_RATE) { + Serial.println(F("[Si4432] Selected bit rate is invalid for this module!")); + while (true); + } else if (state == ERR_INVALID_BIT_RATE_BW_RATIO) { + Serial.println(F("[Si4432] Selected bit rate to bandwidth ratio is invalid!")); + Serial.println(F("[Si4432] Increase receiver bandwidth to set this bit rate.")); + while (true); + } + + // set receiver bandwidth to 284.8 kHz + state = fsk1.setRxBandwidth(284.8); + if (state == ERR_INVALID_RX_BANDWIDTH) { + Serial.println(F("[Si4432] Selected receiver bandwidth is invalid for this module!")); + while (true); + } + + // set frequency deviation to 10.0 kHz + if (fsk1.setFrequencyDeviation(10.0) == ERR_INVALID_FREQUENCY_DEVIATION) { + Serial.println(F("[Si4432] Selected frequency deviation is invalid for this module!")); + while (true); + } + + // set output power to 2 dBm + if (fsk1.setOutputPower(2) == ERR_INVALID_OUTPUT_POWER) { + Serial.println(F("[Si4432] Selected output power is invalid for this module!")); + while (true); + } + + // up to 4 bytes can be set as sync word + // set sync word to 0x01234567 + uint8_t syncWord[] = {0x01, 0x23, 0x45, 0x67}; + if (fsk1.setSyncWord(syncWord, 4) == ERR_INVALID_SYNC_WORD) { + Serial.println(F("[Si4432] Selected sync word is invalid for this module!")); + while (true); + } + + Serial.println(F("[Si4432] All settings changed successfully!")); +} + +void loop() { + // nothing here +} diff --git a/examples/Si443x/Si443x_Transmit/Si443x_Transmit.ino b/examples/Si443x/Si443x_Transmit/Si443x_Transmit.ino new file mode 100644 index 00000000..34a278da --- /dev/null +++ b/examples/Si443x/Si443x_Transmit/Si443x_Transmit.ino @@ -0,0 +1,87 @@ +/* + RadioLib Si443x Transmit Example + + This example transmits packets using Si4432 FSK radio module. + Each packet contains up to 64 bytes of data, in the form of: + - Arduino String + - null-terminated char array (C-string) + - arbitrary binary data (byte array) + + Other modules from Si443x/RFM2x family can also be used. + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// Si4432 has the following connections: +// nSEL pin: 10 +// nIRQ pin: 2 +// SDN pin: 9 +Si4432 fsk = new Module(10, 2, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//Si4432 fsk = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize Si4432 with default settings + Serial.print(F("[Si4432] Initializing ... ")); + // carrier frequency: 434.0 MHz + // bit rate: 48.0 kbps + // frequency deviation: 50.0 kHz + // Rx bandwidth: 225.1 kHz + // output power: 11 dBm + // sync word: 0x2D 0x01 + int state = fsk.begin(); + if (state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } +} + +void loop() { + Serial.print(F("[Si4432] Transmitting packet ... ")); + + // you can transmit C-string or Arduino string up to + // 64 characters long + // NOTE: transmit() is a blocking method! + // See example Si443x_Transmit_Interrupt for details + // on non-blocking transmission method. + int state = fsk.transmit("Hello World!"); + + // you can also transmit byte array up to 64 bytes long + /* + byte byteArr[] = {0x01, 0x23, 0x45, 0x56, 0x78, 0xAB, 0xCD, 0xEF}; + int state = fsk.transmit(byteArr, 8); + */ + + if (state == ERR_NONE) { + // the packet was successfully transmitted + Serial.println(F(" success!")); + + } else if (state == ERR_PACKET_TOO_LONG) { + // the supplied packet was longer than 256 bytes + Serial.println(F(" too long!")); + + } else if (state == ERR_TX_TIMEOUT) { + // timeout occured while transmitting packet + Serial.println(F(" timeout!")); + + } else { + // some other error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + + } + + // wait for a second before transmitting again + delay(1000); +} diff --git a/examples/Si443x/Si443x_Transmit_Interrupt/Si443x_Transmit_Interrupt.ino b/examples/Si443x/Si443x_Transmit_Interrupt/Si443x_Transmit_Interrupt.ino new file mode 100644 index 00000000..e4c56c18 --- /dev/null +++ b/examples/Si443x/Si443x_Transmit_Interrupt/Si443x_Transmit_Interrupt.ino @@ -0,0 +1,133 @@ +/* + RadioLib Si443x Transmit with Interrupts Example + + This example transmits packets using Si4432 FSK radio module. + Each packet contains up to 64 bytes of data, in the form of: + - Arduino String + - null-terminated char array (C-string) + - arbitrary binary data (byte array) + + Other modules from Si443x/RFM2x family can also be used. + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// Si4432 has the following connections: +// nSEL pin: 10 +// nIRQ pin: 2 +// SDN pin: 9 +Si4432 fsk = new Module(10, 2, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//Si4432 fsk = RadioShield.ModuleA; + +// save transmission state between loops +int transmissionState = ERR_NONE; + +void setup() { + Serial.begin(9600); + + // initialize Si4432 with default settings + Serial.print(F("[Si4432] Initializing ... ")); + // carrier frequency: 434.0 MHz + // bit rate: 48.0 kbps + // frequency deviation: 50.0 kHz + // Rx bandwidth: 225.1 kHz + // output power: 11 dBm + // sync word: 0x2D 0x01 + int state = fsk.begin(); + fsk.setOutputPower(13); + if (state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // set the function that will be called + // when packet transmission is finished + fsk.setIrqAction(setFlag); + + // start transmitting the first packet + Serial.print(F("[Si4432] Sending first packet ... ")); + + // you can transmit C-string or Arduino string up to + // 64 characters long + transmissionState = fsk.startTransmit("Hello World!"); + + // you can also transmit byte array up to 64 bytes long + /* + byte byteArr[] = {0x01, 0x23, 0x45, 0x67, + 0x89, 0xAB, 0xCD, 0xEF}; + state = fsk.startTransmit(byteArr, 8); + */ +} + +// flag to indicate that a packet was sent +volatile bool transmittedFlag = false; + +// disable interrupt when it's not needed +volatile bool enableInterrupt = true; + +// this function is called when a complete packet +// is transmitted by the module +// IMPORTANT: this function MUST be 'void' type +// and MUST NOT have any arguments! +void setFlag(void) { + // check if the interrupt is enabled + if(!enableInterrupt) { + return; + } + + // we sent a packet, set the flag + transmittedFlag = true; +} + +void loop() { + // check if the previous transmission finished + if(transmittedFlag) { + // disable the interrupt service routine while + // processing the data + enableInterrupt = false; + + // reset flag + transmittedFlag = false; + + if (transmissionState == ERR_NONE) { + // packet was successfully sent + Serial.println(F("transmission finished!")); + + } else { + Serial.print(F("failed, code ")); + Serial.println(transmissionState); + + } + + // wait a second before transmitting again + delay(1000); + + // send another one + Serial.print(F("[Si4432] Sending another packet ... ")); + + // you can transmit C-string or Arduino string up to + // 256 characters long + transmissionState = fsk.startTransmit("Hello World!"); + + // you can also transmit byte array up to 64 bytes long + /* + byte byteArr[] = {0x01, 0x23, 0x45, 0x67, + 0x89, 0xAB, 0xCD, 0xEF}; + int state = fsk.startTransmit(byteArr, 8); + */ + + // we're ready to send more packets, + // enable interrupt service routine + enableInterrupt = true; + } +} diff --git a/keywords.txt b/keywords.txt index a5ba3b5a..bcf0f9fd 100644 --- a/keywords.txt +++ b/keywords.txt @@ -16,10 +16,15 @@ HC05 KEYWORD1 JDY08 KEYWORD1 nRF24 KEYWORD1 RF69 KEYWORD1 +RFM22 KEYWORD1 +RFM23 KEYWORD1 RFM95 KEYWORD1 RFM96 KEYWORD1 RFM97 KEYWORD1 RFM98 KEYWORD1 +Si4430 KEYWORD1 +Si4431 KEYWORD1 +Si4432 KEYWORD1 SIM800 KEYWORD1 SX1231 KEYWORD1 SX1261 KEYWORD1 @@ -140,6 +145,7 @@ getPacketSource KEYWORD2 getPacketData KEYWORD2 # nRF24 +setIrqAction KEYWORD2 setAddressWidth KEYWORD2 setTransmitPipe KEYWORD2 setReceivePipe KEYWORD2 diff --git a/src/RadioLib.h b/src/RadioLib.h index d434a8d5..16947fbd 100644 --- a/src/RadioLib.h +++ b/src/RadioLib.h @@ -11,6 +11,7 @@ - HC05 Bluetooth module - JDY08 BLE module - RF69 FSK module + - Si443x FSK module - SX126x LoRa/FSK module - SX127x LoRa/FSK module - SX1231 FSK module @@ -49,9 +50,14 @@ #include "modules/JDY08/JDY08.h" #include "modules/nRF24/nRF24.h" #include "modules/RF69/RF69.h" +#include "modules/RFM2x/RFM22.h" +#include "modules/RFM2x/RFM23.h" #include "modules/RFM9x/RFM95.h" #include "modules/RFM9x/RFM96.h" #include "modules/RFM9x/RFM97.h" +#include "modules/Si443x/Si4430.h" +#include "modules/Si443x/Si4431.h" +#include "modules/Si443x/Si4432.h" #include "modules/SX1231/SX1231.h" #include "modules/SX126x/SX1261.h" #include "modules/SX126x/SX1262.h" diff --git a/src/modules/RFM2x/RFM22.h b/src/modules/RFM2x/RFM22.h new file mode 100644 index 00000000..e3cd4a4d --- /dev/null +++ b/src/modules/RFM2x/RFM22.h @@ -0,0 +1,16 @@ +#ifndef _RADIOLIB_RFM22_H +#define _RADIOLIB_RFM22_H + +#include "../../TypeDef.h" +#include "../../Module.h" +#include "../Si443x/Si443x.h" +#include "../Si443x/Si4432.h" + +/*! + \class RFM22 + + \brief Only exists as alias for Si4432, since there seems to be no difference between %RFM22 and %Si4432 modules. +*/ +using RFM22 = Si4432; + +#endif diff --git a/src/modules/RFM2x/RFM23.h b/src/modules/RFM2x/RFM23.h new file mode 100644 index 00000000..4f72557d --- /dev/null +++ b/src/modules/RFM2x/RFM23.h @@ -0,0 +1,16 @@ +#ifndef _RADIOLIB_RFM23_H +#define _RADIOLIB_RFM23_H + +#include "../../TypeDef.h" +#include "../../Module.h" +#include "../Si443x/Si443x.h" +#include "../Si443x/Si4431.h" + +/*! + \class RFM23 + + \brief Only exists as alias for Si4431, since there seems to be no difference between %RFM23 and %Si4431 modules. +*/ +using RFM23 = Si4431; + +#endif diff --git a/src/modules/Si443x/Si4430.cpp b/src/modules/Si443x/Si4430.cpp new file mode 100644 index 00000000..c59f1ea7 --- /dev/null +++ b/src/modules/Si443x/Si4430.cpp @@ -0,0 +1,34 @@ +#include "Si4430.h" + +Si4430::Si4430(Module* mod) : Si4432(mod) { + +} + +int16_t Si4430::begin(float freq, float br, float freqDev, float rxBw, int8_t power) { + // execute common part + int16_t state = Si443x::begin(br, freqDev, rxBw); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setFrequency(freq); + RADIOLIB_ASSERT(state); + + state = setOutputPower(power); + RADIOLIB_ASSERT(state); + + return(state); +} + +int16_t Si4430::setFrequency(float freq) { + RADIOLIB_CHECK_RANGE(freq, 900.0, 960.0, ERR_INVALID_FREQUENCY); + + // set frequency + return(Si443x::setFrequencyRaw(freq)); +} + +int16_t Si4430::setOutputPower(int8_t power) { + RADIOLIB_CHECK_RANGE(power, -8, 13, ERR_INVALID_OUTPUT_POWER); + + // set output power + return(_mod->SPIsetRegValue(SI443X_REG_TX_POWER, (uint8_t)((power + 8) / 3), 2, 0)); +} diff --git a/src/modules/Si443x/Si4430.h b/src/modules/Si443x/Si4430.h new file mode 100644 index 00000000..7e7f8b36 --- /dev/null +++ b/src/modules/Si443x/Si4430.h @@ -0,0 +1,74 @@ +#ifndef _RADIOLIB_SI4430_H +#define _RADIOLIB_SI4430_H + +#include "../../TypeDef.h" +#include "../../Module.h" +#include "Si4432.h" + +/*! + \class Si4430 + + \brief Derived class for %Si4430 modules. +*/ +class Si4430: public Si4432 { + public: + + // constructor + + /*! + \brief Default constructor. + + \param mod Instance of Module that will be used to communicate with the radio chip. + */ + Si4430(Module* mod); + + // basic methods + + /*! + \brief Initialization method. Must be called at least once from Arduino sketch to initialize the module. + + \param freq Carrier frequency in MHz. Allowed values range from 900.0 MHz to 960.0 MHz. + + \param br Bit rate of the FSK transmission in kbps (kilobits per second). Allowed values range from 0.123 to 256.0 kbps. + + \param freqDev Frequency deviation of the FSK transmission in kHz. Allowed values range from 0.625 to 320.0 kbps. + + \param rxBw Receiver bandwidth in kHz. Allowed values range from 2.6 to 620.7 kHz. + + \param power Transmission output power in dBm. Allowed values range from -8 to 13 dBm in 3 dBm steps. + + \returns \ref status_codes + */ + int16_t begin(float freq = 434.0, float br = 48.0, float freqDev = 50.0, float rxBw = 181.1, int8_t power = 10); + + // configuration methods + + /*! + \brief Sets carrier frequency. Allowed values range from 900.0 MHz to 960.0 MHz. + + \param freq Carrier frequency to be set in MHz. + + \returns \ref status_codes + */ + int16_t setFrequency(float freq); + + /*! + \brief Sets output power. Allowed values range from -8 to 13 dBm in 3 dBm steps. + + \param power Output power to be set in dBm. + + \returns \ref status_codes + */ + int16_t setOutputPower(int8_t power); + + +#ifndef RADIOLIB_GODMODE + protected: +#endif + +#ifndef RADIOLIB_GODMODE + private: +#endif +}; + +#endif diff --git a/src/modules/Si443x/Si4431.cpp b/src/modules/Si443x/Si4431.cpp new file mode 100644 index 00000000..25c7baf9 --- /dev/null +++ b/src/modules/Si443x/Si4431.cpp @@ -0,0 +1,27 @@ +#include "Si4431.h" + +Si4431::Si4431(Module* mod) : Si4432(mod) { + +} + +int16_t Si4431::begin(float freq, float br, float freqDev, float rxBw, int8_t power) { + // execute common part + int16_t state = Si443x::begin(br, freqDev, rxBw); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setFrequency(freq); + RADIOLIB_ASSERT(state); + + state = setOutputPower(power); + RADIOLIB_ASSERT(state); + + return(state); +} + +int16_t Si4431::setOutputPower(int8_t power) { + RADIOLIB_CHECK_RANGE(power, -8, 13, ERR_INVALID_OUTPUT_POWER); + + // set output power + return(_mod->SPIsetRegValue(SI443X_REG_TX_POWER, (uint8_t)((power + 8) / 3), 2, 0)); +} diff --git a/src/modules/Si443x/Si4431.h b/src/modules/Si443x/Si4431.h new file mode 100644 index 00000000..c033bcbd --- /dev/null +++ b/src/modules/Si443x/Si4431.h @@ -0,0 +1,65 @@ +#ifndef _RADIOLIB_SI4431_H +#define _RADIOLIB_SI4431_H + +#include "../../TypeDef.h" +#include "../../Module.h" +#include "Si4432.h" + +/*! + \class Si4431 + + \brief Derived class for %Si4431 modules. +*/ +class Si4431: public Si4432 { + public: + + // constructor + + /*! + \brief Default constructor. + + \param mod Instance of Module that will be used to communicate with the radio chip. + */ + Si4431(Module* mod); + + // basic methods + + /*! + \brief Initialization method. Must be called at least once from Arduino sketch to initialize the module. + + \param freq Carrier frequency in MHz. Allowed values range from 240.0 MHz to 930.0 MHz. + + \param br Bit rate of the FSK transmission in kbps (kilobits per second). Allowed values range from 0.123 to 256.0 kbps. + + \param freqDev Frequency deviation of the FSK transmission in kHz. Allowed values range from 0.625 to 320.0 kbps. + + \param rxBw Receiver bandwidth in kHz. Allowed values range from 2.6 to 620.7 kHz. + + \param power Transmission output power in dBm. Allowed values range from -8 to 13 dBm in 3 dBm steps. + + \returns \ref status_codes + */ + int16_t begin(float freq = 434.0, float br = 48.0, float freqDev = 50.0, float rxBw = 181.1, int8_t power = 10); + + // configuration methods + + /*! + \brief Sets output power. Allowed values range from -8 to 13 dBm in 3 dBm steps. + + \param power Output power to be set in dBm. + + \returns \ref status_codes + */ + int16_t setOutputPower(int8_t power); + + +#ifndef RADIOLIB_GODMODE + protected: +#endif + +#ifndef RADIOLIB_GODMODE + private: +#endif +}; + +#endif diff --git a/src/modules/Si443x/Si4432.cpp b/src/modules/Si443x/Si4432.cpp new file mode 100644 index 00000000..38d42863 --- /dev/null +++ b/src/modules/Si443x/Si4432.cpp @@ -0,0 +1,34 @@ +#include "Si4432.h" + +Si4432::Si4432(Module* mod) : Si443x(mod) { + +} + +int16_t Si4432::begin(float freq, float br, float freqDev, float rxBw, int8_t power) { + // execute common part + int16_t state = Si443x::begin(br, freqDev, rxBw); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setFrequency(freq); + RADIOLIB_ASSERT(state); + + state = setOutputPower(power); + RADIOLIB_ASSERT(state); + + return(state); +} + +int16_t Si4432::setFrequency(float freq) { + RADIOLIB_CHECK_RANGE(freq, 240.0, 930.0, ERR_INVALID_FREQUENCY); + + // set frequency + return(Si443x::setFrequencyRaw(freq)); +} + +int16_t Si4432::setOutputPower(int8_t power) { + RADIOLIB_CHECK_RANGE(power, -1, 20, ERR_INVALID_OUTPUT_POWER); + + // set output power + return(_mod->SPIsetRegValue(SI443X_REG_TX_POWER, (uint8_t)((power + 1) / 3), 2, 0)); +} diff --git a/src/modules/Si443x/Si4432.h b/src/modules/Si443x/Si4432.h new file mode 100644 index 00000000..01dd92c5 --- /dev/null +++ b/src/modules/Si443x/Si4432.h @@ -0,0 +1,74 @@ +#ifndef _RADIOLIB_SI4432_H +#define _RADIOLIB_SI4432_H + +#include "../../TypeDef.h" +#include "../../Module.h" +#include "Si443x.h" + +/*! + \class Si4432 + + \brief Derived class for %Si4432 modules. +*/ +class Si4432: public Si443x { + public: + + // constructor + + /*! + \brief Default constructor. + + \param mod Instance of Module that will be used to communicate with the radio chip. + */ + Si4432(Module* mod); + + // basic methods + + /*! + \brief Initialization method. Must be called at least once from Arduino sketch to initialize the module. + + \param freq Carrier frequency in MHz. Allowed values range from 240.0 MHz to 930.0 MHz. + + \param br Bit rate of the FSK transmission in kbps (kilobits per second). Allowed values range from 0.123 to 256.0 kbps. + + \param freqDev Frequency deviation of the FSK transmission in kHz. Allowed values range from 0.625 to 320.0 kbps. + + \param rxBw Receiver bandwidth in kHz. Allowed values range from 2.6 to 620.7 kHz. + + \param power Transmission output power in dBm. Allowed values range from -1 to 20 dBm in 3 dBm steps. + + \returns \ref status_codes + */ + int16_t begin(float freq = 434.0, float br = 48.0, float freqDev = 50.0, float rxBw = 181.1, int8_t power = 11); + + // configuration methods + + /*! + \brief Sets carrier frequency. Allowed values range from 240.0 MHz to 930.0 MHz. + + \param freq Carrier frequency to be set in MHz. + + \returns \ref status_codes + */ + int16_t setFrequency(float freq); + + /*! + \brief Sets output power. Allowed values range from -1 to 20 dBm in 3 dBm steps. + + \param power Output power to be set in dBm. + + \returns \ref status_codes + */ + int16_t setOutputPower(int8_t power); + + +#ifndef RADIOLIB_GODMODE + protected: +#endif + +#ifndef RADIOLIB_GODMODE + private: +#endif +}; + +#endif diff --git a/src/modules/Si443x/Si443x.cpp b/src/modules/Si443x/Si443x.cpp new file mode 100644 index 00000000..6cfc6ffd --- /dev/null +++ b/src/modules/Si443x/Si443x.cpp @@ -0,0 +1,668 @@ +#include "Si443x.h" + +Si443x::Si443x(Module* mod) : PhysicalLayer(SI443X_FREQUENCY_STEP_SIZE, SI443X_MAX_PACKET_LENGTH) { + _mod = mod; + + _packetLengthQueried = false; +} + +int16_t Si443x::begin(float br, float freqDev, float rxBw) { + // set module properties + _mod->init(RADIOLIB_USE_SPI); + Module::pinMode(_mod->getIrq(), INPUT); + Module::pinMode(_mod->getRst(), OUTPUT); + Module::digitalWrite(_mod->getRst(), LOW); + + // try to find the Si443x chip + if(!Si443x::findChip()) { + RADIOLIB_DEBUG_PRINTLN(F("No Si443x found!")); + _mod->term(); + return(ERR_CHIP_NOT_FOUND); + } else { + RADIOLIB_DEBUG_PRINTLN(F("Found Si443x!")); + } + + // clear POR interrupt + clearIRQFlags(); + + // configure settings not accessible by API + int16_t state = config(); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setBitRate(br); + RADIOLIB_ASSERT(state); + + state = setFrequencyDeviation(freqDev); + RADIOLIB_ASSERT(state); + + state = setRxBandwidth(rxBw); + RADIOLIB_ASSERT(state); + + uint8_t syncWord[] = {0x2D, 0x01}; + state = setSyncWord(syncWord, sizeof(syncWord)); + RADIOLIB_ASSERT(state); + + state = packetMode(); + RADIOLIB_ASSERT(state); + + state = setDataShaping(0); + RADIOLIB_ASSERT(state); + + state = setEncoding(0); + RADIOLIB_ASSERT(state); + + return(state); +} + +void Si443x::reset() { + Module::pinMode(_mod->getRst(), OUTPUT); + Module::digitalWrite(_mod->getRst(), HIGH); + delay(1); + Module::digitalWrite(_mod->getRst(), LOW); + delay(100); +} + +int16_t Si443x::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); + 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); + } + } + + // set mode to standby + state = standby(); + + // clear interrupt flags + clearIRQFlags(); + + return(state); +} + +int16_t Si443x::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))*(SI443X_MAX_PACKET_LENGTH*400.0); + + // start reception + int16_t state = startReceive(); + RADIOLIB_ASSERT(state); + + // wait for packet reception or timeout + uint32_t start = micros(); + while(digitalRead(_mod->getIrq())) { + if(micros() - start > timeout) { + standby(); + clearIRQFlags(); + return(ERR_RX_TIMEOUT); + } + } + + // read packet data + return(readData(data, len)); +} + +int16_t Si443x::sleep() { + // disable wakeup timer interrupt + int16_t state = _mod->SPIsetRegValue(SI443X_REG_INTERRUPT_ENABLE_1, 0x00); + RADIOLIB_ASSERT(state); + state = _mod->SPIsetRegValue(SI443X_REG_INTERRUPT_ENABLE_2, 0x00); + RADIOLIB_ASSERT(state); + + // enable wakeup timer to set mode to sleep + _mod->SPIwriteRegister(SI443X_REG_OP_FUNC_CONTROL_1, SI443X_ENABLE_WAKEUP_TIMER); + + return(state); +} + +int16_t Si443x::standby() { + return(_mod->SPIsetRegValue(SI443X_REG_OP_FUNC_CONTROL_1, SI443X_XTAL_ON, 7, 0, 10)); +} + +int16_t Si443x::transmitDirect(uint32_t frf) { + // user requested to start transmitting immediately (required for RTTY) + if(frf != 0) { + // convert the 24-bit frequency to the format accepted by the module + // TODO integers only + float newFreq = frf / 6400.0; + + // check high/low band + uint8_t bandSelect = SI443X_BAND_SELECT_LOW; + uint8_t freqBand = (newFreq / 10) - 24; + if(newFreq >= 480.0) { + bandSelect = SI443X_BAND_SELECT_HIGH; + freqBand = (newFreq / 20) - 24; + } + + // calculate register values + uint16_t freqCarrier = ((newFreq / (10 * ((bandSelect >> 5) + 1))) - freqBand - 24) * (uint32_t)64000; + + // update registers + _mod->SPIwriteRegister(SI443X_REG_FREQUENCY_BAND_SELECT, SI443X_SIDE_BAND_SELECT_LOW | bandSelect | freqBand); + _mod->SPIwriteRegister(SI443X_REG_NOM_CARRIER_FREQUENCY_1, (uint8_t)((freqCarrier & 0xFF00) >> 8)); + _mod->SPIwriteRegister(SI443X_REG_NOM_CARRIER_FREQUENCY_0, (uint8_t)(freqCarrier & 0xFF)); + + // start direct transmission + directMode(); + _mod->SPIwriteRegister(SI443X_REG_OP_FUNC_CONTROL_1, SI443X_TX_ON); + + return(ERR_NONE); + } + + // activate direct mode + int16_t state = directMode(); + RADIOLIB_ASSERT(state); + + // start transmitting + _mod->SPIwriteRegister(SI443X_REG_OP_FUNC_CONTROL_1, SI443X_TX_ON); + return(state); +} + +int16_t Si443x::receiveDirect() { + // activate direct mode + int16_t state = directMode(); + RADIOLIB_ASSERT(state); + + // start receiving + _mod->SPIwriteRegister(SI443X_REG_OP_FUNC_CONTROL_1, SI443X_RX_ON); + return(state); +} + +int16_t Si443x::packetMode() { + return(_mod->SPIsetRegValue(SI443X_REG_MODULATION_MODE_CONTROL_2, SI443X_TX_DATA_SOURCE_FIFO, 5, 4)); +} + +void Si443x::setIrqAction(void (*func)(void)) { + attachInterrupt(digitalPinToInterrupt(_mod->getIrq()), func, FALLING); +} + +void Si443x::clearIrqAction() { + detachInterrupt(digitalPinToInterrupt(_mod->getIrq())); +} + +int16_t Si443x::startTransmit(uint8_t* data, size_t len, uint8_t addr) { + // check packet length + if(len > SI443X_MAX_PACKET_LENGTH) { + return(ERR_PACKET_TOO_LONG); + } + + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // clear Tx FIFO + _mod->SPIsetRegValue(SI443X_REG_OP_FUNC_CONTROL_2, SI443X_TX_FIFO_RESET, 0, 0); + _mod->SPIsetRegValue(SI443X_REG_OP_FUNC_CONTROL_2, SI443X_TX_FIFO_CLEAR, 0, 0); + + // set interrupt mapping + state = _mod->SPIsetRegValue(SI443X_REG_INTERRUPT_ENABLE_1, SI443X_PACKET_SENT_ENABLED); + RADIOLIB_ASSERT(state); + + // clear interrupt flags + clearIRQFlags(); + + // set packet length + // TODO variable packet length + _mod->SPIwriteRegister(SI443X_REG_TRANSMIT_PACKET_LENGTH, len); + + // TODO use header as address field? + (void)addr; + + // write packet to FIFO + _mod->SPIwriteRegisterBurst(SI443X_REG_FIFO_ACCESS, data, len); + + // set mode to transmit + _mod->SPIwriteRegister(SI443X_REG_OP_FUNC_CONTROL_1, SI443X_TX_ON); + + return(state); +} + +int16_t Si443x::startReceive() { + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // clear Rx FIFO + _mod->SPIsetRegValue(SI443X_REG_OP_FUNC_CONTROL_2, SI443X_RX_FIFO_RESET, 1, 1); + _mod->SPIsetRegValue(SI443X_REG_OP_FUNC_CONTROL_2, SI443X_RX_FIFO_CLEAR, 1, 1); + + // set interrupt mapping + state = _mod->SPIsetRegValue(SI443X_REG_INTERRUPT_ENABLE_1, SI443X_VALID_PACKET_RECEIVED_ENABLED, SI443X_CRC_ERROR_ENABLED); + RADIOLIB_ASSERT(state); + state = _mod->SPIsetRegValue(SI443X_REG_INTERRUPT_ENABLE_2, 0x00); + RADIOLIB_ASSERT(state); + + // clear interrupt flags + clearIRQFlags(); + + // set mode to receive + _mod->SPIwriteRegister(SI443X_REG_OP_FUNC_CONTROL_1, SI443X_RX_ON); + + return(state); +} + +int16_t Si443x::readData(uint8_t* data, size_t len) { + // clear interrupt flags + clearIRQFlags(); + + // get packet length + size_t length = len; + if(len == SI443X_MAX_PACKET_LENGTH) { + length = getPacketLength(); + } + + // read packet data + _mod->SPIreadRegisterBurst(SI443X_REG_FIFO_ACCESS, length, data); + + // clear internal flag so getPacketLength can return the new packet length + _packetLengthQueried = false; + + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // clear interrupt flags + clearIRQFlags(); + + return(ERR_NONE); +} + +int16_t Si443x::setBitRate(float br) { + RADIOLIB_CHECK_RANGE(br, 0.123, 256.0, ERR_INVALID_BIT_RATE); + + // check high data rate + uint8_t dataRateMode = SI443X_LOW_DATA_RATE_MODE; + uint8_t exp = 21; + if(br >= 30.0) { + // bit rate above 30 kbps + dataRateMode = SI443X_HIGH_DATA_RATE_MODE; + exp = 16; + } + + // calculate raw data rate value + uint16_t txDr = (br * ((uint32_t)1 << exp)) / 1000.0; + + // update registers + int16_t state = _mod->SPIsetRegValue(SI443X_REG_MODULATION_MODE_CONTROL_1, dataRateMode, 5, 5); + _mod->SPIwriteRegister(SI443X_REG_TX_DATA_RATE_1, (uint8_t)((txDr & 0xFF00) >> 8)); + _mod->SPIwriteRegister(SI443X_REG_TX_DATA_RATE_0, (uint8_t)(txDr & 0xFF)); + + if(state == ERR_NONE) { + _br = br; + } + RADIOLIB_ASSERT(state); + + // update clock recovery + state = updateClockRecovery(); + + return(state); +} + +int16_t Si443x::setFrequencyDeviation(float freqDev) { + // set frequency deviation to lowest available setting (required for RTTY) + if(freqDev == 0.0) { + int16_t state = _mod->SPIsetRegValue(SI443X_REG_MODULATION_MODE_CONTROL_2, 0x00, 2, 2); + _mod->SPIwriteRegister(SI443X_REG_FREQUENCY_DEVIATION, 0x00); + + if(state == ERR_NONE) { + _freqDev = freqDev; + } + + } + + RADIOLIB_CHECK_RANGE(freqDev, 0.625, 320.0, ERR_INVALID_FREQUENCY_DEVIATION); + + // calculate raw frequency deviation value + uint16_t fdev = (uint16_t)(freqDev / 0.625); + + // update registers + int16_t state = _mod->SPIsetRegValue(SI443X_REG_MODULATION_MODE_CONTROL_2, (uint8_t)((fdev & 0x0100) >> 6), 2, 2); + _mod->SPIwriteRegister(SI443X_REG_FREQUENCY_DEVIATION, (uint8_t)(fdev & 0xFF)); + + if(state == ERR_NONE) { + _freqDev = freqDev; + } + + return(state); +} + +int16_t Si443x::setRxBandwidth(float rxBw) { + RADIOLIB_CHECK_RANGE(rxBw, 2.6, 620.7, ERR_INVALID_RX_BANDWIDTH); + + // decide which approximation to use for decimation rate and filter tap calculation + uint8_t bypass = SI443X_BYPASS_DEC_BY_3_OFF; + uint8_t decRate = SI443X_IF_FILTER_DEC_RATE; + uint8_t filterSet = SI443X_IF_FILTER_COEFF_SET; + + // this is the "well-behaved" section - can be linearly approximated + if((rxBw >= 2.6) && (rxBw <= 4.5)) { + decRate = 5; + filterSet = ((rxBw - 2.1429)/0.3250 + 0.5); + } else if((rxBw > 4.5) && (rxBw <= 8.8)) { + decRate = 4; + filterSet = ((rxBw - 3.9857)/0.6643 + 0.5); + } else if((rxBw > 8.8) && (rxBw <= 17.5)) { + decRate = 3; + filterSet = ((rxBw - 7.6714)/1.3536 + 0.5); + } else if((rxBw > 17.5) && (rxBw <= 34.7)) { + decRate = 2; + filterSet = ((rxBw - 15.2000)/2.6893 + 0.5); + } else if((rxBw > 34.7) && (rxBw <= 69.2)) { + decRate = 1; + filterSet = ((rxBw - 30.2430)/5.3679 + 0.5); + } else if((rxBw > 69.2) && (rxBw <= 137.9)) { + decRate = 0; + filterSet = ((rxBw - 60.286)/10.7000 + 0.5); + + // this is the "Lord help thee who tread 'ere" section - no way to approximate this mess + } else if(rxBw == 142.8) { + bypass = SI443X_BYPASS_DEC_BY_3_ON; + decRate = 1; + filterSet = 4; + } else if(rxBw == 167.8) { + bypass = SI443X_BYPASS_DEC_BY_3_ON; + decRate = 1; + filterSet = 5; + } else if(rxBw == 181.1) { + bypass = SI443X_BYPASS_DEC_BY_3_ON; + decRate = 1; + filterSet = 6; + } else if(rxBw == 191.5) { + bypass = SI443X_BYPASS_DEC_BY_3_ON; + decRate = 0; + filterSet = 15; + } else if(rxBw == 225.1) { + bypass = SI443X_BYPASS_DEC_BY_3_ON; + decRate = 0; + filterSet = 1; + } else if(rxBw == 248.8) { + bypass = SI443X_BYPASS_DEC_BY_3_ON; + decRate = 0; + filterSet = 2; + } else if(rxBw == 269.3) { + bypass = SI443X_BYPASS_DEC_BY_3_ON; + decRate = 0; + filterSet = 3; + } else if(rxBw == 284.8) { + bypass = SI443X_BYPASS_DEC_BY_3_ON; + decRate = 0; + filterSet = 4; + } else if(rxBw == 335.5) { + bypass = SI443X_BYPASS_DEC_BY_3_ON; + decRate = 0; + filterSet = 8; + } else if(rxBw == 391.8) { + bypass = SI443X_BYPASS_DEC_BY_3_ON; + decRate = 0; + filterSet = 9; + } else if(rxBw == 420.2) { + bypass = SI443X_BYPASS_DEC_BY_3_ON; + decRate = 0; + filterSet = 10; + } else if(rxBw == 468.4) { + bypass = SI443X_BYPASS_DEC_BY_3_ON; + decRate = 0; + filterSet = 11; + } else if(rxBw == 518.8) { + bypass = SI443X_BYPASS_DEC_BY_3_ON; + decRate = 0; + filterSet = 12; + } else if(rxBw == 577.0) { + bypass = SI443X_BYPASS_DEC_BY_3_ON; + decRate = 0; + filterSet = 13; + } else if(rxBw == 620.7) { + bypass = SI443X_BYPASS_DEC_BY_3_ON; + decRate = 0; + filterSet = 14; + } else { + return(ERR_INVALID_RX_BANDWIDTH); + } + + // shift decimation rate bits + decRate <<= 4; + + // update register + int16_t state = _mod->SPIsetRegValue(SI443X_REG_IF_FILTER_BANDWIDTH, bypass | decRate | filterSet); + RADIOLIB_ASSERT(state); + + // update clock recovery + state = updateClockRecovery(); + + return(state); +} + +int16_t Si443x::setSyncWord(uint8_t* syncWord, size_t len) { + RADIOLIB_CHECK_RANGE(len, 1, 4, ERR_INVALID_SYNC_WORD); + + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // set sync word length + state = _mod->SPIsetRegValue(SI443X_REG_HEADER_CONTROL_2, (uint8_t)(len - 1) << 1, 2, 1); + RADIOLIB_ASSERT(state); + + // set sync word bytes + _mod->SPIwriteRegisterBurst(SI443X_REG_SYNC_WORD_3, syncWord, len); + + return(state); +} + +size_t Si443x::getPacketLength(bool update) { + // TODO variable length mode + if(!_packetLengthQueried && update) { + _packetLength = _mod->SPIreadRegister(SI443X_REG_RECEIVED_PACKET_LENGTH); + _packetLengthQueried = true; + } + + return(_packetLength); +} + +int16_t Si443x::setEncoding(uint8_t encoding) { + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // set encoding + // TODO - add inverted Manchester? + switch(encoding) { + case 0: + return(_mod->SPIsetRegValue(SI443X_REG_MODULATION_MODE_CONTROL_1, SI443X_MANCHESTER_INVERTED_OFF | SI443X_MANCHESTER_OFF | SI443X_WHITENING_OFF, 2, 0)); + case 1: + return(_mod->SPIsetRegValue(SI443X_REG_MODULATION_MODE_CONTROL_1, SI443X_MANCHESTER_INVERTED_OFF | SI443X_MANCHESTER_ON | SI443X_WHITENING_OFF, 2, 0)); + case 2: + return(_mod->SPIsetRegValue(SI443X_REG_MODULATION_MODE_CONTROL_1, SI443X_MANCHESTER_INVERTED_OFF | SI443X_MANCHESTER_OFF | SI443X_WHITENING_ON, 2, 0)); + default: + return(ERR_INVALID_ENCODING); + } +} + +int16_t Si443x::setDataShaping(float sh) { + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + if(sh == 0.0) { + // set modulation to FSK + return(_mod->SPIsetRegValue(SI443X_REG_MODULATION_MODE_CONTROL_2, SI443X_MODULATION_FSK, 1, 0)); + } else { + // set modulation to GFSK + // TODO implement fiter configuration - docs claim this should be possible, but seems undocumented + return(_mod->SPIsetRegValue(SI443X_REG_MODULATION_MODE_CONTROL_2, SI443X_MODULATION_GFSK, 1, 0)); + } +} + +int16_t Si443x::setFrequencyRaw(float newFreq) { + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // check high/low band + uint8_t bandSelect = SI443X_BAND_SELECT_LOW; + uint8_t freqBand = (newFreq / 10) - 24; + if(newFreq >= 480.0) { + bandSelect = SI443X_BAND_SELECT_HIGH; + freqBand = (newFreq / 20) - 24; + } + + // calculate register values + uint16_t freqCarrier = ((newFreq / (10 * ((bandSelect >> 5) + 1))) - freqBand - 24) * (uint32_t)64000; + + // update registers + state = _mod->SPIsetRegValue(SI443X_REG_FREQUENCY_BAND_SELECT, bandSelect | freqBand, 5, 0); + state |= _mod->SPIsetRegValue(SI443X_REG_NOM_CARRIER_FREQUENCY_1, (uint8_t)((freqCarrier & 0xFF00) >> 8)); + state |= _mod->SPIsetRegValue(SI443X_REG_NOM_CARRIER_FREQUENCY_0, (uint8_t)(freqCarrier & 0xFF)); + + return(state); +} + +bool Si443x::findChip() { + uint8_t i = 0; + bool flagFound = false; + while((i < 10) && !flagFound) { + // reset the module + reset(); + + // check version register + uint8_t version = _mod->SPIreadRegister(SI443X_REG_DEVICE_VERSION); + if(version == SI443X_DEVICE_VERSION) { + flagFound = true; + } else { + #ifdef RADIOLIB_DEBUG + RADIOLIB_DEBUG_PRINT(F("Si443x not found! (")); + RADIOLIB_DEBUG_PRINT(i + 1); + RADIOLIB_DEBUG_PRINT(F(" of 10 tries) SI443X_REG_DEVICE_VERSION == ")); + + char buffHex[5]; + sprintf(buffHex, "0x%02X", version); + RADIOLIB_DEBUG_PRINT(buffHex); + RADIOLIB_DEBUG_PRINT(F(", expected 0x00")); + RADIOLIB_DEBUG_PRINTLN(SI443X_DEVICE_VERSION, HEX); + #endif + delay(1000); + i++; + } + } + + return(flagFound); +} + +void Si443x::clearIRQFlags() { + _mod->SPIreadRegister(SI443X_REG_INTERRUPT_STATUS_1); + _mod->SPIreadRegister(SI443X_REG_INTERRUPT_STATUS_2); +} + +int16_t Si443x::config() { + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // disable POR and chip ready interrupts + state = _mod->SPIsetRegValue(SI443X_REG_INTERRUPT_ENABLE_2, 0x00); + RADIOLIB_ASSERT(state); + + // disable packet header + state = _mod->SPIsetRegValue(SI443X_REG_HEADER_CONTROL_2, SI443X_SYNC_WORD_TIMEOUT_ON | SI443X_HEADER_LENGTH_HEADER_NONE, 7, 4); + RADIOLIB_ASSERT(state); + + // disable packet header checking + state = _mod->SPIsetRegValue(SI443X_REG_HEADER_CONTROL_1, SI443X_BROADCAST_ADDR_CHECK_NONE | SI443X_RECEIVED_HEADER_CHECK_NONE); + RADIOLIB_ASSERT(state); + + return(state); +} + +int16_t Si443x::updateClockRecovery() { + // get the parameters + uint8_t bypass = _mod->SPIgetRegValue(SI443X_REG_IF_FILTER_BANDWIDTH, 7, 7) >> 7; + uint8_t decRate = _mod->SPIgetRegValue(SI443X_REG_IF_FILTER_BANDWIDTH, 6, 4) >> 4; + uint8_t manch = _mod->SPIgetRegValue(SI443X_REG_MODULATION_MODE_CONTROL_1, 1, 1) >> 1; + + // calculate oversampling ratio, NCO offset and clock recovery gain + float rxOsr = ((float)(500 * (1 + 2*bypass))) / (((float)((uint16_t)(1) << decRate)) * _br * ((float)(1 + manch))); + uint32_t ncoOff = (_br * (1 + manch) * ((uint32_t)(1) << (20 + decRate))) / (500 * (1 + 2*bypass)); + uint16_t crGain = 2 + (((float)(65536.0 * (1 + manch)) * _br) / (rxOsr * (_freqDev / 0.625))); + + // convert oversampling ratio from float to fixed point + uint8_t rxOsr_int = (uint8_t)rxOsr; + uint8_t rxOsr_dec = 0; + float rxOsr_temp = rxOsr; + if(rxOsr_temp - rxOsr_int >= 0.5) { + rxOsr_dec |= 0x04; + rxOsr_temp -= 0.5; + } + if(rxOsr_temp - rxOsr_int >= 0.25) { + rxOsr_dec |= 0x02; + rxOsr_temp -= 0.25; + } + if(rxOsr_temp - rxOsr_int >= 0.125) { + rxOsr_dec |= 0x01; + rxOsr_temp -= 0.125; + } + uint16_t rxOsr_fixed = ((uint16_t)rxOsr_int << 3) | ((uint16_t)rxOsr_dec); + + // print that whole mess + RADIOLIB_DEBUG_PRINTLN(bypass, HEX); + RADIOLIB_DEBUG_PRINTLN(decRate, HEX); + RADIOLIB_DEBUG_PRINTLN(manch, HEX); + RADIOLIB_DEBUG_PRINT(rxOsr, 3); + RADIOLIB_DEBUG_PRINT('\t'); + RADIOLIB_DEBUG_PRINT(rxOsr_int); + RADIOLIB_DEBUG_PRINT('\t'); + RADIOLIB_DEBUG_PRINT(rxOsr_int, HEX); + RADIOLIB_DEBUG_PRINT('\t'); + RADIOLIB_DEBUG_PRINT(rxOsr_dec); + RADIOLIB_DEBUG_PRINT('\t'); + RADIOLIB_DEBUG_PRINT(rxOsr_dec, HEX); + RADIOLIB_DEBUG_PRINT('\t'); + RADIOLIB_DEBUG_PRINT(rxOsr_fixed); + RADIOLIB_DEBUG_PRINT('\t'); + RADIOLIB_DEBUG_PRINTLN(rxOsr_fixed, HEX); + RADIOLIB_DEBUG_PRINT(ncoOff); + RADIOLIB_DEBUG_PRINT('\t'); + RADIOLIB_DEBUG_PRINTLN(ncoOff, HEX); + RADIOLIB_DEBUG_PRINT(crGain); + RADIOLIB_DEBUG_PRINT('\t'); + RADIOLIB_DEBUG_PRINTLN(crGain, HEX); + + // update oversampling ratio + int16_t state = _mod->SPIsetRegValue(SI443X_REG_CLOCK_REC_OFFSET_2, (uint8_t)((rxOsr_fixed & 0x0700) >> 3), 7, 5); + RADIOLIB_ASSERT(state); + state = _mod->SPIsetRegValue(SI443X_REG_CLOCK_REC_OVERSAMP_RATIO, (uint8_t)(rxOsr_fixed & 0x00FF)); + RADIOLIB_ASSERT(state); + + // update NCO offset + state = _mod->SPIsetRegValue(SI443X_REG_CLOCK_REC_OFFSET_2, (uint8_t)((ncoOff & 0x0F0000) >> 16), 3, 0); + RADIOLIB_ASSERT(state); + state = _mod->SPIsetRegValue(SI443X_REG_CLOCK_REC_OFFSET_1, (uint8_t)((ncoOff & 0x00FF00) >> 8)); + RADIOLIB_ASSERT(state); + state = _mod->SPIsetRegValue(SI443X_REG_CLOCK_REC_OFFSET_0, (uint8_t)(ncoOff & 0x0000FF)); + RADIOLIB_ASSERT(state); + + // update clock recovery loop gain + state = _mod->SPIsetRegValue(SI443X_REG_CLOCK_REC_TIMING_LOOP_GAIN_1, (uint8_t)((crGain & 0x0700) >> 8), 2, 0); + RADIOLIB_ASSERT(state); + state = _mod->SPIsetRegValue(SI443X_REG_CLOCK_REC_TIMING_LOOP_GAIN_0, (uint8_t)(crGain & 0x00FF)); + RADIOLIB_ASSERT(state); + + return(state); +} + +int16_t Si443x::directMode() { + int16_t state = _mod->SPIsetRegValue(SI443X_REG_MODULATION_MODE_CONTROL_2, SI443X_TX_DATA_SOURCE_GPIO, 5, 4); + RADIOLIB_ASSERT(state); + + state = _mod->SPIsetRegValue(SI443X_REG_MODULATION_MODE_CONTROL_2, SI443X_MODULATION_NONE, 1, 0); + return(state); +} diff --git a/src/modules/Si443x/Si443x.h b/src/modules/Si443x/Si443x.h new file mode 100644 index 00000000..df5ae4d0 --- /dev/null +++ b/src/modules/Si443x/Si443x.h @@ -0,0 +1,786 @@ +#ifndef _RADIOLIB_SI443X_H +#define _RADIOLIB_SI443X_H + +#include "../../TypeDef.h" +#include "../../Module.h" + +#include "../../protocols/PhysicalLayer/PhysicalLayer.h" + +// Si443x physical layer properties +#define SI443X_FREQUENCY_STEP_SIZE 156.25 +#define SI443X_MAX_PACKET_LENGTH 64 + +// Si443x series common registers +#define SI443X_REG_DEVICE_TYPE 0x00 +#define SI443X_REG_DEVICE_VERSION 0x01 +#define SI443X_REG_DEVICE_STATUS 0x02 +#define SI443X_REG_INTERRUPT_STATUS_1 0x03 +#define SI443X_REG_INTERRUPT_STATUS_2 0x04 +#define SI443X_REG_INTERRUPT_ENABLE_1 0x05 +#define SI443X_REG_INTERRUPT_ENABLE_2 0x06 +#define SI443X_REG_OP_FUNC_CONTROL_1 0x07 +#define SI443X_REG_OP_FUNC_CONTROL_2 0x08 +#define SI443X_REG_XOSC_LOAD_CAPACITANCE 0x09 +#define SI443X_REG_MCU_OUTPUT_CLOCK 0x0A +#define SI443X_REG_GPIO0_CONFIG 0x0B +#define SI443X_REG_GPIO1_CONFIG 0x0C +#define SI443X_REG_GPIO2_CONFIG 0x0D +#define SI443X_REG_IO_PORT_CONFIG 0x0E +#define SI443X_REG_ADC_CONFIG 0x0F +#define SI443X_REG_ADC_SENSOR_AMP_OFFSET 0x10 +#define SI443X_REG_ADC_VALUE 0x11 +#define SI443X_REG_TEMP_SENSOR_CONTROL 0x12 +#define SI443X_REG_TEMP_VALUE_OFFSET 0x13 +#define SI443X_REG_WAKEUP_TIMER_PERIOD_1 0x14 +#define SI443X_REG_WAKEUP_TIMER_PERIOD_2 0x15 +#define SI443X_REG_WAKEUP_TIMER_PERIOD_3 0x16 +#define SI443X_REG_WAKEUP_TIMER_VALUE_1 0x17 +#define SI443X_REG_WAKEUP_TIMER_VALUE_2 0x18 +#define SI443X_REG_LOW_DC_MODE_DURATION 0x19 +#define SI443X_REG_LOW_BATT_DET_THRESHOLD 0x1A +#define SI443X_REG_BATT_VOLTAGE_LEVEL 0x1B +#define SI443X_REG_IF_FILTER_BANDWIDTH 0x1C +#define SI443X_REG_AFC_LOOP_GEARSHIFT_OVERRIDE 0x1D +#define SI443X_REG_AFC_TIMING_CONTROL 0x1E +#define SI443X_REG_CLOCK_REC_GEARSHIFT_OVERRIDE 0x1F +#define SI443X_REG_CLOCK_REC_OVERSAMP_RATIO 0x20 +#define SI443X_REG_CLOCK_REC_OFFSET_2 0x21 +#define SI443X_REG_CLOCK_REC_OFFSET_1 0x22 +#define SI443X_REG_CLOCK_REC_OFFSET_0 0x23 +#define SI443X_REG_CLOCK_REC_TIMING_LOOP_GAIN_1 0x24 +#define SI443X_REG_CLOCK_REC_TIMING_LOOP_GAIN_0 0x25 +#define SI443X_REG_RSSI 0x26 +#define SI443X_REG_RSSI_CLEAR_CHANNEL_THRESHOLD 0x27 +#define SI443X_REG_ANTENNA_DIVERSITY_1 0x28 +#define SI443X_REG_ANTENNA_DIVERSITY_2 0x29 +#define SI443X_REG_AFC_LIMITER 0x2A +#define SI443X_REG_AFC_CORRECTION 0x2B +#define SI443X_REG_OOK_COUNTER_1 0x2C +#define SI443X_REG_OOK_COUNTER_2 0x2D +#define SI443X_REG_SLICER_PEAK_HOLD 0x2E +#define SI443X_REG_DATA_ACCESS_CONTROL 0x30 +#define SI443X_REG_EZMAC_STATUS 0x31 +#define SI443X_REG_HEADER_CONTROL_1 0x32 +#define SI443X_REG_HEADER_CONTROL_2 0x33 +#define SI443X_REG_PREAMBLE_LENGTH 0x34 +#define SI443X_REG_PREAMBLE_DET_CONTROL 0x35 +#define SI443X_REG_SYNC_WORD_3 0x36 +#define SI443X_REG_SYNC_WORD_2 0x37 +#define SI443X_REG_SYNC_WORD_1 0x38 +#define SI443X_REG_SYNC_WORD_0 0x39 +#define SI443X_REG_TRANSMIT_HEADER_3 0x3A +#define SI443X_REG_TRANSMIT_HEADER_2 0x3B +#define SI443X_REG_TRANSMIT_HEADER_1 0x3C +#define SI443X_REG_TRANSMIT_HEADER_0 0x3D +#define SI443X_REG_TRANSMIT_PACKET_LENGTH 0x3E +#define SI443X_REG_CHECK_HEADER_3 0x3F +#define SI443X_REG_CHECK_HEADER_2 0x40 +#define SI443X_REG_CHECK_HEADER_1 0x41 +#define SI443X_REG_CHECK_HEADER_0 0x42 +#define SI443X_REG_HEADER_ENABLE_3 0x43 +#define SI443X_REG_HEADER_ENABLE_2 0x44 +#define SI443X_REG_HEADER_ENABLE_1 0x45 +#define SI443X_REG_HEADER_ENABLE_0 0x46 +#define SI443X_REG_RECEIVED_HEADER_3 0x47 +#define SI443X_REG_RECEIVED_HEADER_2 0x48 +#define SI443X_REG_RECEIVED_HEADER_1 0x49 +#define SI443X_REG_RECEIVED_HEADER_0 0x4A +#define SI443X_REG_RECEIVED_PACKET_LENGTH 0x4B +#define SI443X_REG_ADC8_CONTROL 0x4F +#define SI443X_REG_CHANNEL_FILTER_COEFF 0x60 +#define SI443X_REG_XOSC_CONTROL_TEST 0x62 +#define SI443X_REG_AGC_OVERRIDE_1 0x69 +#define SI443X_REG_TX_POWER 0x6D +#define SI443X_REG_TX_DATA_RATE_1 0x6E +#define SI443X_REG_TX_DATA_RATE_0 0x6F +#define SI443X_REG_MODULATION_MODE_CONTROL_1 0x70 +#define SI443X_REG_MODULATION_MODE_CONTROL_2 0x71 +#define SI443X_REG_FREQUENCY_DEVIATION 0x72 +#define SI443X_REG_FREQUENCY_OFFSET_1 0x73 +#define SI443X_REG_FREQUENCY_OFFSET_2 0x74 +#define SI443X_REG_FREQUENCY_BAND_SELECT 0x75 +#define SI443X_REG_NOM_CARRIER_FREQUENCY_1 0x76 +#define SI443X_REG_NOM_CARRIER_FREQUENCY_0 0x77 +#define SI443X_REG_FREQUENCY_HOPPING_CHANNEL_SEL 0x79 +#define SI443X_REG_FREQUENCY_HOPPING_STEP_SIZE 0x7A +#define SI443X_REG_TX_FIFO_CONTROL_1 0x7C +#define SI443X_REG_TX_FIFO_CONTROL_2 0x7D +#define SI443X_REG_RX_FIFO_CONTROL 0x7E +#define SI443X_REG_FIFO_ACCESS 0x7F + +// SI443X_REG_DEVICE_TYPE MSB LSB DESCRIPTION +#define SI443X_DEVICE_TYPE 0x08 // 4 0 device identification register + +// SI443X_REG_DEVICE_VERSION +#define SI443X_DEVICE_VERSION 0x06 // 4 0 chip version register + +// SI443X_REG_DEVICE_STATUS +#define SI443X_RX_TX_FIFO_OVERFLOW 0b10000000 // 7 7 Rx/Tx FIFO overflow flag +#define SI443X_RX_TX_FIFO_UNDERFLOW 0b01000000 // 6 6 Rx/Tx FIFO underflow flag +#define SI443X_RX_FIFO_EMPTY 0b00100000 // 5 5 Rx FIFO empty flag +#define SI443X_HEADER_ERROR 0b00010000 // 4 4 header error flag +#define SI443X_FREQUENCY_ERROR 0b00001000 // 3 3 frequency error flag (frequency outside allowed range) +#define SI443X_TX 0b00000010 // 1 0 power state: Tx +#define SI443X_RX 0b00000001 // 1 0 Rx +#define SI443X_IDLE 0b00000000 // 1 0 idle + +// SI443X_REG_INTERRUPT_STATUS_1 +#define SI443X_FIFO_LEVEL_ERROR_INTERRUPT 0b10000000 // 7 7 Tx/Rx FIFO overflow or underflow +#define SI443X_TX_FIFO_ALMOST_FULL_INTERRUPT 0b01000000 // 6 6 Tx FIFO almost full +#define SI443X_TX_FIFO_ALMOST_EMPTY_INTERRUPT 0b00100000 // 5 5 Tx FIFO almost empty +#define SI443X_RX_FIFO_ALMOST_FULL_INTERRUPT 0b00010000 // 4 4 Rx FIFO almost full +#define SI443X_EXTERNAL_INTERRUPT 0b00001000 // 3 3 external interrupt occurred on GPIOx +#define SI443X_PACKET_SENT_INTERRUPT 0b00000100 // 2 2 packet transmission done +#define SI443X_VALID_PACKET_RECEIVED_INTERRUPT 0b00000010 // 1 1 valid packet has been received +#define SI443X_CRC_ERROR_INTERRUPT 0b00000001 // 0 0 CRC failed + +// SI443X_REG_INTERRUPT_STATUS_2 +#define SI443X_SYNC_WORD_DETECTED_INTERRUPT 0b10000000 // 7 7 sync word has been detected +#define SI443X_VALID_PREAMBLE_DETECTED_INTERRUPT 0b01000000 // 6 6 valid preamble has been detected +#define SI443X_INVALID_PREAMBLE_DETECTED_INTERRUPT 0b00100000 // 5 5 invalid preamble has been detected +#define SI443X_RSSI_INTERRUPT 0b00010000 // 4 4 RSSI exceeded programmed threshold +#define SI443X_WAKEUP_TIMER_INTERRUPT 0b00001000 // 3 3 wake-up timer expired +#define SI443X_LOW_BATTERY_INTERRUPT 0b00000100 // 2 2 low battery detected +#define SI443X_CHIP_READY_INTERRUPT 0b00000010 // 1 1 chip ready event detected +#define SI443X_POWER_ON_RESET_INTERRUPT 0b00000001 // 0 0 power-on-reset detected + +// SI443X_REG_INTERRUPT_ENABLE_1 +#define SI443X_FIFO_LEVEL_ERROR_ENABLED 0b10000000 // 7 7 Tx/Rx FIFO overflow or underflow interrupt enabled +#define SI443X_TX_FIFO_ALMOST_FULL_ENABLED 0b01000000 // 6 6 Tx FIFO almost full interrupt enabled +#define SI443X_TX_FIFO_ALMOST_EMPTY_ENABLED 0b00100000 // 5 5 Tx FIFO almost empty interrupt enabled +#define SI443X_RX_FIFO_ALMOST_FULL_ENABLED 0b00010000 // 4 4 Rx FIFO almost full interrupt enabled +#define SI443X_EXTERNAL_ENABLED 0b00001000 // 3 3 external interrupt interrupt enabled +#define SI443X_PACKET_SENT_ENABLED 0b00000100 // 2 2 packet transmission done interrupt enabled +#define SI443X_VALID_PACKET_RECEIVED_ENABLED 0b00000010 // 1 1 valid packet received interrupt enabled +#define SI443X_CRC_ERROR_ENABLED 0b00000001 // 0 0 CRC failed interrupt enabled + +// SI443X_REG_INTERRUPT_ENABLE_2 +#define SI443X_SYNC_WORD_DETECTED_ENABLED 0b10000000 // 7 7 sync word interrupt enabled +#define SI443X_VALID_PREAMBLE_DETECTED_ENABLED 0b01000000 // 6 6 valid preamble interrupt enabled +#define SI443X_INVALID_PREAMBLE_DETECTED_ENABLED 0b00100000 // 5 5 invalid preamble interrupt enabled +#define SI443X_RSSI_ENABLED 0b00010000 // 4 4 RSSI exceeded programmed threshold interrupt enabled +#define SI443X_WAKEUP_TIMER_ENABLED 0b00001000 // 3 3 wake-up timer interrupt enabled +#define SI443X_LOW_BATTERY_ENABLED 0b00000100 // 2 2 low battery interrupt enabled +#define SI443X_CHIP_READY_ENABLED 0b00000010 // 1 1 chip ready event interrupt enabled +#define SI443X_POWER_ON_RESET_ENABLED 0b00000001 // 0 0 power-on-reset interrupt enabled + +// SI443X_REG_OP_FUNC_CONTROL_1 +#define SI443X_SOFTWARE_RESET 0b10000000 // 7 7 reset all registers to default values +#define SI443X_ENABLE_LOW_BATTERY_DETECT 0b01000000 // 6 6 enable low battery detection +#define SI443X_ENABLE_WAKEUP_TIMER 0b00100000 // 5 5 enable wakeup timer +#define SI443X_32_KHZ_RC 0b00000000 // 4 4 32.768 kHz source: RC oscillator (default) +#define SI443X_32_KHZ_XOSC 0b00010000 // 4 4 crystal oscillator +#define SI443X_TX_ON 0b00001000 // 3 3 Tx on in manual transmit mode +#define SI443X_RX_ON 0b00000100 // 2 2 Rx on in manual receive mode +#define SI443X_PLL_ON 0b00000010 // 1 1 PLL on (tune mode) +#define SI443X_XTAL_OFF 0b00000000 // 0 0 crystal oscillator: off (standby mode) +#define SI443X_XTAL_ON 0b00000001 // 0 0 on (ready mode) + +// SI443X_REG_OP_FUNC_CONTROL_2 +#define SI443X_ANT_DIV_TR_HL_IDLE_L 0b00000000 // 7 5 GPIO1/2 states: Tx/Rx GPIO1 H, GPIO2 L; idle low (default) +#define SI443X_ANT_DIV_TR_LH_IDLE_L 0b00100000 // 7 5 Tx/Rx GPIO1 L, GPIO2 H; idle low +#define SI443X_ANT_DIV_TR_HL_IDLE_H 0b01000000 // 7 5 Tx/Rx GPIO1 H, GPIO2 L; idle high +#define SI443X_ANT_DIV_TR_LH_IDLE_H 0b01100000 // 7 5 Tx/Rx GPIO1 L, GPIO2 H; idle high +#define SI443X_ANT_DIV_TR_ALG_IDLE_L 0b10000000 // 7 5 Tx/Rx diversity algorithm; idle low +#define SI443X_ANT_DIV_TR_ALG_IDLE_H 0b10100000 // 7 5 Tx/Rx diversity algorithm; idle high +#define SI443X_ANT_DIV_TR_ALG_BEACON_IDLE_L 0b11000000 // 7 5 Tx/Rx diversity algorithm (beacon); idle low +#define SI443X_ANT_DIV_TR_ALG_BEACON_IDLE_H 0b11100000 // 7 5 Tx/Rx diversity algorithm (beacon); idle high +#define SI443X_RX_MULTIPACKET_OFF 0b00000000 // 4 4 Rx multipacket: disabled (default) +#define SI443X_RX_MULTIPACKET_ON 0b00010000 // 4 4 enabled +#define SI443X_AUTO_TX_OFF 0b00000000 // 3 3 Tx autotransmit on FIFO almost full: disabled (default) +#define SI443X_AUTO_TX_ON 0b00001000 // 3 3 enabled +#define SI443X_LOW_DUTY_CYCLE_OFF 0b00000000 // 2 2 low duty cycle mode: disabled (default) +#define SI443X_LOW_DUTY_CYCLE_ON 0b00000100 // 2 2 enabled +#define SI443X_RX_FIFO_RESET 0b00000010 // 1 1 Rx FIFO reset/clear: reset (call first) +#define SI443X_RX_FIFO_CLEAR 0b00000000 // 1 1 clear (call second) +#define SI443X_TX_FIFO_RESET 0b00000001 // 0 0 Tx FIFO reset/clear: reset (call first) +#define SI443X_TX_FIFO_CLEAR 0b00000000 // 0 0 clear (call second) + +// SI443X_REG_XOSC_LOAD_CAPACITANCE +#define SI443X_XTAL_SHIFT 0b00000000 // 7 7 crystal capacitance configuration: +#define SI443X_XTAL_LOAD_CAPACITANCE 0b01111111 // 6 0 C_int = 1.8 pF + 0.085 pF * SI443X_XTAL_LOAD_CAPACITANCE + 3.7 pF * SI443X_XTAL_SHIFT + +// SI443X_REG_MCU_OUTPUT_CLOCK +#define SI443X_CLOCK_TAIL_CYCLES_OFF 0b00000000 // 5 4 additional clock cycles: none (default) +#define SI443X_CLOCK_TAIL_CYCLES_128 0b00010000 // 5 4 128 +#define SI443X_CLOCK_TAIL_CYCLES_256 0b00100000 // 5 4 256 +#define SI443X_CLOCK_TAIL_CYCLES_512 0b00110000 // 5 4 512 +#define SI443X_LOW_FREQ_CLOCK_OFF 0b00000000 // 3 3 32.768 kHz clock output: disabled (default) +#define SI443X_LOW_FREQ_CLOCK_ON 0b00001000 // 3 3 enabled +#define SI443X_MCU_CLOCK_30_MHZ 0b00000000 // 2 0 GPIO clock output: 30 MHz +#define SI443X_MCU_CLOCK_15_MHZ 0b00000001 // 2 0 15 MHz +#define SI443X_MCU_CLOCK_10_MHZ 0b00000010 // 2 0 10 MHz +#define SI443X_MCU_CLOCK_4_MHZ 0b00000011 // 2 0 4 MHz +#define SI443X_MCU_CLOCK_3_MHZ 0b00000100 // 2 0 3 MHz +#define SI443X_MCU_CLOCK_2_MHZ 0b00000101 // 2 0 2 MHz +#define SI443X_MCU_CLOCK_1_MHZ 0b00000110 // 2 0 1 MHz (default) +#define SI443X_MCU_CLOCK_32_KHZ 0b00000111 // 2 0 32.768 kHz + +// SI443X_REG_GPIO0_CONFIG + SI443X_REG_GPIO1_CONFIG + SI443X_REG_GPIO2_CONFIG +#define SI443X_GPIOX_DRIVE_STRENGTH 0b00000000 // 7 6 GPIOx drive strength (higher number = stronger drive) +#define SI443X_GPIOX_PULLUP_OFF 0b00000000 // 5 5 GPIOx internal 200k pullup: disabled (default) +#define SI443X_GPIOX_PULLUP_ON 0b00100000 // 5 5 enabled +#define SI443X_GPIO0_POWER_ON_RESET_OUT 0b00000000 // 4 0 GPIOx function: power-on-reset output (GPIO0 only, default) +#define SI443X_GPIO1_POWER_ON_RESET_INV_OUT 0b00000000 // 4 0 inverted power-on-reset output (GPIO1 only, default) +#define SI443X_GPIO2_MCU_CLOCK_OUT 0b00000000 // 4 0 MCU clock output (GPIO2 only, default) +#define SI443X_GPIOX_WAKEUP_OUT 0b00000001 // 4 0 wakeup timer expired output +#define SI443X_GPIOX_LOW_BATTERY_OUT 0b00000010 // 4 0 low battery detect output +#define SI443X_GPIOX_DIGITAL_OUT 0b00000011 // 4 0 direct digital output +#define SI443X_GPIOX_EXT_INT_FALLING_IN 0b00000100 // 4 0 external interrupt, falling edge +#define SI443X_GPIOX_EXT_INT_RISING_IN 0b00000101 // 4 0 external interrupt, rising edge +#define SI443X_GPIOX_EXT_INT_CHANGE_IN 0b00000110 // 4 0 external interrupt, state change +#define SI443X_GPIOX_ADC_IN 0b00000111 // 4 0 ADC analog input +#define SI443X_GPIOX_ANALOG_TEST_N_IN 0b00001000 // 4 0 analog test N input +#define SI443X_GPIOX_ANALOG_TEST_P_IN 0b00001001 // 4 0 analog test P input +#define SI443X_GPIOX_DIGITAL_IN 0b00001010 // 4 0 direct digital input +#define SI443X_GPIOX_DIGITAL_TEST_OUT 0b00001011 // 4 0 digital test output +#define SI443X_GPIOX_ANALOG_TEST_N_OUT 0b00001100 // 4 0 analog test N output +#define SI443X_GPIOX_ANALOG_TEST_P_OUT 0b00001101 // 4 0 analog test P output +#define SI443X_GPIOX_REFERENCE_VOLTAGE_OUT 0b00001110 // 4 0 reference voltage output +#define SI443X_GPIOX_TX_RX_DATA_CLK_OUT 0b00001111 // 4 0 Tx/Rx clock output in direct mode +#define SI443X_GPIOX_TX_DATA_IN 0b00010000 // 4 0 Tx data input direct mode +#define SI443X_GPIOX_EXT_RETRANSMIT_REQUEST_IN 0b00010001 // 4 0 external retransmission request input +#define SI443X_GPIOX_TX_STATE_OUT 0b00010010 // 4 0 Tx state output +#define SI443X_GPIOX_TX_FIFO_ALMOST_FULL_OUT 0b00010011 // 4 0 Tx FIFO almost full output +#define SI443X_GPIOX_RX_DATA_OUT 0b00010100 // 4 0 Rx data output +#define SI443X_GPIOX_RX_STATE_OUT 0b00010101 // 4 0 Rx state output +#define SI443X_GPIOX_RX_FIFO_ALMOST_FULL_OUT 0b00010110 // 4 0 Rx FIFO almost full output +#define SI443X_GPIOX_ANT_DIV_1_OUT 0b00010111 // 4 0 antenna diversity output 1 +#define SI443X_GPIOX_ANT_DIV_2_OUT 0b00011000 // 4 0 antenna diversity output 2 +#define SI443X_GPIOX_VALID_PREAMBLE_OUT 0b00011001 // 4 0 valid preamble detected output +#define SI443X_GPIOX_INVALID_PREAMBLE_OUT 0b00011010 // 4 0 invalid preamble detected output +#define SI443X_GPIOX_SYNC_WORD_DETECTED_OUT 0b00011011 // 4 0 sync word detected output +#define SI443X_GPIOX_CLEAR_CHANNEL_OUT 0b00011100 // 4 0 clear channel assessment output +#define SI443X_GPIOX_VDD 0b00011101 // 4 0 VDD +#define SI443X_GPIOX_GND 0b00011110 // 4 0 GND + +// SI443X_REG_IO_PORT_CONFIG +#define SI443X_GPIO2_EXT_INT_STATE_MASK 0b01000000 // 6 6 external interrupt state mask for: GPIO2 +#define SI443X_GPIO1_EXT_INT_STATE_MASK 0b00100000 // 5 5 GPIO1 +#define SI443X_GPIO0_EXT_INT_STATE_MASK 0b00010000 // 4 4 GPIO0 +#define SI443X_IRQ_BY_SDO_OFF 0b00000000 // 3 3 output IRQ state on SDO pin: disabled (default) +#define SI443X_IRQ_BY_SDO_ON 0b00001000 // 3 3 enabled +#define SI443X_GPIO2_DIGITAL_STATE_MASK 0b00000100 // 2 2 digital state mask for: GPIO2 +#define SI443X_GPIO1_DIGITAL_STATE_MASK 0b00000010 // 1 1 GPIO1 +#define SI443X_GPIO0_DIGITAL_STATE_MASK 0b00000001 // 0 0 GPIO0 + +// SI443X_REG_ADC_CONFIG +#define SI443X_ADC_START 0b10000000 // 7 7 ADC control: start measurement +#define SI443X_ADC_RUNNING 0b00000000 // 7 7 measurement in progress +#define SI443X_ADC_DONE 0b10000000 // 7 7 done +#define SI443X_ADC_SOURCE_TEMPERATURE 0b00000000 // 6 4 ADC source: internal temperature sensor (default) +#define SI443X_ADC_SOURCE_GPIO0_SINGLE 0b00010000 // 6 4 single-ended on GPIO0 +#define SI443X_ADC_SOURCE_GPIO1_SINGLE 0b00100000 // 6 4 single-ended on GPIO1 +#define SI443X_ADC_SOURCE_GPIO2_SINGLE 0b00110000 // 6 4 single-ended on GPIO2 +#define SI443X_ADC_SOURCE_GPIO01_DIFF 0b01000000 // 6 4 differential on GPIO0 (+) and GPIO1 (-) +#define SI443X_ADC_SOURCE_GPIO12_DIFF 0b01010000 // 6 4 differential on GPIO1 (+) and GPIO2 (-) +#define SI443X_ADC_SOURCE_GPIO02_DIFF 0b01100000 // 6 4 differential on GPIO0 (+) and GPIO2 (-) +#define SI443X_ADC_SOURCE_GND 0b01110000 // 6 4 GND +#define SI443X_ADC_REFERNCE_BAND_GAP 0b00000000 // 3 2 ADC reference: internal bandgap 1.2 V (default) +#define SI443X_ADC_REFERNCE_VDD_3 0b00001000 // 3 2 VDD/3 +#define SI443X_ADC_REFERNCE_VDD_2 0b00001100 // 3 2 VDD/2 +#define SI443X_ADC_GAIN 0b00000000 // 1 0 ADC amplifier gain + +// SI443X_REG_ADC_SENSOR_AMP_OFFSET +#define SI443X_ADC_OFFSET 0b00000000 // 3 0 ADC offset + +// SI443X_REG_TEMP_SENSOR_CONTROL +#define SI443X_TEMP_SENSOR_RANGE_64_TO_64_C 0b00000000 // 7 6 temperature sensor range: -64 to 64 deg. C, 0.5 deg. C resolution (default) +#define SI443X_TEMP_SENSOR_RANGE_64_TO_192_C 0b01000000 // 7 6 -64 to 192 deg. C, 1.0 deg. C resolution +#define SI443X_TEMP_SENSOR_RANGE_0_TO_128_C 0b11000000 // 7 6 0 to 128 deg. C, 0.5 deg. C resolution +#define SI443X_TEMP_SENSOR_RANGE_40_TO_216_F 0b10000000 // 7 6 -40 to 216 deg. F, 1.0 deg. F resolution +#define SI443X_TEMP_SENSOR_KELVIN_TO_CELSIUS_OFF 0b00000000 // 5 5 Kelvin to Celsius offset: disabled +#define SI443X_TEMP_SENSOR_KELVIN_TO_CELSIUS_ON 0b00100000 // 5 5 enabled (default) +#define SI443X_TEMP_SENSOR_TRIM_OFF 0b00000000 // 4 4 temperature sensor trim: disabled (default) +#define SI443X_TEMP_SENSOR_TRIM_ON 0b00010000 // 4 4 enabled +#define SI443X_TEMP_SENSOR_TRIM_VALUE 0b00000000 // 3 0 temperature sensor trim value + +// SI443X_REG_WAKEUP_TIMER_PERIOD_1 +#define SI443X_WAKEUP_TIMER_EXPONENT 0b00000011 // 4 0 wakeup timer value exponent + +// SI443X_REG_WAKEUP_TIMER_PERIOD_2 + SI443X_REG_WAKEUP_TIMER_PERIOD_3 +#define SI443X_WAKEUP_TIMER_MANTISSA_MSB 0x00 // 7 0 wakeup timer value: +#define SI443X_WAKEUP_TIMER_MANTISSA_LSB 0x01 // 7 0 T = (4 * SI443X_WAKEUP_TIMER_MANTISSA * 2 ^ SI443X_WAKEUP_TIMER_EXPONENT) / 32.768 ms + +// SI443X_REG_LOW_DC_MODE_DURATION +#define SI443X_LOW_DC_MODE_DURATION_MANTISSA 0x01 // 7 0 low duty cycle mode duration: T = (4 * SI443X_LOW_DC_MODE_DURATION_MANTISSA * 2 ^ SI443X_WAKEUP_TIMER_EXPONENT) / 32.768 ms + +// SI443X_REG_LOW_BATT_DET_THRESHOLD +#define SI443X_LOW_BATT_DET_THRESHOLD 0b00010100 // 4 0 low battery detection threshold: Vth = 1.7 + SI443X_LOW_BATT_DET_THRESHOLD * 0.05 V (defaults to 2.7 V) + +// SI443X_REG_IF_FILTER_BANDWIDTH +#define SI443X_BYPASS_DEC_BY_3_OFF 0b00000000 // 7 7 bypass decimate-by-3 stage: disabled (default) +#define SI443X_BYPASS_DEC_BY_3_ON 0b10000000 // 7 7 enabled +#define SI443X_IF_FILTER_DEC_RATE 0b00000000 // 6 4 IF filter decimation rate +#define SI443X_IF_FILTER_COEFF_SET 0b00000001 // 3 0 IF filter coefficient set selection + +// SI443X_REG_AFC_LOOP_GEARSHIFT_OVERRIDE +#define SI443X_AFC_WIDEBAND_OFF 0b00000000 // 7 7 AFC wideband: disabled (default) +#define SI443X_AFC_WIDEBAND_ON 0b10000000 // 7 7 enabled +#define SI443X_AFC_OFF 0b00000000 // 6 6 AFC: disabled +#define SI443X_AFC_ON 0b01000000 // 6 6 enabled (default) +#define SI443X_AFC_HIGH_GEAR_SETTING 0b00000000 // 5 3 AFC high gear setting +#define SI443X_SECOND_PHASE_BIAS_0_DB 0b00000100 // 2 2 second phase antenna selection bias: 0 dB (default) +#define SI443X_SECOND_PHASE_BIAS_1_5_DB 0b00000000 // 2 2 1.5 dB +#define SI443X_MOVING_AVERAGE_TAP_8 0b00000010 // 1 1 moving average filter tap length: 8*Tb +#define SI443X_MOVING_AVERAGE_TAP_4 0b00000000 // 1 1 4*Tb after first preamble (default) +#define SI443X_ZERO_PHASE_RESET_5 0b00000000 // 0 0 reset preamble detector after: 5 zero phases (default) +#define SI443X_ZERO_PHASE_RESET_2 0b00000001 // 0 0 3 zero phases + +// SI443X_REG_AFC_TIMING_CONTROL +#define SI443X_SW_ANT_TIMER 0b00000000 // 7 6 number of periods to wait for RSSI to stabilize during antenna switching +#define SI443X_SHORT_WAIT 0b00001000 // 5 3 period to wait after AFC correction +#define SI443X_ANTENNA_SWITCH_WAIT 0b00000010 // 2 0 antenna switching wait time + +// SI443X_REG_CLOCK_REC_GEARSHIFT_OVERRIDE +#define SI443X_CLOCK_RECOVER_FAST_GEARSHIFT 0b00000000 // 5 3 clock recovery fast gearshift value +#define SI443X_CLOCK_RECOVER_SLOW_GEARSHIFT 0b00000011 // 2 0 clock recovery slow gearshift value + +// SI443X_REG_CLOCK_REC_OVERSAMP_RATIO +#define SI443X_CLOCK_REC_OVERSAMP_RATIO_LSB 0b01100100 // 7 0 oversampling rate LSB, defaults to 12.5 clock cycles per bit + +// SI443X_REG_CLOCK_REC_OFFSET_2 +#define SI443X_CLOCK_REC_OVERSAMP_RATIO_MSB 0b00000000 // 7 5 oversampling rate MSB, defaults to 12.5 clock cycles per bit +#define SI443X_SECOND_PHASE_SKIP_THRESHOLD 0b00000000 // 4 4 skip seconds phase antenna diversity threshold +#define SI443X_NCO_OFFSET_MSB 0b00000001 // 3 0 NCO offset MSB + +// SI443X_REG_CLOCK_REC_OFFSET_1 +#define SI443X_NCO_OFFSET_MID 0b01000111 // 7 0 NCO offset MID + +// SI443X_REG_CLOCK_REC_OFFSET_0 +#define SI443X_NCO_OFFSET_LSB 0b10101110 // 7 0 NCO offset LSB + +// SI443X_REG_CLOCK_REC_TIMING_LOOP_GAIN_1 +#define SI443X_RX_COMPENSATION_OFF 0b00000000 // 4 4 Rx compensation for high data rate: disabled (default) +#define SI443X_RX_COMPENSATION_ON 0b00010000 // 4 4 enabled +#define SI443X_CLOCK_REC_GAIN_DOUBLE_OFF 0b00000000 // 3 3 clock recovery gain doubling: disabled (default) +#define SI443X_CLOCK_REC_GAIN_DOUBLE_ON 0b00001000 // 3 3 enabled +#define SI443X_CLOCK_REC_LOOP_GAIN_MSB 0b00000010 // 2 0 clock recovery timing loop gain MSB + +// SI443X_REG_CLOCK_REC_TIMING_LOOP_GAIN_0 +#define SI443X_CLOCK_REC_LOOP_GAIN_LSB 0b10001111 // 7 0 clock recovery timing loop gain LSB + +// SI443X_REG_RSSI_CLEAR_CHANNEL_THRESHOLD +#define SI443X_RSSI_CLEAR_CHANNEL_THRESHOLD 0b00011110 // 7 0 RSSI clear channel interrupt threshold + +// SI443X_REG_AFC_LIMITER +#define SI443X_AFC_LIMITER 0x00 // 7 0 AFC limiter value + +// SI443X_REG_OOK_COUNTER_1 +#define SI443X_OOK_FREEZE_OFF 0b00000000 // 5 5 OOK moving average detector freeze: disabled (default) +#define SI443X_OOK_FREEZE_ON 0b00100000 // 5 5 enabled +#define SI443X_PEAK_DETECTOR_OFF 0b00000000 // 4 4 peak detector: disabled +#define SI443X_PEAK_DETECTOR_ON 0b00010000 // 4 4 enabled (default) +#define SI443X_OOK_MOVING_AVERAGE_OFF 0b00000000 // 3 3 OOK moving average: disabled +#define SI443X_OOK_MOVING_AVERAGE_ON 0b00001000 // 3 3 enabled (default) +#define SI443X_OOK_COUNTER_MSB 0b00000000 // 2 0 OOK counter MSB + +// SI443X_REG_OOK_COUNTER_2 +#define SI443X_OOK_COUNTER_LSB 0b10111100 // 7 0 OOK counter LSB + +// SI443X_REG_SLICER_PEAK_HOLD +#define SI443X_PEAK_DETECTOR_ATTACK 0b00010000 // 6 4 OOK peak detector attach time +#define SI443X_PEAK_DETECTOR_DECAY 0b00001100 // 3 0 OOK peak detector decay time + +// SI443X_REG_DATA_ACCESS_CONTROL +#define SI443X_PACKET_RX_HANDLING_OFF 0b00000000 // 7 7 packet Rx handling: disabled +#define SI443X_PACKET_RX_HANDLING_ON 0b10000000 // 7 7 enabled (default) +#define SI443X_LSB_FIRST_OFF 0b00000000 // 6 6 LSB first transmission: disabled (default) +#define SI443X_LSB_FIRST_ON 0b01000000 // 6 6 enabled +#define SI443X_CRC_DATA_ONLY_OFF 0b00000000 // 5 5 CRC calculated only from data fields: disabled (default) +#define SI443X_CRC_DATA_ONLY_ON 0b00100000 // 5 5 enabled +#define SI443X_SKIP_SECOND_PHASE_PREAMBLE_DET_OFF 0b00000000 // 4 4 skip second phase of preamble detection: disabled (default) +#define SI443X_SKIP_SECOND_PHASE_PREAMBLE_DET_ON 0b00010000 // 4 4 enabled +#define SI443X_PACKET_TX_HANDLING_OFF 0b00000000 // 3 3 packet Tx handling: disabled +#define SI443X_PACKET_TX_HANDLING_ON 0b00001000 // 3 3 enabled (default) +#define SI443X_CRC_OFF 0b00000000 // 2 2 CRC: disabled +#define SI443X_CRC_ON 0b00000100 // 2 2 enabled (default) +#define SI443X_CRC_CCITT 0b00000000 // 1 0 CRC type: CCITT +#define SI443X_CRC_IBM_CRC16 0b00000001 // 1 0 IBM CRC-16 (default) +#define SI443X_CRC_IEC16 0b00000010 // 1 0 IEC-16 +#define SI443X_CRC_BIACHEVA 0b00000011 // 1 0 Biacheva + +// SI443X_REG_EZMAC_STATUS +#define SI443X_CRC_ALL_ONE 0b01000000 // 6 6 last received CRC was all ones +#define SI443X_PACKET_SEARCHING 0b00100000 // 5 5 radio is searching for a valid packet +#define SI443X_PACKET_RECEIVING 0b00010000 // 4 4 radio is currently receiving packet +#define SI443X_VALID_PACKET_RECEIVED 0b00001000 // 3 3 valid packet was received +#define SI443X_CRC_ERROR 0b00000100 // 2 2 CRC check failed +#define SI443X_PACKET_TRANSMITTING 0b00000010 // 1 1 radio is currently transmitting packet +#define SI443X_PACKET_SENT 0b00000001 // 0 0 packet sent + +// SI443X_REG_HEADER_CONTROL_1 +#define SI443X_BROADCAST_ADDR_CHECK_NONE 0b00000000 // 7 4 broadcast address check: none (default) +#define SI443X_BROADCAST_ADDR_CHECK_BYTE0 0b00010000 // 7 4 on byte 0 +#define SI443X_BROADCAST_ADDR_CHECK_BYTE1 0b00100000 // 7 4 on byte 1 +#define SI443X_BROADCAST_ADDR_CHECK_BYTE2 0b01000000 // 7 4 on byte 2 +#define SI443X_BROADCAST_ADDR_CHECK_BYTE3 0b10000000 // 7 4 on byte 3 +#define SI443X_RECEIVED_HEADER_CHECK_NONE 0b00000000 // 3 0 received header check: none +#define SI443X_RECEIVED_HEADER_CHECK_BYTE0 0b00000001 // 3 0 on byte 0 +#define SI443X_RECEIVED_HEADER_CHECK_BYTE1 0b00000010 // 3 0 on byte 1 +#define SI443X_RECEIVED_HEADER_CHECK_BYTE2 0b00000100 // 3 0 on byte 2 (default) +#define SI443X_RECEIVED_HEADER_CHECK_BYTE3 0b00001000 // 3 0 on byte 3 (default) + +// SI443X_REG_HEADER_CONTROL_2 +#define SI443X_SYNC_WORD_TIMEOUT_OFF 0b00000000 // 7 7 ignore timeout period when searching for sync word: disabled (default) +#define SI443X_SYNC_WORD_TIMEOUT_ON 0b10000000 // 7 7 enabled +#define SI443X_HEADER_LENGTH_HEADER_NONE 0b00000000 // 6 4 header length: none +#define SI443X_HEADER_LENGTH_HEADER_3 0b00010000 // 6 4 header 3 +#define SI443X_HEADER_LENGTH_HEADER_32 0b00100000 // 6 4 header 3 and 2 +#define SI443X_HEADER_LENGTH_HEADER_321 0b00110000 // 6 4 header 3, 2 and 1 (default) +#define SI443X_HEADER_LENGTH_HEADER_3210 0b01000000 // 6 4 header 3, 2, 1, and 0 +#define SI443X_FIXED_PACKET_LENGTH_OFF 0b00000000 // 3 3 fixed packet length mode: disabled (default) +#define SI443X_FIXED_PACKET_LENGTH_ON 0b00001000 // 3 3 enabled +#define SI443X_SYNC_LENGTH_SYNC_3 0b00000000 // 2 1 sync word length: sync 3 +#define SI443X_SYNC_LENGTH_SYNC_32 0b00000010 // 2 1 sync 3 and 2 (default) +#define SI443X_SYNC_LENGTH_SYNC_321 0b00000100 // 2 1 sync 3, 2 and 1 +#define SI443X_SYNC_LENGTH_SYNC_3210 0b00000110 // 2 1 sync 3, 2, 1 and 0 +#define SI443X_PREAMBLE_LENGTH_MSB 0b00000000 // 0 0 preamble length MSB + +// SI443X_REG_PREAMBLE_LENGTH +#define SI443X_PREAMBLE_LENGTH_LSB 0b00001000 // 0 0 preamble length LSB, defaults to 32 bits + +// SI443X_REG_PREAMBLE_DET_CONTROL +#define SI443X_PREAMBLE_DET_THRESHOLD 0b00101000 // 7 3 number of 4-bit nibbles in valid preamble, defaults to 20 bits +#define SI443X_RSSI_OFFSET 0b00000010 // 2 0 RSSI calculation offset, defaults to +8 dB + +// SI443X_REG_SYNC_WORD_3 - SI443X_REG_SYNC_WORD_0 +#define SI443X_SYNC_WORD_3 0x2D // 7 0 sync word: 4th byte (MSB) +#define SI443X_SYNC_WORD_2 0xD4 // 7 0 3rd byte +#define SI443X_SYNC_WORD_1 0x00 // 7 0 2nd byte +#define SI443X_SYNC_WORD_0 0x00 // 7 0 1st byte (LSB) + +// SI443X_REG_CHANNEL_FILTER_COEFF +#define SI443X_INVALID_PREAMBLE_THRESHOLD 0b00000000 // 7 4 invalid preamble threshold in nibbles + +// SI443X_REG_XOSC_CONTROL_TEST +#define SI443X_STATE_LOW_POWER 0b00000000 // 7 5 chip power state: low power +#define SI443X_STATE_READY 0b00100000 // 7 5 ready +#define SI443X_STATE_TUNE 0b01100000 // 7 5 tune +#define SI443X_STATE_TX 0b01000000 // 7 5 Tx +#define SI443X_STATE_RX 0b11100000 // 7 5 Rx + +// SI443X_REG_AGC_OVERRIDE_1 +#define SI443X_AGC_GAIN_INCREASE_OFF 0b00000000 // 6 6 AGC gain increase override: disabled (default) +#define SI443X_AGC_GAIN_INCREASE_ON 0b01000000 // 6 6 enabled +#define SI443X_AGC_OFF 0b00000000 // 5 5 AGC loop: disabled +#define SI443X_AGC_ON 0b00100000 // 5 5 enabled (default) +#define SI443X_LNA_GAIN_MIN 0b00000000 // 4 4 LNA gain select: 5 dB (default) +#define SI443X_LNA_GAIN_MAX 0b00010000 // 4 4 25 dB +#define SI443X_PGA_GAIN_OVERRIDE 0b00000000 // 3 0 PGA gain override, gain = SI443X_PGA_GAIN_OVERRIDE * 3 dB + +// SI443X_REG_TX_POWER +#define SI443X_LNA_SWITCH_OFF 0b00000000 // 3 3 LNA switch control: disabled +#define SI443X_LNA_SWITCH_ON 0b00001000 // 3 3 enabled (default) +#define SI443X_OUTPUT_POWER 0b00000000 // 2 0 output power in 3 dB steps, 0 is chip min, 7 is chip max + +// SI443X_REG_TX_DATA_RATE_1 + SI443X_REG_TX_DATA_RATE_0 +#define SI443X_DATA_RATE_MSB 0x0A // 7 0 data rate: DR = 10^6 * (SI443X_DATA_RATE / 2^16) in high data rate mode or +#define SI443X_DATA_RATE_LSB 0x3D // 7 0 DR = 10^6 * (SI443X_DATA_RATE / 2^21) in low data rate mode (defaults to 40 kbps) + +// SI443X_REG_MODULATION_MODE_CONTROL_1 +#define SI443X_HIGH_DATA_RATE_MODE 0b00000000 // 5 5 data rate: above 30 kbps (default) +#define SI443X_LOW_DATA_RATE_MODE 0b00100000 // 5 5 below 30 kbps +#define SI443X_PACKET_HANDLER_POWER_DOWN_OFF 0b00000000 // 4 4 power off packet handler in low power mode: disabled (default) +#define SI443X_PACKET_HANDLER_POWER_DOWN_ON 0b00010000 // 4 4 enabled +#define SI443X_MANCHESTER_PREAMBLE_POL_LOW 0b00000000 // 3 3 preamble polarity in Manchester mode: low +#define SI443X_MANCHESTER_PREAMBLE_POL_HIGH 0b00001000 // 3 3 high (default) +#define SI443X_MANCHESTER_INVERTED_OFF 0b00000000 // 2 2 inverted Manchester encoding: disabled +#define SI443X_MANCHESTER_INVERTED_ON 0b00000100 // 2 2 enabled (default) +#define SI443X_MANCHESTER_OFF 0b00000000 // 1 1 Manchester encoding: disabled (default) +#define SI443X_MANCHESTER_ON 0b00000010 // 1 1 enabled +#define SI443X_WHITENING_OFF 0b00000000 // 0 0 data whitening: disabled (default) +#define SI443X_WHITENING_ON 0b00000001 // 0 0 enabled + +// SI443X_REG_MODULATION_MODE_CONTROL_2 +#define SI443X_TX_DATA_CLOCK_NONE 0b00000000 // 7 6 Tx data clock: disabled (default) +#define SI443X_TX_DATA_CLOCK_GPIO 0b01000000 // 7 6 GPIO pin +#define SI443X_TX_DATA_CLOCK_SDI 0b10000000 // 7 6 SDI pin +#define SI443X_TX_DATA_CLOCK_NIRQ 0b11000000 // 7 6 nIRQ pin +#define SI443X_TX_DATA_SOURCE_GPIO 0b00000000 // 5 4 Tx data source in direct mode: GPIO pin (default) +#define SI443X_TX_DATA_SOURCE_SDI 0b00010000 // 5 4 SDI pin +#define SI443X_TX_DATA_SOURCE_FIFO 0b00100000 // 5 4 FIFO +#define SI443X_TX_DATA_SOURCE_PN9 0b00110000 // 5 4 PN9 internal +#define SI443X_TX_RX_INVERTED_OFF 0b00000000 // 3 3 Tx/Rx data inverted: disabled (default) +#define SI443X_TX_RX_INVERTED_ON 0b00001000 // 3 3 enabled +#define SI443X_FREQUENCY_DEVIATION_MSB 0b00000000 // 2 2 frequency deviation MSB +#define SI443X_MODULATION_NONE 0b00000000 // 1 0 modulation type: unmodulated carrier (default) +#define SI443X_MODULATION_OOK 0b00000001 // 1 0 OOK +#define SI443X_MODULATION_FSK 0b00000010 // 1 0 FSK +#define SI443X_MODULATION_GFSK 0b00000011 // 1 0 GFSK + +// SI443X_REG_FREQUENCY_DEVIATION +#define SI443X_FREQUENCY_DEVIATION_LSB 0b00100000 // 7 0 frequency deviation LSB, Fd = 625 Hz * SI443X_FREQUENCY_DEVIATION, defaults to 20 kHz + +// SI443X_REG_FREQUENCY_OFFSET_1 + SI443X_REG_FREQUENCY_OFFSET_2 +#define SI443X_FREQUENCY_OFFSET_MSB 0x00 // 7 0 frequency offset: +#define SI443X_FREQUENCY_OFFSET_LSB 0x00 // 1 0 Foff = 156.25 Hz * (SI443X_BAND_SELECT + 1) * SI443X_FREQUENCY_OFFSET, defaults to 156.25 Hz + +// SI443X_REG_FREQUENCY_BAND_SELECT +#define SI443X_SIDE_BAND_SELECT_LOW 0b00000000 // 6 6 Rx LO tuning: below channel frequency (default) +#define SI443X_SIDE_BAND_SELECT_HIGH 0b01000000 // 6 6 above channel frequency +#define SI443X_BAND_SELECT_LOW 0b00000000 // 5 5 band select: low, 240 - 479.9 MHz +#define SI443X_BAND_SELECT_HIGH 0b00100000 // 5 5 high, 480 - 960 MHz (default) +#define SI443X_FREQUENCY_BAND_SELECT 0b00010101 // 4 0 frequency band select + +// SI443X_REG_NOM_CARRIER_FREQUENCY_1 + SI443X_REG_NOM_CARRIER_FREQUENCY_0 +#define SI443X_NOM_CARRIER_FREQUENCY_MSB 0b10111011 // 7 0 nominal carrier frequency: +#define SI443X_NOM_CARRIER_FREQUENCY_LSB 0b10000000 // 7 0 Fc = (SI443X_BAND_SELECT + 1)*10*(SI443X_FREQUENCY_BAND_SELECT + 24) + (SI443X_NOM_CARRIER_FREQUENCY - SI443X_FREQUENCY_OFFSET)/6400 [MHz] + +// SI443X_REG_FREQUENCY_HOPPING_CHANNEL_SEL +#define SI443X_FREQUENCY_HOPPING_CHANNEL 0x00 // 7 0 frequency hopping channel number + +// SI443X_REG_FREQUENCY_HOPPING_STEP_SIZE +#define SI443X_FREQUENCY_HOPPING_STEP_SIZE 0x00 // 7 0 frequency hopping step size + +// SI443X_REG_TX_FIFO_CONTROL_1 +#define SI443X_TX_FIFO_ALMOST_FULL_THRESHOLD 0x37 // 5 0 Tx FIFO almost full threshold + +// SI443X_REG_TX_FIFO_CONTROL_2 +#define SI443X_TX_FIFO_ALMOST_EMPTY_THRESHOLD 0x04 // 5 0 Tx FIFO almost full threshold + +// SI443X_REG_RX_FIFO_CONTROL +#define SI443X_RX_FIFO_ALMOST_FULL_THRESHOLD 0x37 // 5 0 Rx FIFO almost full threshold + +/*! + \class Si443x + + \brief Base class for Si443x series. All derived classes for Si443x (e.g. Si4431 or Si4432) inherit from this base class. + This class should not be instantiated directly from Arduino sketch, only from its derived classes. +*/ +class Si443x: public PhysicalLayer { + public: + // introduce PhysicalLayer overloads + using PhysicalLayer::transmit; + using PhysicalLayer::receive; + using PhysicalLayer::startTransmit; + using PhysicalLayer::readData; + + // constructor + + /*! + \brief Default constructor. + + \param mod Instance of Module that will be used to communicate with the radio. + */ + Si443x(Module* mod); + + // basic methods + + /*! + \brief Initialization method. + + \param br Bit rate of the FSK transmission in kbps (kilobits per second). + + \param freqDev Frequency deviation of the FSK transmission in kHz. + + \param rxBw Receiver bandwidth in kHz. + + \returns \ref status_codes + */ + int16_t begin(float br, float freqDev, float rxBw); + + /*! + \brief Reset method. Will reset the chip to the default state using SDN pin. + */ + void reset(); + + /*! + \brief Binary transmit method. Will transmit arbitrary binary data up to 64 bytes long. + For overloads to transmit Arduino String or C-string, see PhysicalLayer::transmit. + + \param data Binary data that will be transmitted. + + \param len Length of binary data to transmit (in bytes). + + \param addr Node address to transmit the packet to. + + \returns \ref status_codes + */ + int16_t transmit(uint8_t* data, size_t len, uint8_t addr = 0); + + /*! + \brief Binary receive method. Will attempt to receive arbitrary binary data up to 64 bytes long. + For overloads to receive Arduino String, see PhysicalLayer::receive. + + \param data Pointer to array to save the received binary data. + + \param len Number of bytes that will be received. Must be known in advance for binary transmissions. + + \returns \ref status_codes + */ + int16_t receive(uint8_t* data, size_t len); + + /*! + \brief Sets the module to sleep to save power. %Module will not be able to transmit or receive any data while in sleep mode. + %Module will wake up automatically when methods like transmit or receive are called. + + \returns \ref status_codes + */ + int16_t sleep(); + + /*! + \brief Sets the module to standby. + + \returns \ref status_codes + */ + int16_t standby(); + + /*! + \brief Enables direct transmission mode. While in direct mode, the module will not be able to transmit or receive packets. + + \param FRF 24-bit raw frequency value to start transmitting at. Required for quick frequency shifts in RTTY. + + \returns \ref status_codes + */ + int16_t transmitDirect(uint32_t frf = 0); + + /*! + \brief Enables direct reception mode. While in direct mode, the module will not be able to transmit or receive packets. + + \returns \ref status_codes + */ + int16_t receiveDirect(); + + /*! + \brief Disables direct mode and enables packet mode, allowing the module to receive packets. + + \returns \ref status_codes + */ + int16_t packetMode(); + + // interrupt methods + + /*! + \brief Sets interrupt service routine to call when IRQ activates. + + \param func ISR to call. + */ + void setIrqAction(void (*func)(void)); + + /*! + \brief Clears interrupt service routine to call when IRQ activates. + */ + void clearIrqAction(); + + /*! + \brief Interrupt-driven binary transmit method. Will start transmitting arbitrary binary data up to 64 bytes long. + + \param data Binary data that will be transmitted. + + \param len Length of binary data to transmit (in bytes). + + \param addr Node address to transmit the packet to. + + \returns \ref status_codes + */ + int16_t startTransmit(uint8_t* data, size_t len, uint8_t addr = 0); + + /*! + \brief Interrupt-driven receive method. IRQ will be activated when full valid packet is received. + + \returns \ref status_codes + */ + int16_t startReceive(); + + /*! + \brief Reads data that was received after calling startReceive method. This method reads len characters. + + \param data Pointer to array to save the received binary data. + + \param len Number of bytes that will be received. Must be known in advance for binary transmissions. + + \returns \ref status_codes + */ + int16_t readData(uint8_t* data, size_t len); + + // configuration methods + + /*! + \brief Sets FSK bit rate. Allowed values range from 0.123 to 256.0 kbps. + + \param br Bit rate to be set (in kbps). + + \returns \ref status_codes + */ + int16_t setBitRate(float br); + + /*! + \brief Sets FSK frequency deviation from carrier frequency. Allowed values range from 0.625 to 320.0 kHz. + + \param freqDev Frequency deviation to be set (in kHz). + + \returns \ref status_codes + */ + int16_t setFrequencyDeviation(float freqDev); + + /*! + \brief Sets receiver bandwidth. Allowed values range from 2.6 to 620.7 kHz. + + \param rxBw Receiver bandwidth to be set in kHz. + + \returns \ref status_codes + */ + int16_t setRxBandwidth(float rxBw); + + /*! + \brief Sets sync word. Up to 4 bytes can be set as sync word. + + \param syncWord Pointer to the array of sync word bytes. + + \param len Sync word length in bytes. + */ + int16_t setSyncWord(uint8_t* syncWord, size_t len); + + /*! + \brief Query modem for the packet length of received payload. + + \param update Update received packet length. Will return cached value when set to false. + + \returns Length of last received packet in bytes. + */ + size_t getPacketLength(bool update = true); + + /*! + \brief Sets transmission encoding. Only available in FSK mode. + + \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); + + /*! + \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. Only available in FSK mode with FSK modulation. + + \param sh Gaussian shaping bandwidth-time product that will be used for data shaping + + \returns \ref status_codes + */ + int16_t setDataShaping(float sh); + +#ifndef RADIOLIB_GODMODE + protected: +#endif + Module* _mod; + + float _br; + float _freqDev; + + size_t _packetLength; + bool _packetLengthQueried; + + int16_t setFrequencyRaw(float newFreq); + +#ifndef RADIOLIB_GODMODE + private: +#endif + bool findChip(); + void clearIRQFlags(); + int16_t config(); + int16_t updateClockRecovery(); + int16_t directMode(); +}; + +#endif