├── LICENSE ├── README.md ├── example ├── si4432_rx │ └── si4432_rx.ino └── si4432_tx │ └── si4432_tx.ino ├── library.json ├── library.properties ├── si4432.cpp └── si4432.h /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 nopnop2002 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Si443x ISM Transceiver Library for Arduino 2 | 3 | This library version is an enhancement of the [origial GitHub project](https://github.com/ADiea/si4432) by ahmetipkin. 4 | 5 | It can be an alternative to the [RadioHead library](http://www.airspayce.com/mikem/arduino/RadioHead) when you are focused on the Si443x and need a specific combination of parameters that are not supported by the RadioHead library (e.g. Manchester encoding). 6 | 7 | ## Boards 8 | 9 | Example of boards with the Silicon Labs Si4430/31/32 chip: 10 | 11 | - 2.54mm pitch 433MHz 12 | ![Si4432-433MHz-2 54mm](https://user-images.githubusercontent.com/6020549/163330289-770357cd-5bb4-4030-9347-edd0da6f21d3.JPG) 13 | 14 | - 1.27mm pitch 433MHz 15 | Pinout is [here](http://www.dorji.com/docs/data/DRF4432F20.pdf) 16 | ![Si4432-433MHz-1 27mm](https://user-images.githubusercontent.com/6020549/170854841-ab5318ae-7b31-4d11-98d9-719f48a49c36.JPG) 17 | 18 | ## Features 19 | 20 | - Support floating point values for frequency (MHz) and baud rate (kbps). 21 | 22 | - Select modulation type GFSK (default) or OOK. 23 | 24 | - Select Manchester encoding (default off). 25 | 26 | - Select packet handling (default on). 27 | 28 | - Select transmit power (default max). 29 | 30 | - Select blocking (default) or non-blocking transmit. 31 | 32 | - Select idle mode from standby, sleep, ready (default) and tune. 33 | 34 | - Boot config callback for individual chip settings (e.g. antenna switch) 35 | 36 | - Boot SyncWord validation: 37 | Sync Word 3 0x2D. 38 | Sync Word 2 0xD4. 39 | Sync Word 1 0x00. 40 | Sync Word 0 0x00. 41 | 42 | - Use SPI transactions. Legacy SPI communication works fine on ATMega with fixed CPU frequency, but doesn't work on CPUs like STM32 due to overclocking. 43 | 44 | - Configurable CS pin parameter. 45 | 46 | - Example code for transmit and receive. 47 | 48 | ## Breaking Changes 49 | 50 | - Support pin 0, use 0xFF when pin is not available. 51 | 52 | - Removed delay in turnOn() to allow non-blocking wakeup. 53 | 54 | - Removed receive processing from sendPacket function. 55 | 56 | - Removed waitForPacket function. 57 | 58 | ## Wiring 59 | 60 | |Si4432||UNO|MEGA|ESP8266|SAMD21| 61 | |:-:|:-:|:-:|:-:|:-:|:-:| 62 | |VCC|--|3.3V(*1)|3.3V|3.3V|3.3V| 63 | |GND|--|GND|GND|GND|GND| 64 | |SCK|--|D13(*2)|D52(*2)|IO14|SCK| 65 | |MISO|--|D12|D50|IO12|MISO| 66 | |MOSI|--|D11(*2)|D51(*2)|IO13|MOSI| 67 | |/SEL|--|D10(*2)|D10(*2)|IO15|(*3)| 68 | |SDN|--|D7(*2)|D7(*2)|IO4|(*3)| 69 | |/IRQ|--|D2|D2|IO5|(*3)| 70 | 71 | (*1) 72 | __The Si443X requires up to 30 mA and may not work reliably when supplied from the MCU on-board 3v3 voltage regulator.__ 73 | UNO's 3.3V output can supply 50 mA but the output current capacity of UNO-compatible devices is typically lower than that of official products. 74 | 75 | (*2) 76 | Si4432 is not 5V tolerant. You need to level shift between 5V and 3.3V, e.g. with this [level shifter](https://www.ti.com/lit/ds/symlink/txs0108e.pdf?ts=1647593549503). 77 | 78 | (*3) 79 | Configurable via constructor. -------------------------------------------------------------------------------- /example/si4432_rx/si4432_rx.ino: -------------------------------------------------------------------------------- 1 | #include "si4432.h" 2 | 3 | #define RESPONCE 1 4 | 5 | Si4432 radio(10, 7, 2); // CS, SDN, IRQ 6 | 7 | 8 | void setup() { 9 | delay(1000); 10 | Serial.begin(115200); 11 | if (radio.init() == false) { 12 | Serial.println("SI4432 not installed"); 13 | while(1); 14 | } 15 | radio.setBaudRate(70); 16 | radio.setFrequency(433); 17 | radio.readAll(); 18 | 19 | radio.startListening(); 20 | } 21 | 22 | void loop() { 23 | byte rxBuf[64] = {0}; 24 | byte rxLen = 0; 25 | 26 | bool recv = radio.isPacketReceived(); 27 | if (recv) { 28 | radio.getPacketReceived(&rxLen, rxBuf); 29 | Serial.print(millis()); 30 | Serial.print(" rxLen:"); 31 | Serial.println(rxLen, DEC); 32 | 33 | for (int i = 0; i < rxLen; ++i) { 34 | Serial.print(rxBuf[i], HEX); 35 | Serial.print(" "); 36 | } 37 | Serial.println(); 38 | 39 | for (int i = 0; i < rxLen; ++i) { 40 | char c = rxBuf[i]; 41 | if (c > 0x20 && c < 0xFF) { 42 | Serial.print(c); 43 | } else { 44 | Serial.print(" "); 45 | } 46 | } 47 | Serial.println(); 48 | 49 | #if RESPONCE 50 | byte txLen; 51 | byte txBuf[64]; 52 | for (int i = 0; i < rxLen; ++i) { 53 | char c = rxBuf[i]; 54 | if (islower(c)) { 55 | txBuf[i] = toupper(c); 56 | } else { 57 | txBuf[i] = tolower(c); 58 | } 59 | } 60 | txLen = rxLen; 61 | 62 | delay(30); 63 | Serial.println("Sending response... "); 64 | radio.sendPacket(txLen, txBuf); 65 | #endif 66 | 67 | radio.startListening(); // restart the listening. 68 | } else { 69 | //Serial.println("No packet this cycle"); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /example/si4432_tx/si4432_tx.ino: -------------------------------------------------------------------------------- 1 | #include "si4432.h" 2 | 3 | #define RESPONCE 1 4 | 5 | Si4432 radio(10, 7, 2); // CS, SDN, IRQ 6 | 7 | void setup() { 8 | delay(1000); 9 | Serial.begin(115200); 10 | if (radio.init() == false) { 11 | Serial.println("SI4432 not installed"); 12 | while(1); 13 | } 14 | radio.setBaudRate(70); 15 | radio.setFrequency(433); 16 | radio.readAll(); 17 | 18 | radio.startListening(); 19 | 20 | } 21 | 22 | void loop() { 23 | byte txBuf[64]; 24 | sprintf(txBuf,"Hello World %lu", millis()); 25 | byte txLen = strlen((char *)txBuf); 26 | 27 | bool sent = radio.sendPacket(txLen, txBuf); 28 | //Serial.print("sent="); 29 | //Serial.println(sent); 30 | if (sent) { 31 | 32 | #if RESPONCE 33 | byte rxLen; 34 | byte rxBuf[64]; 35 | unsigned long startMillis = millis(); 36 | Serial.println("startListening"); 37 | radio.startListening(); // restart the listening. 38 | while(1) { 39 | bool recv = radio.isPacketReceived(); 40 | if (recv) { 41 | radio.getPacketReceived(&rxLen, rxBuf); 42 | unsigned long receiveMillis = millis(); 43 | unsigned long elaspedMills = receiveMillis - startMillis; 44 | Serial.print(receiveMillis); 45 | Serial.print(" rxLen:"); 46 | Serial.print(rxLen, DEC); 47 | Serial.print(" elasped:"); 48 | Serial.print(elaspedMills); 49 | Serial.println(); 50 | 51 | for (int i = 0; i < rxLen; ++i) { 52 | Serial.print(rxBuf[i], HEX); 53 | Serial.print(" "); 54 | } 55 | Serial.println(); 56 | 57 | for (int i = 0; i < txLen; ++i) { 58 | char c = txBuf[i]; 59 | if (c > 0x20 && c < 0xFF) { 60 | Serial.print(c); 61 | } else { 62 | Serial.print(" "); 63 | } 64 | } 65 | Serial.print("-->"); 66 | for (int i = 0; i < rxLen; ++i) { 67 | char c = rxBuf[i]; 68 | if (c > 0x20 && c < 0xFF) { 69 | Serial.print(c); 70 | } else { 71 | Serial.print(" "); 72 | } 73 | } 74 | Serial.println(); 75 | break; 76 | } // end if 77 | 78 | if ( (millis() - startMillis) > 1000) { 79 | Serial.println("No responce within 1000 ms"); 80 | break; 81 | } 82 | 83 | } // end while 84 | #endif 85 | 86 | } else { 87 | Serial.println("sendPacket Fail"); 88 | delay(10000); 89 | } 90 | 91 | delay(1000); 92 | 93 | } 94 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Si4432", 3 | "keywords": "Si4430, Si4431, Si4432", 4 | "description": "Driver for SILICON LABS SI443x ISM transmitter, tested with module Si4432", 5 | "version": "1.1.2", 6 | "license": "MIT", 7 | "frameworks": "Arduino", 8 | "platforms": "*", 9 | "headers": "si4432.h", 10 | "authors": [ 11 | { 12 | "name": "ahmetipkin", 13 | "url": "", 14 | "maintainer": false 15 | }, 16 | { 17 | "name": "nopnop2002", 18 | "url": "", 19 | "maintainer": true 20 | }, 21 | { 22 | "name": "jensb", 23 | "url": "", 24 | "maintainer": false 25 | } 26 | ], 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/nopnop2002/Arduino-SI4432" 30 | } 31 | } -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Si4432 2 | version=1.1.2 3 | author=jensb 4 | maintainer=nopnop2002 5 | sentence=Driver for SILICON LABS Si443x. 6 | paragraph=Driver for SILICON LABS Si443x. 7 | category=Communication 8 | url=https://github.com/nopnop2002/Arduino-SI4432 9 | architectures=avr,esp8266,samd 10 | -------------------------------------------------------------------------------- /si4432.cpp: -------------------------------------------------------------------------------- 1 | #include "si4432.h" 2 | 3 | #define MAX_TRANSMIT_TIMEOUT 200 // [ms] 4 | //#define DEBUG // note: readback of registers and serial output will slow down operations 5 | 6 | //values here are kept in khz x 10 format (for not to deal with decimals) - look at AN440 page 26 for whole table 7 | const uint16_t IFFilterTable[][2] = { { 322, 0x26 }, 8 | { 3355, 0x88 }, 9 | { 3618, 0x89 }, 10 | { 4202, 0x8A }, 11 | { 4684, 0x8B }, 12 | { 5188, 0x8C }, 13 | { 5770, 0x8D }, 14 | { 6207, 0x8E } 15 | }; 16 | 17 | Si4432::Si4432(uint8_t csPin, uint8_t sdnPin, uint8_t intPin) : 18 | _csPin(csPin), 19 | _sdnPin(sdnPin), 20 | _intPin(intPin), 21 | _freqCarrier(433.0), 22 | _kbps(100), 23 | _freqChannel(0), 24 | _modulationType(GFSK), 25 | _idleMode(Ready), 26 | _transmitPower(7), 27 | _directTie(true), 28 | _manchesterEnabled(false), 29 | _manchesterInverted(false), 30 | _packetHandlingEnabled(true), 31 | _lsbFirst(false), 32 | _sendBlocking(true), 33 | _spi(nullptr), 34 | _spiClock(0), 35 | _packageSign(0xDEAD), 36 | _sendStart(0), 37 | _configCallback(nullptr) { 38 | } 39 | 40 | void Si4432::setFrequency(int frequency) { 41 | setFrequency((double)frequency); 42 | } 43 | 44 | void Si4432::setFrequency(unsigned long frequency) { 45 | setFrequency((double)frequency); 46 | } 47 | 48 | void Si4432::setFrequency(double frequency) { 49 | 50 | if ((frequency < 240) || (frequency > 930)) 51 | return; // invalid frequency 52 | 53 | _freqCarrier = frequency; 54 | 55 | byte highBand = 0; 56 | if (frequency >= 480) { 57 | highBand = 1; 58 | } 59 | 60 | float fPart = frequency / (10 * (highBand + 1)) - 24; 61 | 62 | uint8_t freqBand = (uint8_t)fPart; // truncate the int 63 | 64 | uint16_t freqCarrier = (fPart - freqBand) * 64000; 65 | 66 | // rx sideband is always on (bit 6) 67 | byte vals[3] = { (byte)((1 << 6) | (highBand << 5) | (freqBand & 0x3F)), 68 | (byte)(freqCarrier >> 8), 69 | (byte)(freqCarrier & 0xFF) 70 | }; 71 | BurstWrite(REG_FREQBAND, vals, 3); 72 | } 73 | 74 | void Si4432::setCommsSignature(uint16_t signature) { 75 | _packageSign = signature; 76 | 77 | ChangeRegister(REG_TRANSMIT_HEADER3, _packageSign >> 8); // header (signature) byte 3 val 78 | ChangeRegister(REG_TRANSMIT_HEADER2, (_packageSign & 0xFF)); // header (signature) byte 2 val 79 | 80 | ChangeRegister(REG_CHECK_HEADER3, _packageSign >> 8); // header (signature) byte 3 val for receive checks 81 | ChangeRegister(REG_CHECK_HEADER2, (_packageSign & 0xFF)); // header (signature) byte 2 val for receive checks 82 | 83 | #ifdef DEBUG 84 | Serial.println("Package signature is set!"); 85 | #endif 86 | } 87 | 88 | bool Si4432::init(SPIClass* spi, uint32_t spiClock) { 89 | _spi = spi; 90 | _spiClock = spiClock; 91 | 92 | if (_intPin != 0xFF) 93 | { 94 | pinMode(_intPin, INPUT); 95 | } 96 | 97 | if (_sdnPin != 0xFF) { 98 | pinMode(_sdnPin, OUTPUT); 99 | digitalWrite(_sdnPin, LOW); // turn chip on 100 | } else { 101 | delay(50); // why wait so long? 102 | } 103 | 104 | if (_csPin != 0xFF) { 105 | pinMode(_csPin, OUTPUT); 106 | digitalWrite(_csPin, HIGH); // set SPI CS pin high, so chip would know we don't use it 107 | } 108 | 109 | _spi->begin(); 110 | 111 | #ifdef DEBUG 112 | Serial.println("SPI initialized"); 113 | #endif 114 | 115 | reset(); 116 | 117 | // Check Sync Word 118 | byte syncWord3 = ReadRegister(REG_SYNC_WORD3); 119 | byte syncWord2 = ReadRegister(REG_SYNC_WORD2); 120 | byte syncWord1 = ReadRegister(REG_SYNC_WORD1); 121 | byte syncWord0 = ReadRegister(REG_SYNC_WORD0); 122 | 123 | #ifdef DEBUG 124 | Serial.print("syncWord3="); 125 | Serial.print(syncWord3, HEX); 126 | Serial.print(" syncWord2="); 127 | Serial.print(syncWord2, HEX); 128 | Serial.print(" syncWord1="); 129 | Serial.print(syncWord1, HEX); 130 | Serial.print(" syncWord0="); 131 | Serial.print(syncWord0, HEX); 132 | Serial.println(); 133 | #endif 134 | 135 | if (syncWord3 != 0x2D || syncWord2 != 0xD4 || syncWord1 != 0 || syncWord0 != 0) 136 | return false; 137 | else 138 | return true; 139 | 140 | if (getDeviceStatus() & 0x4) 141 | { 142 | // frequency error 143 | return false; 144 | } 145 | } 146 | 147 | void Si4432::boot() { 148 | /* 149 | byte currentFix[] = { 0x80, 0x40, 0x7F }; 150 | BurstWrite(REG_CHARGEPUMP_OVERRIDE, currentFix, 3); // refer to AN440 for reasons 151 | */ 152 | 153 | ChangeRegister(REG_AFC_TIMING_CONTROL, 0x02); // refer to AN440 for reasons 154 | ChangeRegister(REG_AFC_LIMITER, 0xFF); // write max value - excel file did that. 155 | ChangeRegister(REG_AGC_OVERRIDE, 0x60); // max gain control 156 | ChangeRegister(REG_AFC_LOOP_GEARSHIFT_OVERRIDE, 0x3C); // turn off AFC 157 | 158 | if (_packetHandlingEnabled) { 159 | ChangeRegister(REG_DATAACCESS_CONTROL, 0xAD | (_lsbFirst? 1 << 6 : 0)); // enable rx packet handling, enable tx packet handling, enable CRC, use CRC-IBM 160 | ChangeRegister(REG_HEADER_CONTROL1, 0x0C); // no broadcast address control, enable check headers for bytes 3 & 2 161 | ChangeRegister(REG_HEADER_CONTROL2, 0x22); // enable headers byte 3 & 2, no fixed package length, sync word 3 & 2 162 | ChangeRegister(REG_PREAMBLE_LENGTH, 0x08); // 8 * 4 bits = 32 bits (4 bytes) preamble length 163 | ChangeRegister(REG_PREAMBLE_DETECTION, 0x3A); // validate 7 * 4 bits of preamble in a package 164 | setCommsSignature(_packageSign); // default signature 165 | } else { 166 | ChangeRegister(REG_DATAACCESS_CONTROL, _lsbFirst? 1 << 6 : 0); // disable packet handling 167 | } 168 | 169 | ChangeRegister(REG_CHANNEL_STEPSIZE, 0x64); // each channel is of 100 * 10 kHz = 1 MHz interval 170 | 171 | // backup current settings 172 | byte oldTransmitPower = _transmitPower; 173 | _transmitPower = 0xFF; 174 | float oldFreqCarrier = _freqCarrier; 175 | _freqCarrier = 0; 176 | float oldKbps = _kbps; 177 | _kbps = 0; 178 | uint16_t oldFreqChannel = _freqChannel; 179 | _freqChannel = 0xFFFF; 180 | 181 | if (_configCallback) { 182 | _configCallback(); 183 | } 184 | 185 | // perform missing config 186 | if (_transmitPower == 0xFF) 187 | setTransmitPower(oldTransmitPower, _directTie); // default max power (7), direct-tie enabled 188 | if (_freqCarrier == 0) 189 | setFrequency(oldFreqCarrier); // default freq is 433 MHz 190 | if (_kbps == 0) 191 | setBaudRate(oldKbps); // default baud rate is 100 kpbs 192 | if (_freqChannel == 0xFFFF) 193 | setChannel((byte)oldFreqChannel); // default channel is 0 194 | 195 | switchMode(_idleMode); 196 | } 197 | 198 | bool Si4432::sendPacket(uint8_t length, const byte* data) { 199 | 200 | bool result = false; 201 | 202 | if (length <= 64) { 203 | if (length) { 204 | // write data into FIFO 205 | clearTxFIFO(); 206 | ChangeRegister(REG_PKG_LEN, length); 207 | BurstWrite(REG_FIFO, data, length); 208 | } 209 | 210 | // enable interrupt for package sent 211 | enableInt(INT_PKSENT); 212 | 213 | // read interrupt registers to clean them 214 | getIntStatus(); 215 | 216 | // start transmit (will automatically return to previously assigned mode after transmit is completed) 217 | switchMode(_idleMode | TXMode); 218 | _sendStart = millis(); 219 | 220 | // wait for transmit to complete 221 | result = true; 222 | if (_sendBlocking) { 223 | result = waitTransmitCompleted(); 224 | if (!result) { 225 | // transmit timeout, reset 226 | reset(); 227 | } 228 | } 229 | } 230 | 231 | return result; 232 | } 233 | 234 | bool Si4432::waitTransmitCompleted() { 235 | while (millis() - _sendStart < MAX_TRANSMIT_TIMEOUT) { 236 | 237 | // check interrupt pin if configured 238 | if (_intPin != 0xFF && digitalRead(_intPin)) { 239 | // no interrupt, wait 240 | continue; 241 | } 242 | 243 | // typical state change without interrupt signal: 244 | // 0x20 (TX enabled: FIFO almost empty = itxffaem) -> 0x80 (TX FIFO empty = ifferr) -> 0xA0 (itxffaem + ifferr) -> 0xA4 (packet send, ipksent) 245 | uint16_t intStatus = getIntStatus(); 246 | #ifdef DEBUG 247 | Serial.print("sendPacket REG_INT_STATUS="); 248 | Serial.println(intStatus, HEX); 249 | #endif 250 | if (intStatus & INT_PKSENT) { 251 | return true; 252 | } 253 | 254 | delay(1); 255 | } 256 | 257 | // timeout 258 | #ifdef DEBUG 259 | Serial.println("Transmit Timeout --"); 260 | #endif 261 | 262 | return false; 263 | } 264 | 265 | void Si4432::getPacketReceived(uint8_t* length, byte* readData) { 266 | 267 | *length = ReadRegister(REG_RECEIVED_LENGTH); 268 | 269 | BurstRead(REG_FIFO, readData, *length); 270 | 271 | clearRxFIFO(); // which will also clear the interrupts 272 | } 273 | 274 | void Si4432::setChannel(byte channel) { 275 | _freqChannel = channel; 276 | ChangeRegister(REG_FREQCHANNEL, channel); 277 | } 278 | 279 | void Si4432::switchMode(byte mode) { 280 | #ifdef DEBUG 281 | Serial.print("switchMode mode=0x"); 282 | Serial.println(mode, HEX); 283 | #endif 284 | ChangeRegister(REG_STATE, mode); // operation mode 285 | #ifdef XDEBUG 286 | byte val = getDeviceStatus(); 287 | if (val == 0 || val == 0xFF) { 288 | Serial.print(val, HEX); 289 | Serial.println(" -- WHAT THE HELL!!"); 290 | } else if (val & 0x4) { 291 | // frequency error 292 | Serial.print("switchMode freqerr"); 293 | } 294 | #endif 295 | } 296 | 297 | void Si4432::ChangeRegister(Registers reg, byte value) { 298 | BurstWrite(reg, &value, 1); 299 | 300 | #ifdef DEBUG 301 | byte _value; 302 | BurstRead(reg, &_value, 1); 303 | if (value != _value) { 304 | Serial.println("ChangeRegister failed"); 305 | Serial.print("reg=0x"); 306 | Serial.print(reg, HEX); 307 | Serial.print(" value=0x"); 308 | Serial.println(_value, HEX); 309 | } 310 | #endif 311 | } 312 | 313 | void Si4432::setBaudRate(int kbps) { 314 | setBaudRate((double)kbps); 315 | } 316 | 317 | void Si4432::setBaudRate(uint16_t kbps) { 318 | setBaudRate((double)kbps); 319 | } 320 | 321 | void Si4432::setBaudRate(double kbps) { 322 | 323 | // chip normally supports very low bps values, but they are cumbersome to implement - so I just didn't implement lower bps values 324 | if ((kbps < 1) || (kbps > 256)) 325 | return; 326 | 327 | _kbps = kbps; 328 | 329 | // set modulation (GFSK or OOK) and frequency deviation 330 | byte modulationMode1 = (kbps < 30 ? 1 << 5 : 0) | (_manchesterInverted? 1 << 2 : 0) | (_manchesterEnabled? 1 << 1 : 0); // txdtrtscale (bit 5), enmaninv (bit2), enmanch (bit 1) 331 | byte modulationMode2 = _modulationType == OOK? 0x21 : 0x23; // 0x20 = dtmod = FIFO, 0x01 = modtyp = OOK, 0x03 = modtyp = GFSK 332 | byte freqDev = round(((kbps <= 10 ? 15 : 150) * 1000.0) / 625.0); // 15kHz / 150 kHz, msb of kpbs to 3rd bit of register 333 | 334 | byte modulationVals[] = { modulationMode1, 335 | modulationMode2, 336 | freqDev 337 | }; 338 | BurstWrite(REG_MODULATION_MODE1, modulationVals, 3); // 0x70 339 | 340 | // set tx data rate 341 | uint16_t bpsRegVal = round((kbps * (kbps < 30 ? 1 << 21 : 1 << 16)) / 1000); 342 | #ifdef DEBUG 343 | Serial.print("kbps: "); 344 | Serial.println(kbps); 345 | Serial.print("tx rate reg value: "); 346 | Serial.println(bpsRegVal, HEX); 347 | #endif 348 | byte datarateVals[] = { (byte)(bpsRegVal >> 8), 349 | (byte)(bpsRegVal & 0xFF) 350 | }; 351 | BurstWrite(REG_TX_DATARATE1, datarateVals, 2); // 0x6E 352 | 353 | // set rx timings 354 | uint16_t minBandwidth = (uint16_t)(freqDev << 1) + (uint16_t)round(kbps); 355 | #ifdef DEBUG 356 | Serial.print("min Bandwidth value: "); 357 | Serial.println(minBandwidth, HEX); 358 | #endif 359 | byte IFValue = 0xFF; 360 | //since the table is ordered (from low to high), just find the 'minimum bandwidth which is greater than required' 361 | for (byte i = 0; i < 8; ++i) { 362 | if (IFFilterTable[i][0] >= (minBandwidth * 10)) { 363 | IFValue = IFFilterTable[i][1]; 364 | break; 365 | } 366 | } 367 | #ifdef DEBUG 368 | Serial.print("Selected IF value: "); 369 | Serial.println(IFValue, HEX); 370 | #endif 371 | 372 | ChangeRegister(REG_IF_FILTER_BW, IFValue); 373 | 374 | byte dwn3_bypass = (IFValue & 0x80) ? 1 : 0; // if msb is set 375 | byte ndec_exp = (IFValue >> 4) & 0x07; // only 3 bits 376 | 377 | uint16_t rxOversampling = round((500.0 * (1 + 2 * dwn3_bypass)) / ((pow(2, ndec_exp - 3)) * kbps)); 378 | 379 | uint32_t ncOffset = ceil((kbps * (pow(2, ndec_exp + 20))) / (500.0 * (1 + 2 * dwn3_bypass))); 380 | 381 | uint16_t crGain = 2 + ((65535 * (int64_t)kbps) / ((int64_t) rxOversampling * freqDev)); 382 | byte crMultiplier = 0x00; 383 | if (crGain > 0x7FF) { 384 | crGain = 0x7FF; 385 | } 386 | #ifdef DEBUG 387 | Serial.print("dwn3_bypass value: "); 388 | Serial.println(dwn3_bypass, HEX); 389 | Serial.print("ndec_exp value: "); 390 | Serial.println(ndec_exp, HEX); 391 | Serial.print("rxOversampling value: "); 392 | Serial.println(rxOversampling, HEX); 393 | Serial.print("ncOffset value: "); 394 | Serial.println(ncOffset, HEX); 395 | Serial.print("crGain value: "); 396 | Serial.println(crGain, HEX); 397 | Serial.print("crMultiplier value: "); 398 | Serial.println(crMultiplier, HEX); 399 | #endif 400 | 401 | byte timingVals[] = { (byte)(rxOversampling & 0xFF), 402 | (byte)(((rxOversampling & 0x0700) >> 3) | ((ncOffset >> 16) & 0x0F)), 403 | (byte)((ncOffset >> 8) & 0xFF), 404 | (byte)(ncOffset & 0xFF), 405 | (byte)(((crGain & 0x0700) >> 8) | crMultiplier), 406 | (byte)(crGain & 0xFF) 407 | }; 408 | BurstWrite(REG_CLOCK_RECOVERY_OVERSAMPLING, timingVals, 6); 409 | } 410 | 411 | byte Si4432::ReadRegister(Registers reg) { 412 | byte val[1]; 413 | BurstRead(reg, val, 1); 414 | return val[0]; 415 | } 416 | 417 | void Si4432::BurstWrite(Registers startReg, const byte value[], uint8_t length) { 418 | 419 | byte regVal = (byte) startReg | 0x80; // set MSB 420 | 421 | if (_csPin != 0xFF) { 422 | digitalWrite(_csPin, LOW); 423 | } 424 | _spi->beginTransaction(SPISettings(_spiClock, MSBFIRST, SPI_MODE0)); 425 | _spi->transfer(regVal); 426 | 427 | for (byte i = 0; i < length; ++i) { 428 | #ifdef DEBUG 429 | Serial.print(" writing: "); 430 | Serial.print((regVal != 0xFF ? (regVal + i) & 0x7F : 0x7F), HEX); 431 | Serial.print(" | "); 432 | Serial.println(value[i], HEX); 433 | #endif 434 | _spi->transfer(value[i]); 435 | } 436 | 437 | if (_csPin != 0xFF) { 438 | digitalWrite(_csPin, HIGH); 439 | } 440 | _spi->endTransaction(); 441 | } 442 | 443 | void Si4432::BurstRead(Registers startReg, byte value[], uint8_t length) { 444 | 445 | byte regVal = (byte) startReg & 0x7F; // clear MSB 446 | 447 | if (_csPin != 0xFF) { 448 | digitalWrite(_csPin, LOW); 449 | } 450 | _spi->beginTransaction(SPISettings(_spiClock, MSBFIRST, SPI_MODE0)); 451 | _spi->transfer(regVal); 452 | 453 | for (byte i = 0; i < length; ++i) { 454 | value[i] = _spi->transfer(0xFF); 455 | 456 | #ifdef DEBUG 457 | Serial.print(" reading: "); 458 | Serial.print((regVal != 0x7F ? (regVal + i) & 0x7F : 0x7F), HEX); 459 | Serial.print(" | "); 460 | Serial.println(value[i], HEX); 461 | #endif 462 | } 463 | 464 | if (_csPin != 0xFF) { 465 | digitalWrite(_csPin, HIGH); 466 | } 467 | _spi->endTransaction(); 468 | } 469 | 470 | void Si4432::readAll() { 471 | 472 | byte allValues[0x7F]; 473 | 474 | BurstRead(REG_DEV_TYPE, allValues, 0x7F); 475 | 476 | #ifdef DEBUG 477 | for (byte i = 0; i < 0x7f; ++i) { 478 | Serial.print("REG("); 479 | Serial.print((int) REG_DEV_TYPE + i, HEX); 480 | Serial.print(") : "); 481 | Serial.println((int) allValues[i], HEX); 482 | } 483 | #endif 484 | } 485 | 486 | void Si4432::clearTxFIFO() { 487 | ChangeRegister(REG_OPERATION_CONTROL, 0x01); 488 | ChangeRegister(REG_OPERATION_CONTROL, 0x00); 489 | } 490 | 491 | void Si4432::clearRxFIFO() { 492 | ChangeRegister(REG_OPERATION_CONTROL, 0x02); 493 | ChangeRegister(REG_OPERATION_CONTROL, 0x00); 494 | } 495 | 496 | void Si4432::clearFIFO() { 497 | ChangeRegister(REG_OPERATION_CONTROL, 0x03); 498 | ChangeRegister(REG_OPERATION_CONTROL, 0x00); 499 | } 500 | 501 | void Si4432::softReset() { 502 | reset(true); 503 | } 504 | 505 | void Si4432::hardReset() { 506 | reset(); 507 | } 508 | 509 | void Si4432::reset(bool soft) { 510 | 511 | #ifdef DEBUG 512 | Serial.println("resetting Si4432 ..."); 513 | #endif 514 | 515 | if (soft || _sdnPin == 0xFF) { 516 | // soft reset command 517 | switchMode(Ready); 518 | switchMode(Reset); 519 | } else { 520 | // toggle shutdown pin off/on 521 | turnOff(); 522 | delay(1); 523 | turnOn(); 524 | delay(15); 525 | } 526 | 527 | // wait for clock to become ready (changing registers will not work without it) 528 | int noTimeout = 15; // [ms] 529 | while (!isClockReady() && noTimeout) { 530 | delay(1); 531 | noTimeout--; 532 | } 533 | 534 | if (noTimeout) 535 | { 536 | #ifdef DEBUG 537 | Serial.println("Si4432 reset successful"); 538 | #endif 539 | boot(); 540 | } 541 | #ifdef DEBUG 542 | else 543 | { 544 | Serial.println("Si4432 not responding after reset, check wiring"); 545 | } 546 | #endif 547 | } 548 | 549 | void Si4432::startListening() { 550 | 551 | clearRxFIFO(); // clear first, so it doesn't overflow if packet is big 552 | 553 | // set interrupts on for package received and CRC error 554 | #ifdef DEBUG 555 | enableInt(INT_SWDET | INT_PREAVAL | INT_PKVALID | INT_CRCERROR); 556 | #else 557 | enableInt(INT_PKVALID | INT_CRCERROR); 558 | #endif 559 | 560 | //read interrupt registers to clean them 561 | getIntStatus(); 562 | 563 | switchMode(_idleMode | RXMode); 564 | } 565 | 566 | bool Si4432::isPacketReceived() { 567 | 568 | if ((_intPin != 0xFF) && (digitalRead(_intPin) != 0)) { 569 | return false; // if no interrupt occurred, no packet received is assumed (since startListening will be called prior, this assumption is enough) 570 | } 571 | // check for package received status interrupt register 572 | uint16_t intStat = getIntStatus(); 573 | #ifdef DEBUG 574 | Serial.print("isPacketReceived REG_INT_STATUS="); 575 | Serial.println(intStat, HEX); 576 | 577 | if (intStat & INT_PREAVAL) { //interrupt occurred, check it && read the Interrupt Status1 register for 'preamble ' 578 | 579 | Serial.print("HEY!! HEY!! Valid Preamble detected -- "); 580 | 581 | } 582 | 583 | if (intStat & INT_SWDET) { //interrupt occurred, check it && read the Interrupt Status1 register for 'preamble ' 584 | 585 | Serial.print("HEY!! HEY!! SYNC WORD detected -- "); 586 | 587 | } 588 | #endif 589 | 590 | if (intStat & INT_PKVALID) { //interrupt occurred, check it && read the Interrupt Status1 register for 'valid packet' 591 | switchMode(TuneMode); // if packet came, get out of Rx mode till the packet is read out. Keep PLL on for fast reaction 592 | #ifdef DEBUG 593 | Serial.print("Packet detected -- "); 594 | Serial.println(intStat, HEX); 595 | #endif 596 | return true; 597 | } else if (intStat & INT_CRCERROR) { // packet crc error 598 | switchMode(Ready); // get out of Rx mode till buffers are cleared 599 | #ifdef DEBUG 600 | Serial.print("CRC Error in Packet detected!-- "); 601 | Serial.println(intStat, HEX); 602 | #endif 603 | clearRxFIFO(); 604 | switchMode(_idleMode | RXMode); // get back to work 605 | return false; 606 | } 607 | 608 | //no relevant interrupt? no packet! 609 | 610 | return false; 611 | } 612 | 613 | void Si4432::turnOn() { 614 | if (_sdnPin != 0xFF) 615 | digitalWrite(_sdnPin, LOW); // turn on the chip 616 | } 617 | 618 | void Si4432::turnOff() { 619 | if (_sdnPin != 0xFF) 620 | digitalWrite(_sdnPin, HIGH); // turn off the chip 621 | if (_csPin != 0xFF) 622 | digitalWrite(_csPin, HIGH); // set SPI CS pin high, so chip would know we don't use it 623 | } 624 | 625 | uint8_t Si4432::getIntPin() const { 626 | return _intPin; 627 | } 628 | 629 | uint16_t Si4432::getIntStatus() { 630 | 631 | uint16_t intStatus; 632 | 633 | BurstRead(REG_INT_STATUS1, (byte*)&intStatus, 2); 634 | 635 | return intStatus; 636 | } 637 | 638 | void Si4432::enableInt(uint16_t flags) 639 | { 640 | BurstWrite(REG_INT_ENABLE1, (byte*)&flags, 2); 641 | } 642 | 643 | void Si4432::setModulationType(ModulationType modulationType) { 644 | _modulationType = modulationType; 645 | } 646 | 647 | void Si4432::setManchesterEncoding(bool enabled, bool inverted) { 648 | _manchesterEnabled = enabled; 649 | _manchesterInverted = inverted; 650 | } 651 | 652 | void Si4432::setPacketHandling(bool enabled, bool lsbFirst) { 653 | _packetHandlingEnabled = enabled; 654 | _lsbFirst = lsbFirst; 655 | } 656 | 657 | void Si4432::setConfigCallback(void (*callback)()) { 658 | _configCallback = callback; 659 | } 660 | 661 | void Si4432::setTransmitPower(byte level, bool directTie) { 662 | _transmitPower = min(level, 7); 663 | _directTie = directTie; 664 | ChangeRegister(REG_TX_POWER, 0x10 | (directTie? 1 << 4 : 0) | (level & 0x7)); 665 | } 666 | 667 | void Si4432::setSendBlocking(bool enabled) { 668 | _sendBlocking = enabled; 669 | } 670 | 671 | void Si4432::setIdleMode(byte mode) { 672 | _idleMode = mode; 673 | } 674 | 675 | byte Si4432::getDeviceStatus() 676 | { 677 | return ReadRegister(REG_DEV_STATUS); 678 | } 679 | 680 | bool Si4432::isClockReady() 681 | { 682 | // notes: - an interrupt status of 0xFF is possible but not probable (see datasheet) 683 | // - the value 0xFF is typically returned when the device is not responding 684 | // - on power up the typical interrupt sequence is 0 -> 1 (ipor) -> 2 (ichiprdy) 685 | byte status = ReadRegister(REG_INT_STATUS2); 686 | return (status != 0xFF) && (status & 0x02); 687 | } 688 | -------------------------------------------------------------------------------- /si4432.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Si4432 library for Arduino 3 | * 4 | * made by Ahmet (theGanymedes) Ipkin 2014 5 | * 6 | * (c) 2022 nopnop2002 7 | * (c) 2023 Jens B. 8 | * 9 | * SPDX-License-Identifier: MIT 10 | */ 11 | 12 | #ifndef si4432_H_ 13 | #define si4432_H_ 14 | 15 | #include "Arduino.h" 16 | #include 17 | 18 | /* 19 | * To perform a send/receive you must: 20 | * 21 | * 1- Create an instance 22 | * 2- Configure parameters with setXXXX() where defaults are not suitable 23 | * 3- Init SPI with init() 24 | * 4a- Send with sendPacket() 25 | * 4b- Receive with startListening() + isPacketReceived() + getPacketReceived() 26 | * 27 | * According to the data sheet, you can change any register value and most will get to work after going into IDLE state and back (to RX/TX) 28 | * (some are hot - changes right away) I didn't test this - but why not? :) 29 | * 30 | * Antenna configuration depends on board. If an antenna switch exists that can be controlled via GPIO the GPIO function must be configured 31 | * after init or reset by the application. As internal conditions (e.g. transmit timeout) can also trigger a reset, implementation is best 32 | * done by overriding boot(). Here is an example for 1 antenna, 1 switch and separate TX/RX paths: 33 | * 34 | * setTransmitPower(7, false); // disable direct-tie 35 | * ChangeRegister(REG_GPIO0_CONF, 0b10010); // Tx state output 36 | * ChangeRegister(REG_GPIO1_CONF, 0b10101); // Rx state output 37 | * ChangeRegister(REG_GPIO2_CONF, 0b100011); // passive input with 200 kOhm pullup 38 | * 39 | * When the packet handler is enabled the following packet configuration is used: 40 | * - 4 byte preamble (0/1 bit sequence) 41 | * - 2 byte sync word (0x2DD4) 42 | * - 2 byte header, defined with setCommsSignature() 43 | * - 1 byte packet length 44 | * - variable data bytes 45 | * - 1 byte CRC-16 (IBM) 46 | * 47 | * The default packet configuration can be changed using setConfigCallback() or in the source, see data sheet for more details. 48 | * 49 | */ 50 | 51 | class Si4432 { 52 | public: 53 | enum ModulationType { 54 | NONE = 0x00, 55 | OOK = 0x01, 56 | FSK = 0x02, 57 | GFSK = 0x03, 58 | }; 59 | 60 | enum OperationMode { 61 | StandbyMode = 0x00, // 800 µs wakeup, 450 nA 62 | SleepMode = 0x10, // 800 µs wakeup, 1 µA, 32 kHz clock on 63 | Ready = 0x01, // 200 µs wakeup, 800 µA, 30 MHz clock on 64 | TuneMode = 0x02, // 200 µs wakeup, 8.5 mA, PLL on 65 | RXMode = 0x04, // 18.5 mA 66 | TXMode = 0x08, // .. 30.0 mA 67 | Reset = 0x80, 68 | }; 69 | 70 | enum Registers { 71 | REG_DEV_TYPE = 0x00, 72 | REG_DEV_VERSION = 0x01, 73 | REG_DEV_STATUS = 0x02, 74 | 75 | REG_INT_STATUS1 = 0x03, 76 | REG_INT_STATUS2 = 0x04, 77 | REG_INT_ENABLE1 = 0x05, 78 | REG_INT_ENABLE2 = 0x06, 79 | REG_STATE = 0x07, 80 | REG_OPERATION_CONTROL = 0x08, 81 | 82 | REG_GPIO0_CONF = 0x0B, 83 | REG_GPIO1_CONF = 0x0C, 84 | REG_GPIO2_CONF = 0x0D, 85 | REG_IOPORT_CONF = 0x0E, 86 | 87 | REG_IF_FILTER_BW = 0x1C, 88 | REG_AFC_LOOP_GEARSHIFT_OVERRIDE = 0x1D, 89 | REG_AFC_TIMING_CONTROL = 0x1E, 90 | REG_CLOCK_RECOVERY_GEARSHIFT = 0x1F, 91 | REG_CLOCK_RECOVERY_OVERSAMPLING = 0x20, 92 | REG_CLOCK_RECOVERY_OFFSET2 = 0x21, 93 | REG_CLOCK_RECOVERY_OFFSET1 = 0x22, 94 | REG_CLOCK_RECOVERY_OFFSET0 = 0x23, 95 | REG_CLOCK_RECOVERY_TIMING_GAIN1 = 0x24, 96 | REG_CLOCK_RECOVERY_TIMING_GAIN0 = 0x25, 97 | REG_RSSI = 0x26, 98 | REG_RSSI_THRESHOLD = 0x27, 99 | 100 | REG_AFC_LIMITER = 0x2A, 101 | REG_AFC_CORRECTION_READ = 0x2B, 102 | 103 | REG_DATAACCESS_CONTROL = 0x30, 104 | REG_EZMAC_STATUS = 0x31, 105 | REG_HEADER_CONTROL1 = 0x32, 106 | REG_HEADER_CONTROL2 = 0x33, 107 | REG_PREAMBLE_LENGTH = 0x34, 108 | REG_PREAMBLE_DETECTION = 0x35, 109 | REG_SYNC_WORD3 = 0x36, 110 | REG_SYNC_WORD2 = 0x37, 111 | REG_SYNC_WORD1 = 0x38, 112 | REG_SYNC_WORD0 = 0x39, 113 | REG_TRANSMIT_HEADER3 = 0x3A, 114 | REG_TRANSMIT_HEADER2 = 0x3B, 115 | REG_TRANSMIT_HEADER1 = 0x3C, 116 | REG_TRANSMIT_HEADER0 = 0x3D, 117 | 118 | REG_PKG_LEN = 0x3E, 119 | 120 | REG_CHECK_HEADER3 = 0x3F, 121 | REG_CHECK_HEADER2 = 0x40, 122 | REG_CHECK_HEADER1 = 0x41, 123 | REG_CHECK_HEADER0 = 0x42, 124 | 125 | REG_RECEIVED_HEADER3 = 0x47, 126 | REG_RECEIVED_HEADER2 = 0x48, 127 | REG_RECEIVED_HEADER1 = 0x49, 128 | REG_RECEIVED_HEADER0 = 0x4A, 129 | 130 | REG_RECEIVED_LENGTH = 0x4B, 131 | 132 | REG_CHARGEPUMP_OVERRIDE = 0x58, // not documented in AN440 Rev. 0.9 133 | REG_DIVIDER_CURRENT_TRIM = 0x59, // not documented in AN440 Rev. 0.9 134 | REG_VCO_CURRENT_TRIM = 0x5A, // not documented in AN440 Rev. 0.9 135 | 136 | REG_AGC_OVERRIDE = 0x69, 137 | 138 | REG_TX_POWER = 0x6D, 139 | REG_TX_DATARATE1 = 0x6E, 140 | REG_TX_DATARATE0 = 0x6F, 141 | 142 | REG_MODULATION_MODE1 = 0x70, 143 | REG_MODULATION_MODE2 = 0x71, 144 | 145 | REG_FREQ_DEVIATION = 0x72, 146 | REG_FREQ_OFFSET1 = 0x73, 147 | REG_FREQ_OFFSET2 = 0x74, 148 | REG_FREQBAND = 0x75, 149 | REG_FREQCARRIER_H = 0x76, 150 | REG_FREQCARRIER_L = 0x77, 151 | 152 | REG_FREQCHANNEL = 0x79, 153 | REG_CHANNEL_STEPSIZE = 0x7A, 154 | 155 | REG_FIFO = 0x7F, 156 | }; 157 | 158 | // settings for registers REG_GPIO0_CONF, REG_GPIO1_CONF and REG_GPIO2_CONF 159 | enum GPIO { 160 | // function, exclusive 161 | GPIO_DIGITAL_INPUT = 0b00011, 162 | GPIO_ANALOG_INPUT = 0b00111, 163 | GPIO_TX_STATE_OUTPUT = 0b10010, 164 | GPIO_RX_STATE_OUTPUT = 0b10101, 165 | GPIO_ANTENNA_1_OUTPUT = 0b10111, 166 | GPIO_ANTENNA_2_OUTPUT = 0b11000, 167 | // options 168 | GPIO_INPUT_PULLUP = 0x20, 169 | GPIO_OUTPUT_DRV1 = 0x40, 170 | GPIO_OUTPUT_DRV2 = 0x80, 171 | GPIO_OUTPUT_DRV3 = 0xC0, 172 | }; 173 | 174 | // interrupt flags 175 | enum INT { 176 | // reg REG_INT_STATUS1 / REG_INT_ENABLE1 177 | INT_CRCERROR = 0x0001, 178 | INT_PKVALID = 0x0002, 179 | INT_PKSENT = 0x0004, 180 | INT_TXFFAEM = 0x0020, 181 | INT_FFERR = 0x0080, 182 | // reg REG_INT_STATUS2 / REG_INT_ENABLE2 183 | INT_POR = 0x0100, 184 | INT_CHIPRDY = 0x0200, 185 | INT_SWDET = 0x8000, 186 | INT_PREAVAL = 0x4000, 187 | }; 188 | 189 | // when intPin is given, interrupts are checked with this pin - rather than SPI polling 190 | Si4432(uint8_t csPin, uint8_t sdnPin = 0xFF, uint8_t intPin = 0xFF); 191 | 192 | // get interrupt pin, returns 0xFF if not set - may be used to attach ISR 193 | uint8_t getIntPin() const; 194 | // get interrupt flags, see enum INT, also clears pending interrupts - may be used in ISR 195 | uint16_t getIntStatus(); 196 | // enable interrupt sources, see enum INT - typically used internally 197 | void enableInt(uint16_t flags); 198 | 199 | // sets the frequency [MHz], 240..930 MHz, default 433 MHz - call before switching to tx or rx mode 200 | void setFrequency(int frequency); 201 | // sets the frequency [MHz], 240..930 MHz, default 433 MHz - call before switching to tx or rx mode 202 | void setFrequency(unsigned long frequency); 203 | // sets the frequency [MHz], 240..930 MHz, default 433 MHz - call before switching to tx or rx mode 204 | void setFrequency(double frequency); 205 | // select a 1 MHz channel rel. to the frequency, 0..255, default 0 - call before switching to tx or rx mode 206 | void setChannel(byte channel); 207 | 208 | // sets modulation type GFSK or OOK, default GFSK - call before setting baud rate 209 | void setModulationType(ModulationType modulationType); 210 | // select Manachester encoding, default disabled 211 | void setManchesterEncoding(bool enabled, bool inverted = false); 212 | // sets the bit rate, 1..256 kbps, default 100 kbps - call before switching to tx or rx mode 213 | void setBaudRate(int kbps); 214 | // sets the bit rate, 1..256 kbps, default 100 kbps - call before switching to tx or rx mode 215 | void setBaudRate(uint16_t kbps); 216 | // sets the bit rate, 0.123 .. 256 kbps, default 100 kbps - call before switching to tx or rx mode 217 | void setBaudRate(double kbps); 218 | 219 | // enables packet handling, default on - call before init/reset 220 | void setPacketHandling(bool enabled, bool lsbFirst = false); 221 | // set packet header value, default 0xDEAD - call before init/reset 222 | void setCommsSignature(uint16_t signature); 223 | 224 | // user specific boot config - call before init/reset 225 | void setConfigCallback(void (*callback)()); 226 | // blocking, also performs reset and boot to idle mode, spiClock ..10000000 227 | bool init(SPIClass* spi = &SPI, uint32_t spiClock = 8000000); 228 | 229 | // 0 (min) .. 7 (max), default 7 - call before switching to tx or rx mode 230 | void setTransmitPower(byte level, bool directTie = true); 231 | // make sendPacket block until Tx has completed, default true - call before sendPacket 232 | void setSendBlocking(bool enabled = true); 233 | // switches to Tx mode and sends the package, length 0..64, length = 0 repeats last package 234 | bool sendPacket(uint8_t length = 0, const byte* data = nullptr); 235 | // wait for Tx to complete, also returns false on transmit timeout 236 | bool waitTransmitCompleted(); 237 | 238 | // switch to Rx mode (don't block) 239 | void startListening(); 240 | // check for the packet received flags 241 | bool isPacketReceived(); 242 | // read from FIFO 243 | void getPacketReceived(uint8_t* length, byte* readData); 244 | 245 | // set operation mode, see enum OperationMode 246 | void switchMode(byte mode); 247 | // set idle operation mode to use after completing boot, TX/FIFO or RX/single, StandbyMode/SleepMode/Ready/TuneMode, default Ready - call before init/sendPacket/startListening 248 | void setIdleMode(byte mode); 249 | // freqerr (bit 3) should not be set, see datasheet 250 | byte getDeviceStatus(); 251 | 252 | void readAll(); 253 | 254 | void clearTxFIFO(); 255 | void clearRxFIFO(); 256 | void clearFIFO(); 257 | 258 | // blocking up to ~17 ms depending on mode, also performs boot 259 | void reset(bool soft = false); 260 | // blocking, also performs boot 261 | void softReset(); 262 | // blocking for ~17 ms, also performs boot 263 | void hardReset(); 264 | 265 | // non-blocking - wakeup takes ~17 ms, use isClockReady to check status 266 | void turnOn(); 267 | // non-blocking 268 | void turnOff(); 269 | // check if oscillator is in ready state 270 | bool isClockReady(); 271 | // sets SPI and pins ready and boots the radio 272 | void boot(); 273 | 274 | void ChangeRegister(Registers reg, byte value); 275 | byte ReadRegister(Registers reg); 276 | 277 | protected: 278 | void BurstWrite(Registers startReg, const byte value[], uint8_t length); 279 | void BurstRead(Registers startReg, byte value[], uint8_t length); 280 | 281 | uint8_t _csPin, _sdnPin, _intPin; 282 | 283 | float _freqCarrier; 284 | float _kbps; 285 | uint16_t _freqChannel; 286 | 287 | ModulationType _modulationType; 288 | 289 | byte _idleMode; 290 | byte _transmitPower; 291 | bool _directTie; 292 | bool _manchesterEnabled; 293 | bool _manchesterInverted; 294 | bool _packetHandlingEnabled; 295 | bool _lsbFirst; 296 | bool _sendBlocking; 297 | 298 | SPIClass* _spi; 299 | uint32_t _spiClock; 300 | 301 | uint16_t _packageSign; 302 | uint32_t _sendStart; 303 | 304 | void (*_configCallback)() = nullptr; 305 | 306 | }; 307 | 308 | #endif /* si4432_H_ */ 309 | --------------------------------------------------------------------------------