Si4463 Radio Library for AVR and Arduino

This is a library for use with the Si4463 radio IC from Silicon Labs, used (or will soon be) in some of my projects. The Si4463 is configurable in a ton of different ways with options for modulation, packet format, CRC algorithms and has a high output power of up to +20dBm (100mW). The Si4463 is used in a number of pre-made modules like the HopeRF RFM26W and Dorji_Com DRF4463F. The whole range of Si446x transceivers should also work with this library.

This library configures the chip to send and receive variable length packets of up to 128 bytes and uses callback functions for when events occur, such as receiving a new packet and completing a packet transmission. The callback functions run straight from the interrupt routine allowing the program to react much faster to events than if the library was polled.

Download from GitHub


Si4463 ATmega328 Arduino Uno Arduino Mega Description
VCC 3.3V 3.3V 3.3V Power (3.3V)
SDO B4 (18) 12 50 SPI MISO
SDI B3 (17) 11 51 SPI MOSI
SCLK B5 (19) 13 52 SPI SCK
NSEL B2 (16) 10 10 SPI SS
NIRQ D2 (4) 2 2 Interrupt
SDN D5 (11) 5 5 Shutdown/reset – High = on, Low = off

The Si4463 is not 5V compatible, you’ll need to do some level conversion to bring the 5V IO voltage down to 3.3V.

This library uses callbacks which are ran when events occur. Since the callbacks run from the interrupt routine be sure that any global variables you use in them are declared ‘volatile’, just as you would when dealing with normal ISRs. These events will wake the microcontroller if it is sleeping. Some callbacks can be turned on and off using Si446x_setupCallback() and Si446x_setupWUT().

Event Callback Default setting
New packet incoming SI446X_CB_RXBEGIN Off
Valid packet received SI446X_CB_RXCOMPLETE Always enabled
Invalid packet received SI446X_CB_RXINVALID Always enabled
Transmission of packet complete SI446X_CB_SENT Off
Wakeup timer expired SI446X_CB_WUT Off
Supply voltage below threshold SI446X_CB_LOWBATT Off

Wakeup timer (WUT)
The Si4463 can be configured to periodically wake up using the wake up timer (WUT). When the wake up timer is active and the radio is put into sleep mode it consumes a tiny 900nA. When the WUT expires the SI446X_CB_WUT callback is ran. The low battery detector uses the wake up timer to initiate a supply voltage measurement. If the supply voltage is below the threshold set by Si446x_setLowBatt() then the SI446X_CB_LOWBATT callback will be ran.
Calculating the correct mantissa (M) and exponent (R) values for the WUT can be a little difficult so here’s a calculator:

WUT Calculator
Interval (seconds)
Actual interval

Compatible chips/modules

Device Tested
Si4460 Not tested
Si4461 Not tested
Si4463 Works
Si4467 Not tested
Si4468 Not tested
HopeRF RFM26W Works
Dorji_Com DRF4463F Works

Radio configuration
The radio configuration is quite complex. Wireless Development Suite (WDS) can be used to create different configuration header files:

  1. Open WDS (can sometimes take a while to load, also try clicking on the center logo)
  2. Choose 'Simulate radio'
  3. Scroll down and select 'Si4463'
  4. Make sure the Revision is set to 'C2'
  5. Click 'Select Radio' button
  6. Select 'Radio Configuration Application'
  7. Click 'Select Application' button
  8. Click Open (top right of the window) and choose config_normal.xml from the GitHub repo

Areas to avoid changing

  • Packet tab
    • Don't change anything in the 'Packet config' and 'Variable length config' tabs
  • Interrupt tab
    • Don't change any of these settings
  • GPIO and FRR tab
    • Only change GPIO 2 and 3 if your module uses different pins for the RF TX/RX switch
    • Don't change NIRQ and SDO
    • Don't change Fast Response Register A and B

To generate the header file containing the new config

  1. Click 'Generate source' (bottom right)
  2. Choose 'Save custom radio configuration header file'
  3. Save the file somewhere
  4. The generated config file can't be used just yet, WDS has a few options missing. A Perl script (and a Windows .exe version of it) is provided in the GitHub repo - perl radio_config_Si4463.h radio_config.h will read radio_config_Si4463.h and output to radio_config.h. With the Windows .exe the input header file can be dragged on top of the exe and it will output to radio_config.h. Or to do this manually follow the fix up instructions below, though the manual way won't remove unnecessary settings.

Manual config header fix up

  1. Remove any '#include' lines (usually just 1 around line 41)
  2. In 'RF_POWER_UP' define change the second value from 0x81 to 0x01 (normal power up without any patch stuff)
  3. In 'RF_GLOBAL_CONFIG_1' define change the 5th value (GLOBAL_CONFIG) from 0x20 to 0x30 (puts the FIFO into single 129 byte mode)
  4. In 'RF_PKT_RX_THRESHOLD_12' define change the 6th from last value (PKT_FIELD_2_LENGTH_7_0) from 0x3F to 0x80 (sets max packet length to 128)
  5. Remove the 'SI446X_PATCH_CMDS' and 'RF_GLOBAL_CONFIG_1_1' lines from the 'RADIO_CONFIGURATION_DATA_ARRAY' define

Sometimes the defines may not be called RF_GLOBAL_CONFIG_1 and RF_PKT_RX_THRESHOLD_12, in which case search for the field name instead (GLOBAL_CONFIG or PKT_FIELD_2_LENGTH_7_0) and change the corresponding value.

The Si4463 has 4 GPIO pins (numbered 0 to 3) which can be configured in a number of ways, however 2 of the GPIOs are used to control an RF TX/RX switch on most pre-made modules. By default the library configures GPIO2=HIGH when receiving and GPIO3=HIGH when transmitting, this works for the RFM26W and DRF4463F. Most other modules use this same configuration, but check anyways.

Library coding tips

  • When the radio is put to sleep with Si446x_sleep() it will automatically wake up when its SPI select line is asserted. Once SPI communication has finished the Si446x_sleep() function must be called again to put it back to sleep.
  • Si446x_setupWUT() will take 300us to complete if the WUT was previously disabled.

Range Testing
Using the config_longrange_500 config in the GitHub repo and driving around a fairly flat village/country area I managed to receive a valid packet at 2.2 km (1.37 miles) and 3.79 km (2.36 miles) for an invalid packet. This was done using a 3dBi antenna shown below instead of the sort that are normally supplied with radio modules.

Transmitter setup


Skip to comment form

    • Hendrik on December 1, 2017 at 6:53 am
    • Reply

    Hi Zak,

    I just want to find out if your si4463 Library for Arduino can be used to receive in RAW/Direct mode?


    1. Hey Hendrik, no the library only supports packet mode. Direct mode requires code to read and write to a GPIO pin on the module. Though, you might be able use this library to configure the module and then use VirtualWire ( to send and receive the data.

    • Tri Dao on January 12, 2018 at 7:54 am
    • Reply

    WOA…this is all things I need, I’m Viet Nam with limitted sources about this chip, and I’m finding docs for my RF projects (I have some modules Si4464 and they’re OK)
    I’m gona working hard with your project and it will take me a long time because I only have a little bit experiments about Si446x and coding with them ! 😀
    Anyways…Thanks you so much, Zak!

    • Puls Flag on January 29, 2018 at 4:35 pm
    • Reply

    Hi Zak,
    this seems to be a great work!
    I’ll try to get it work with the Si4460….

    Can the NIRQ-Pin be used with PCINT1_vect = Vectror Number 5 (ATmega 328) ?

    Many Thanks!

    1. Hey Puls,
      The interrupt define value is the INTx number, not the raw vector number, so 5 won’t work, only 0 and 1 for the mega328. You’ll need to modify the library a little to use PCINTx.
      Modify Si446x_irq_off() and Si446x_irq_on() to use the correct registers and bits for PCINT1, and change the ISR vector name near the bottom of the library code to use PCINT1_vect. I think that’s it.

    • Puls Flag on February 2, 2018 at 5:00 pm
    • Reply

    Hey Zak,
    great, works fine!
    And all my SPI stuff, like display, also works fine. That’s really great!

    But I got an issue using the UART library of Peter Fleury ( ).
    Waiting for RF data using while(pingInfo.ready == PACKET_NONE); the UART doesn’t seem to react to any external input correctly anymore. The UART0_RECEIVE_INTERRUPT Peter is using, seems to work on incomming data, but reading the RX receive buffer, it always shows only a size of 2 counts, although it should be 9 (in my case).
    Commenting out the line with waiting for RF data, all UART stuff works fine again…
    Do you have any idea what to do, as UART-communication would be really useful?

    Many thanks and once again:
    You did an awesome job!!

    1. while(pingInfo.ready == PACKET_NONE) will block the program until a packet is received, which also includes reading from the UART library buffer (though it should be able to buffer up to 32 characters by default, not sure why yours is only going to 2). It can be made non-blocking pretty easily by placing all the Si446x example code in the main while(1) loop into in if(pingInfo.ready != PACKET_NONE){ (code here) } and then have your stuff after the if().

    • Sachin on March 23, 2018 at 5:26 am
    • Reply

    Hi Zac,

    The Windows .exe file to convert the WDS generated file no longer works. I am using WDS version Any idea why? Please let me know,

    Kind Regards,

    1. Hey Sachin, in what why does the .exe not work, what does it say? WDS is the version I’ve been using too.

    • Saurav Paudel on May 14, 2018 at 1:58 pm
    • Reply

    Can we set frequency of transmission by calling any function directly?

    1. Hey Saurav, no not in this library. Changing the frequency isn’t as simple as setting a single register like on some RF chips. The Si4463 needs to have a bunch of PLL synthesizer fractional and divider values and probably more things calculated. So instead the library just uses channels, which by default has a base frequency of 433MHz and each channel increments by 250KHz.

        • Saurav Paudel on May 14, 2018 at 3:54 pm
        • Reply

        Ahh got it, Thank You.

    • Puls Flag on May 14, 2018 at 8:54 pm
    • Reply

    Hi Zak,

    last time I build up a transmission with 2 HOPE RFM24W modules at 868MHz and transferring data with your library worked quite fine.
    Now I build my own module with an Si4460 rev. b1 (which is used in RFM24W module), but no data transmission is possible with the RFM24W module.
    The RFM24W module uses a 30MhZ external crystal and my setup uses a 26MHz crystal, as suggested by silabs.
    I modified the WDS file for my setup and used a simple RF-explorer to see what happens, while being in fast TX-mode.
    Both modules (RFM24W and my setup) show the same transmission frequency at 868,204 MHz with nearly the same signal strength.
    Unfortunately I don’t have the equipment to view the RF-data.

    Do you have any idea where I go wrong?

    Many thanks for your help!

    1. Hah, it’s kinda difficult to say whats wrong. Are you able to test a 30MHz crystal on your own module? Try putting your module in receive mode and monitor the RSSI signal level when the RFM24W is transmitting, does your module sense the transmission?

    • Alexander on July 3, 2018 at 9:27 pm
    • Reply

    Hello, Zak! You tested this module: E10-433MS1W?
    Based on si4463, spi.
    What is “channels”? You have frequency table for this?

    1. Hey Alexandar, I’ve not tried that module, I’ll buy some and check them out though. Looks like they’ve got a nice 1W transmit power! The default base frequency for this library is 433MHz with a channel seperation of 250KHz, so channel 0 is 433.00 MHz, channel 10 is 435.50 MHz, upto channel 255 at 496.75 MHz.

    • Lo on July 4, 2018 at 9:55 pm
    • Reply

    i just see your last reply : the default config for this library is 433M so please ignore the last message regarding carrier wave frequency. I however have another interrogation regarding config file, you mention GPIO2 and GPIO3 as drive lines for the RF switch, the module i am using doesnt have an RF switch, is there anything i can do to disable the switch control without making a new config.h file with the windows only tool?

    1. Hey Lo, the Si446x_writeGPIO() function can be used to change GPIO settings, you can see it being used in the WUT_ADC_GPIO example.

    • saikiran on July 24, 2018 at 6:38 am
    • Reply

    Im getting compilation error when i am going to use this library with one example code which you provided in library file.
    that it is showing as..

    C:\Users\lenovo\Documents\Arduino\hardware\Arduino_STM32-master\STM32F1\libraries\Si446x-master\src\Si446x.cpp: In function ‘uint8_t Si446x_irq_off()’:

    C:\Users\lenovo\Documents\Arduino\hardware\Arduino_STM32-master\STM32F1\libraries\Si446x-master\src\Si446x.cpp:182:50: error: ‘digitalPinToInterrupt’ was not declared in this scope



    C:\Users\lenovo\Documents\Arduino\hardware\Arduino_STM32-master\STM32F1\libraries\Si446x-master\src\Si446x.cpp: In function ‘void Si446x_irq_on(uint8_t)’:

    C:\Users\lenovo\Documents\Arduino\hardware\Arduino_STM32-master\STM32F1\libraries\Si446x-master\src\Si446x.cpp:207:51: error: ‘digitalPinToInterrupt’ was not declared in this scope

    attachInterrupt(digitalPinToInterrupt(SI446X_IRQ), Si446x_SERVICE, FALLING);


    1. Hi saikiran, STM32duino doesn’t have support for the digitalPinToInterrupt() function. A work around I found is to put this line at the top of Si446x.cpp: #define digitalPinToInterrupt(pin) (pin)

    • Max on January 7, 2019 at 6:36 pm
    • Reply

    Hi Zak,
    I was trying to use your library; I struggled a little bit but in the end I’ve been able to see some results!

    What I am using is a Si4463 @868.3 MHz and I am trying to sniff the packets the thermostat sends to the boiler. Using the scanned, I am able to get high values of RSSI when I change the temperature, but when I try to read the content of the package, it looks like nothing is received.

    Big changes in RSSI (from -115 to -72/-70) every time I change the temperature make me believe the board works and it is correctly connected to Arduino Mega.

    Could you help me to understand what I am doing wrong? I created a new radio_config using WDS (actually I have 5, i.e. ook, 2/4fsk, 2/4gfsk), crystal @30MHz…but nothing! Any suggestion?


    1. Hey Max, you need to know the exact packet structure and other things, like bitrate, encoding (manchester, data whitening), preamble, sync word, variable/fixed packet length, CRC and more. Try opening up the thermostat transmitter or receiver and looking to see what chip it uses and researching it will probably help narrow things down.

    • Coby Levy on January 14, 2019 at 5:14 pm
    • Reply

    hi guys,
    first of all thank you for this project, its very helpful.

    I have managed to make it work on E10 433MD module.
    the E10 is based on si4463 B1 (other then c1), and with a 26MHZ xtal.

    I attached a radio_config file for you, just rename it to radio_config.h and place it in the correct folder.


    1. Hi Coby, actually I’ve only used the B1 revision of the chip. I just found that choosing C1 in WDS seemed to work better and had more options than choosing B1, hah. Could you also share the .xml from WDS that was used to generate the .h? It’s easier to see what’s changed that way.

      • Damien on August 10, 2019 at 10:57 pm
      • Reply

      Hi Coby Levy, thank you for sharing your config file, that’s works fine for me. Could you provide the .xml from WDS please ? Thank you.

        • Damien on August 13, 2019 at 10:34 pm
        • Reply

        Hi Zak, seriously thank you very much for your work!!

        have you an idea why the file of Coby Levy work find??

        1. Hey Damien, thanks 🙂 Coby hasn’t sent any links to the .xml file. Best thing to do would be to install WDS from SiLabs and open one of the provided .xml from the GitHub repo then change the crystal frequency to 26MHz and follow the instructions in the blog post about how to make a usable .h file.

    • Dan on March 4, 2019 at 12:29 am
    • Reply

    Hi Zak,

    Thanks for all your great work on the Si446x library. I’m attempting to solve the same problem as Max. I’m using a Si4463 868Mhz module (YJ-14005 revision B1, 30Mhz oscillator) wired to an Arduino Nano and I’ve copied the RF and packet settings from a set-up I’ve been using successfully with a Ti CC1101 connected to the Nano. The messages are 868.3Mhz, 38.383kbps, 2GFSK, 50kHz deviation, no CRC or whitening. I’m trying a really simple packet structure with just a preamable and one 8-byte fixed length field (no syncword) to attempt to capture part of a more complex packet structure (it actually has a variable length structure with a 5-byte syncword). I’ve tried both revision C2 and B1 in WDS and made the edits you’ve described, but I just can’t seem to get any notification of successful RX using your callbacks_irq example. I may not be setting the channel/frequency information correctly as I only seem to be able to detect a signal with weird combinations e.g. base frequency 868Mhz + channels 200-210??

    Any ideas where I may be going wrong? Thanks, Dan.

    1. Hey Dan, without a syncword the radio will have a hard time figuring out where the preamble ends and the data starts. It might be better to have a 1 byte sync and try to guess the correct value (maybe 0xAA or 0x55 so it matches the preamble, then you’ll get preample in your data but at least you might see the real sync word in there somewhere), or have the raw data stream outputted to one of the GPIO pins and try to figure things out from that.
      Also keep the WDS revision as C2, even if you have a B1 chip. I forget why, but I think WDS outputs a more complete set of parameters with C2. I’ve also only tested the library with B1 chips.

    • Dan on March 4, 2019 at 4:02 pm
    • Reply

    Hi Zak,
    I really appreciate you taking the time to respond to my message. I think I’ve finally identified the source of my problem which was a really dumb mistake by me! My radio configs were being overwritten when compiling as I had kept the original radio_config_Si4463.h in the same directory as well as my new config file and left both #includes in the code. This meant that each tweak I was making to the packet structure etc was being ignored. It all seems to working OK now and I can make progress on optimising the packet settings.
    Thanks again. Dan

    • Alex on August 9, 2019 at 1:49 pm
    • Reply

    thank you for the library.

    Can you tell me how I change the transmit and receive frequencies?


    1. Hey, no problem 🙂 Calculating the correct register values for a frequency is complex with the Si4463, instead the library uses channels when calling Si44x_RX() and Si446x_TX(). With the default configuration, channel 0 is 433.0MHz and each channel is separated by 0.25MHz, up to channel 255 which is 496.75MHz.

        • Alex on August 9, 2019 at 8:27 pm
        • Reply

        Hi Zak,
        I’m using the 868MHz versions of the module, would greatly appreciate if you could advise me how to setup the module to use the channels on this frequency.

        thanks again,

        1. Check out the “Radio configuration” section of the blog post, you’ll need to use the WDS program from SiLabs to generate a config.

    • Puls Flag on August 15, 2019 at 2:22 pm
    • Reply

    Hi Zak,
    your library is still working great!
    Now I’m playing around optimizing the antenna.
    Would it be possible to turn ON and OFF only the rf-carrier without sending any data?
    It also would be nice for checking the TX power.

    Many thanks!

    1. Yea, modulation type can be set to continuous-wave and modulation source to pseudo-random. I think sticking this at the end of the Si446x_init() function in the library might to the trick – setProperty((SI446X_PROP_GROUP_MODEM<<8) | 0x00, 0x10);.

    • Damien on December 4, 2019 at 3:26 pm
    • Reply

    Hi Zak, after some test the lib work fine with a radio config normal but I’m obligated to add these lines to the server so that work fine with a radio config longrange

    Si446x_TX((uint8_t *)data, strlen(data), CHANNEL, SI446X_STATE_RX);
    Si446x_TX((uint8_t *)data, strlen(data), CHANNEL, SI446X_STATE_RX);

    I use a E10 433MD module

    1. Hey Damien! You must wait for the current transmission to complete before sending more data, the long range config uses a lower datarate so transmissions take longer! Use the SI446X_CB_SENT() callback to determine when a transmission finishes.

    • juan luis on February 22, 2020 at 7:18 pm
    • Reply

    Hi Zak, I am using the SI4463 and I make all it sais here but the library is not working , it tells me that arduino.h in the si446x doesnt work and make several omisions in the code. To see and modify the librery i used visual studio code to see the library and make the changes , can I be in contact with you via whatsapp 4922003778 or email?

    1. Hey Jaun, I’d email you but I think hotmail blocks emails from my server for some reason. It sounds like you’re using the Arduino version of the library in a non-Arduino environment. Use the non-Arduino library instead, which is in the ./Si446x folder, not in ./arduino/Si446x.

    • max on September 25, 2020 at 10:13 am
    • Reply

    it seems a good work and I’m trying to use it with a wemos 32 (esp32). I just started to find the correct correspondance between the pins and i’m stuck to choose the correct pin for the interrupt in esp32. do you have suggestion?
    many thanks

    1. Hey Max, it looks like any pin on the ESP32 can be used as an interrupt pin.

    • Will on May 26, 2021 at 5:01 pm
    • Reply

    Hi Zak,

    Thank for your great work!

    For this library, could we upgrade to transmit packet over 128 bytes length?


    1. Hey Will, the Si4463 supports having data sent to the radio module while it is transmitting to create a continous stream of data. So yes, it is possible to modify to library for packets larger than 128 bytes.

        • Will on June 9, 2021 at 4:46 pm
        • Reply

        Hi Zack,

        After reference your source, I could transmit/receive data around 200bytes.
        Thanks for your source sharing!

        In the SI4463, there is the word “listen before talk functionality”, but I cannot find any SiLab’s documents related to this topic. Do you have experience about this?


        1. I think the RSSI jump detector needs to be configured to get collision avoidance working.

    • Puls on October 5, 2021 at 2:17 pm
    • Reply

    Hi Zak,
    good you are still there! 🙂

    You set the Maximum packet length to 128 (SI446X_MAX_PACKET_LEN) and looking at the WDS config file, in the Packet Config Tab, the ‘Packet TX/RX Threshold is set to 48 Bytes.
    I’m just wondering if this is correct, as I’m not really understanding the threshold feature.
    What would be the difference if a packet length is below or above the threshold?

    I’m actually working with packet length of 20 bytes, which works very reliable.
    But I’d need go up to 50 bytes packet length. Would be there any different behaviour or reliability?

    Many thanks!

    1. The threshold thing is for receiving packets larger than the FIFO size of 64 bytes (or 128 in single FIFO mode), but the library doesn’t support this so the setting has no effect. The larger the packet the greater the chance of data corruption and losing the entire packet, but otherwise there’s no difference in behaviour.

Leave a Reply

Your email address will not be published.

Are you human? *