-
Notifications
You must be signed in to change notification settings - Fork 106
Sub GHz
Introduction:
- Introduction to Sub-GHz radio & YouTube link.
- Key concepts & terms, like frequency, modulation, deviation, etc.
Flipper Zero Applications:
- "Sub-GHz" application for the Flipper Zero.
- COMING SOON: CLI application for Flipper Zero (Phone/PC uses Flipper Zero).
Basic topics:
- Distances for send/receive on the Flipper Zero.
- Modulation learn the differences between "AM650" and "FM238".
- Static and Dynamic codes. "Rolling code"
Advanced topics:
- Create custom settings to tune your Flipper Zero Sub-GHz radio!
- File formats to understand how to read the .SUB files supported by the Flipper Zero.
My YouTube playlist on SubGHz has a variety of videos about Sub-GHz radio.
Flipper Zero can receive and transmit radio frequencies in the range of 300-348, 387-464, 779-928 MHz with its built-in CC1101 module. The frequencies you are allowed to transmit on varies by region. Most firmware allow connecting an external CC1101 module, which can end up adding extended range (but typically cannot handle higher data rates). Sub-GHz feature can read, save, and emulate remote controls that operate in the 300-928MHz range (below 1000MHz or 1GHz). These controls are used for interaction with gates, barriers, radio locks, remote control switches, wireless doorbells, smart lights, and more. This feature will not work for remote controls that use Infrared or for remote controls that use Bluetooth, BLE, 2.4GHz, etc. Flipper Zero can help you to learn if your security is compromised and susceptible to replay attacks.
Flipper Zero official firmware will not Save/Replay a rolling code. It does support adding a remote which you may be able to pair to your existing system. When you do this, the SUB file will be updated each time you send a signal using the Flipper Zero. If you edit the SUB file to match another remote you own, you risk desyncing your remote.
Most devices are registered with the FCC, so it may be helpful to go to fccid.io and search for the device FCC id to learn additional information about the device you are trying to test. Often times this will list the frequency, modulation, internal pictures, etc. of the remote or receiver that you are testing.
Different devices broadcast their signal on different frequencies. Similar to how you may listen to 101.5MHz FM on the radio for one station and 102.3MHz FM for a different station. The frequency that you set in the Flipper Zero is the carrier frequency. The lowest supported frequency is 300MHz and the highest is 928MHz. The Sub-GHz application uses the CC1101 radio module where it is tuned to a specific frequency and modulation. This is different than an SDR (software defined radio) where you are able to pick up a range of frequency spectrum at the same time (the Flipper can quickly hop between a small set of frequencies or range of spectrum, but it's typically milliseconds per frequency before the change). Many devices in the US operate at a frequency of 433.92 MHz.
Most devices you find in the United States will be FCC certified and will display the FCC identifier on the device. You can go to fccid.io to lookup the device and typically will find the frequency information. The Flipper Zero also has a Frequency Analyzer that can help identify the frequency.
TX stands for transmitting a signal. The Flipper Zero needs to be updated with latest database so that it knows what region you are in and then it will enable transmit for frequencies that are valid in your region.
RX stands for receiving a signal. The Flipper Zero is able to receive signals on a variety of frequencies. There are some default frequencies which it can receive on, but you can modify your configuration to enable more frequencies if needed.
Hopping is the act of bouncing between a small set of frequencies looking for a signal instead of just listening to one frequency. The larger the list, the more chance there is that you miss part of a signal while listening on a different frequency.
The CC1101 supports OOK, ASK, 2FSK, GFSK, 4FSK & MSK. The Flipper Zero Sub-GHz application uses the CC1101 in async mode which only OOK, ASK, 2FSK and GFSK. OOK and ASK are called "AM" where the amplitude of the carrier frequency is altered. OOK (On-Off-Keying) the signal is either present or not. ASK (Amplitude Shift Keying) the signal is either low amplitude or large amplitude. 2FSK and GFSK are called "FM" where the signal is either lower than the carrier frequency or higher than the carrier frequency. In GFSK, instead of the signal instantly jumping from one frequency to another, it quickly slides over to the other frequency.
The video Jeeves teaches RF modulation explains all of the modulations in detail.
NOTE: In the Flipper Zero the "Modulation" setting is actually picking a CC1101 preset configuration, which includes attributes like modulation, deviation, bandwidth, data rate, ASK decision boundary, AGC settings, etc.
For FM signals, like 2FSK, the signal is not sent on the carrier frequency, but instead is some distance above/below that frequency. For 2FSK, this distance is the deviation. Having the proper deviation is almost as important as getting the correct frequency for an FM device, especially when trying to retransmit the signal (as the receiver may be finely tuned to the two frequencies it expects the transmitter to use).
NOTE: For other modulations used by the CC1101, like 4FSK, the signal may be +-1/3 of the deviation as well.
Bandwidth concept in Sub-GHz refers to receiving (RX) a signal. This applies a filter on the signal that the CC1101 can receive. For example, a 650KHz bandwidth will have a range of 650KHz (650KHz/2 = 325KHz), where it will only pick up signals +- 325KHz of the carrier frequency. For AM, all signals within the bandwidth will be considered as part of the OOK/ASK -- a wider range can help if you are unsure of the frequency (but it also means other devices have a better chance of causing interference). For FM you need to make sure the bandwidth is large enough so that the frequencies on the deviation are in range.
The Flipper Zero Sub-GHz communicates with the CC1101 using asynchronous serial mode. In this mode, a GPIO pin is used to represent the current state of the received signal. This GPIO pin is updated at 8x the configured data rate. For example, if your data rate is 4000 then the data will be updated 32,000 times a second or every 31.25 microseconds. The higher the data rate, the more accurate the Flipper Zero will time the length of the pulses. Internal CC1101 seems to be find running at a data rate of 115K (which is within 2 microsecond accuracy) however external CC1101 have been reported to only support rates around 15-20K. When trying to clone a remote with short pulses, you may need to use higher data rates, so your pulses are properly timed.
The CC1101 supports 4 dB, 8 dB, 12 dB and 16 dB decision boundary. This boundary is the strength of the signal relative to the off signal, for the signal to be considered the on signal. A bigger dB number indicates that the signal needs to be larger to be considered part of the on signal, this allow for more noise in the signal but also requires you be closer to the transmitter. Typically, 8 dB is a good compromise, which is what "AM650" on the Flipper Zero uses.
The RSSI value is an estimate of the signal power level. The rate at which the RSSI value is updated is based on the bandwidth and the filter length. You can use RSSI to help the Flipper Zero only start recording data when the signal is at least a certain average strength. This can be helpful if there are other devices far away broadcasting on the same frequency range as the device you are trying to record.
The CC1101 supports many settings for automatic gain control (AGC). The primary registers used are AGCCTRL2, AGCCTRL1 and AGCCTRL0. These values impact RSSI, Carrier Sense and other aspects.
Official firmware has a "Region Information" menu option. This will display your region (like "US") and the transmit frequency bands that are supported. See the list of frequencies based on your region. Your flipper can still do receive in frequencies outside of this list, for example, in the US one of the receive frequencies is 868.35MHz (even though you can't transmit).
If you try to transmit on an unsupported frequency, you will get the "Transmission is blocked" error message.
The Frequency Analyzer is helpful to determine what frequency a device is broadcasting on. If you press the right button on the Flipper Zero you can then use the OK button to toggle how to sort the information. When looking at this extended view, there is a box showing the max RSSI for the value (if it exceeds a certain limit) each pixel represents 3dB (in groups of 4 pixels or 12dB groups).
| Label | Description |
|---|---|
| Seq D | Order in which frequency was first received (newest to oldest) |
| Seq A | Order in which frequency was first received (oldest to newest) |
| Count D | Ordered by occurrences of the frequency (most to least) |
| Count A | Ordered by occurrences of the frequency (least to most) |
| RSSI D | Ordered by RSSI [signal strength] of strongest signal for frequency (strong to weak) |
| RSSI A | Ordered by RSSI [signal strength] of strongest signal for frequency (weak to strong) |
| Freq D | Ordered by frequency (high to low) |
| Freq A | Ordered by frequency (low to high) |
Read RAW is used to read a signal without interpreting it, just store the RAW data (duration in microseconds of HIGH/LOW signal from CC1101 chip). You can press the left button to go into a Config menu. You can set the Frequency in MHz. You can set the Modulation (which is really the preset information). You can set Sound ON/OFF - which will click the speaker each time the data switches from HIGH to LOW. You can set the RSSI Threshold, which is the minimum signal strength needed for capturing data.
| Modulation | Description |
|---|---|
| AM270 | AM (OOK/ASK). 270 kHz bandwidth. 4 dB decision boundary. 3794 data rate. |
| AM650 | AM (OOK/ASK). 650 kHz bandwidth. 8 dB decision boundary. 3794 data rate. |
| FM238 | FM (2FSK). 2.380 kHz deviation. 270 kHz bandwidth. 4798 data rate. 16 chan filter w/carrier-sense |
| FM476 | FM (2FSK). 47.607 kHz deviation. 270 kHz bandwidth. 4798 data rate. 16 chan filter w/carrier-sense |
| FM95 * | POCSAG - FM (2FSK). 9.521 kHz deviation. 270 kHz bandwidth. 4798 data rate. 16 chan filter w/carrier-sense |
| FM15k * | FM (2FSK). 15.869 kHz deviation. 135 kHz bandwidth. 3794 data rate. 32 chan filter w/carrier-sense |
| Pagers * | FM (2FSK). 5.157 kHz deviation. 270 kHz bandwidth. 625 data rate. 16 chan filter w/carrier-sense |
| HND_1 * | Honda_1 - FM (2FSK). 15.869 kHz deviation. 270 kHz bandwidth. 15373 data rate. 16 chan filter w/carrier-sense |
| HND_2 * | Honda_2 - FM (2FSK). 15.869 kHz deviation. 67 kHz bandwidth. 15373 data rate. 32 chan filter w/carrier-sense |
NOTE: The items above marked with * are not part of the official firmware settings.
Press OK button to start recording the data, then press it again to stop recording. Once you have a recorded signal, you can:
- press Left button to erase your recording & arm for recording.
- press OK button to send the signal (if the frequency is allowed in your region)
- press Right button to save the file, for analysis or playback later. One you save it, the right button will allow you to rename or delete the file.
The saved data is a text file that contains the frequency, the modulation and mostly the raw data. The positive numbers are the duration in microseconds where there was tone, and the negative numbers are the microseconds where there was silence.
Read is used to read a signal and try to match it to an existing known protocol. You can press the left button to go into a Config menu. You can set the Frequency in MHz. You can enable Hopping to hop between a small set of frequencies to be monitored (instead of choosing the frequency). You can set the Modulation (which is really the preset information). You can set Sound ON/OFF - which will click the speaker each time the data switches from HIGH to LOW. You can enable "Bin_RAW" (I still need to learn more about this feature). You can choose to lock the keyboard, which will require you to press Back button three times to reenable the keyboard. Some firmware has options to Ignore certain protocols (for example to ignore a signal from car alarms).
If a signal matches a protocol, the protocol and id will be displayed. You can use the Up and Down buttons on the Flipper Zero to choose a signal (the oldest signal is at the top of the list). Pressing the Ok button will display details about the signal. You can then use the Ok button again to send the signal (if the frequency is allowed in your region) or you can press the Right button to save the signal.
When you send the signal, the protocol code will use the data to create a transmit timing. The encoder is interpreting the data from the decoded signal, so it may be slightly different than the received signal. For example, the encoder could choose to transmit 300 microseconds of tone and 200 microseconds of silence, even though the received signal was 270 microseconds of tone and 230 microseconds of silence. Typically, the encoder will play a more "ideal to the spec" signal vs. what is captured during a RAW capture.
The saved data is a text file, that contains the details the protocol code needs to be able to recreate the signal.
How does it bucketize? Does anything above some RSSI match?
Add Manually feature will create a random saved signal, without needing to receive the signal. This uses subghz_scene_set_type which will generate a random number and use that for the key and then feed that to the protocol for saving the signal to the Flipper Zero. Choose the protocol you would like to create and then enter a name for the saved file.
The saved data is a text file, that contains the details the protocol code needs to be able to recreate the signal (exactly the same as a file that was saved using the Read feature).
Saved feature will allow you to load a saved file. (These are just text files, so you can edit these files to represent different data than what was originally saved.)
- If the signal is a RAW file, you will go to the RAW menu as if you had just saved a Read RAW signal.
- If the signal is a known protocol, you will go to a menu with Emulate, Rename, Delete options. Choosing Emulate will display the technical details of the signal and the Ok button will send the signal (similar UI to clicking on a Read signal, but without the Save option). If the code is a Dynamic code, the SUB file will be updated once the signal is sent.
Test feature has three options - Carrier, Packet, Static.
In Carrier mode, the application displays the RSSI. Use the Left and Right buttons to change the frequency the Flipper Zero is listening on. Up and Down buttons change the path (isolate, 315 MHz, 433Mhz, 868 Mhz). The Flipper has second stage components which are picked.
In Packet mode, the application displays the RSSI and number of packets detected. Use the Left and Right buttons to change the frequency the Flipper Zero is listening on. Up and Down buttons change the path (isolate, 315 MHz, 433Mhz, 868 Mhz). The Flipper has second stage components which are picked. Press the Ok button to switch between receiving Priceton packets and transmitting them.
In Static model, the application will broadcast 1 of 4 static Princeton codes. Use the Left and Right buttons to change the frequency the Flipper Zero will transmit on. Up and Down buttons change the static key used. Ok button transmits the code.
These settings are not enabled in official firmware, however some firmware expose additional options for SubGHz.
You can choose between Internal and External radio. You must connect a CC1101 to the SPI pins to enable the External Radio, otherwise you will get an error.
You can enable the +5 volt GPIO pin. Typically +5 volts is only supplied when the Flipper is connected to USB-C cable, but this option will provide 5 volts without needing the cable.
Enable this setting will make your save files have a timestamp by default.
Choose how file names are incremented.
Enable a pin to mirror the CC1101 raw data. Note: This pin will have the signal data even when the signal has not met the RSSI threshold value.
Coming soon - I will be adding information about the Flipper Zero's CLI (command line interface). This allows devices such as a phone or computer to communicate with the Flipper Zero using the serial port.
A lot of factors can impact the distance the Flipper Zero and send and receive signals. I did some testing using two Flipper Zeros with AM650 modulation sending a DoorHan signal at different frequencies. I used an external CC1101 w/433 antenna and 915MHz antenna and 315MHz antenna. I used the Sub-GHz Read function on official firmware. For sending I modified the Saved .SUB files to contain the frequencies I wanted to test and updated official firmware to resend a signal every 10 seconds after the first signal was sent. I was in an open parking lot for testing and made sure I could receive a repeat signal within a 30-second window from the same location. Your distance may vary if you use AM270 (should be slightly farther) or FM.
| Freq | TX | RX | Approx distance (feet) |
|---|---|---|---|
| 315MHz | int | int | 140 ft |
| 315MHz | int | ext | 225 ft |
| 315MHz | ext | int | 90 ft |
| 315MHz | ext | ext | 160 ft |
| 433MHz | int | int | 50 ft |
| 433MHz | int | ext | 120 ft |
| 433MHz | ext | int | 110 ft |
| 433MHz | ext | ext | Max (290 ft+) |
| 915MHz | int | int | 115 ft |
| 915MHz | int | ext | 10 ft |
| 915MHz | ext | int | 26 ft |
| 915MHz | ext | ext | 4 ft |
There are a variety of modulations, depending on the firmware you have.
| Modulation | Description |
|---|---|
| AM270 | AM (OOK/ASK). 270 kHz bandwidth. 4 dB decision boundary. 3794 data rate. |
| AM650 | AM (OOK/ASK). 650 kHz bandwidth. 8 dB decision boundary. 3794 data rate. |
| FM238 | FM (2FSK). 2.380 kHz deviation. 270 kHz bandwidth. 4798 data rate. 16 chan filter w/carrier-sense |
| FM476 | FM (2FSK). 47.607 kHz deviation. 270 kHz bandwidth. 4798 data rate. 16 chan filter w/carrier-sense |
| FM95 * | POCSAG - FM (2FSK). 9.521 kHz deviation. 270 kHz bandwidth. 4798 data rate. 16 chan filter w/carrier-sense |
| FM15k * | FM (2FSK). 15.869 kHz deviation. 135 kHz bandwidth. 3794 data rate. 32 chan filter w/carrier-sense |
| Pagers * | FM (2FSK). 5.157 kHz deviation. 270 kHz bandwidth. 625 data rate. 16 chan filter w/carrier-sense |
| HND_1 * | Honda_1 - FM (2FSK). 15.869 kHz deviation. 270 kHz bandwidth. 15373 data rate. 16 chan filter w/carrier-sense |
| HND_2 * | Honda_2 - FM (2FSK). 15.869 kHz deviation. 67 kHz bandwidth. 15373 data rate. 32 chan filter w/carrier-sense |
NOTE: The items above marked with * are not part of the official firmware settings.
Static codes are codes that are broadcast the same time each time the remote is pressed. The Flipper Zero official firmware supports saving codes that are registered as Static.
Dynamic codes are codes that change based on some algorithm each time the remote is pressed. An internal counter is updated and the SUB (SubGHz file) is updated with the new information. Replaying a dynamic (also known as rolling code) has a high probability of getting out of sync with the remote. If you replay the old signal, the receiving device may detect this condition. If you play a newer signal, then the original remote will have an old count (and so it will be sending old signals that will not work). It is difficult to keep multiple remotes in sync. The Flipper Zero official firmware does not support saving dynamic codes.
The Flipper Zero official firmware does allow you to use the Add Manually to create a new SUB file, which you can then associate with the receiver. This is a better solution, since you do not risk getting the original remote out of sync and having to resync the device. Every time you send a code, the SUB file is updated with new information, so the next code will be later in the sequence.
As mentioned above, it is better to Add Manually and just create a new device that you pair with. If you look at the SUB file and the detailed data presented by the Flipper, and then press Ok to generate a new code, looking at the data & look at the SUB file... eventually you will realize how to modify the SUB file on a second Flipper to match a transmission by your first Flipper. The data has to be in the SUB file, otherwise the Flipper couldn't keep sending an incrementing code. You will likely be able to edit a SUB file originally created with Add Manually using a text editor. If you figure all of the bytes of data you receive, when you emulate your hand-edited SUB file, it should be able to match the signal that your other Flipper will send in the future. Depending on your firmware, the counters might not increment at the same rate, so it is still possible to get out of sync with the other device (like having to press the button twice as often on one Flipper with one firmware vs another) so it is highly recommended to use Add Manually a new code and register that with the receiver.
In the firmware, the applications\main\subghz\helpers\subghz_txrx.c file loads a "subghz/assets/setting_user" file to further configure the SubGHz application. In some firmware, this file may be renamed, like "setting_user.txt". Some firmware use this file for their own configuration, while other firmware set their own configuration in code. An example of the "setting_user" file may be found in "setting_user.example", where you can rename the file to "setting_user" (or "setting_user.txt") and copy it to your SD CARD/subghz/assets folder to make it apply. If you have debug logging enabled (Settings, System, LogLevel Debug) then messages should be logged if the file fails to load.
The "setting_user" file is a text file. If the line starts with a "#" character, the line will be treated as a comment and the Flipper Zero will ignore the contents of that line. Remove the "#" to uncomment the line.
If you uncomment the line and set the value to "Add_standard_frequencies: true" then the Flipper will populate the list of standard frequencies for Read and Read RAW. It will also populate the list of hopper frequencies.
If you uncomment the line and set the value to "Add_standard_frequencies: false" then the Flipper will not populate the frequencies or hopper frequencies. You must provide at least one uncommented "Frequency" and one uncommented "Hopper_frequency" for your settings file to be valid.
If you uncomment the line "Default_frequency: 433920000" then that value will be the default frequency to use when Read or Read RAW is selected.
If you uncomment the line "Frequency: 300000000" then that frequency will be used for Read, Read RAW and Frequency Analyzer. You can change the frequency to any valid frequency that the Flipper Zero supports. The list of frequencies are not in order (so your frequency may be at the beginning or end of the configuration selection when doing Read or Read RAW). The Flipper Zero does not need to be able to transmit on the frequency for you to add it to this list. If you set "Add_standard_frequencies: false" then you must provide at least one of these entries.
If you uncomment the line "Hopper_frequency: 300000000" then that frequency will be added to the list of hopper frequencies to use when frequency hopping is enabled. If you set "Add_standard_frequencies: false" then you must provide at least one of these entries.
Set the "Custom_preset_name" to a name you would like to show up in Modulation. Set "Custom_preset_module: CC1101" to specify the module. Set "Custom_preset_data" to the data for your preset.
AM650 is the following preset data: "02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00"
The data is a hexadecimal register number of the CC1101 followed by the hexadecimal value to set for the register. For example, in AM650 register 0x02 is set to a value of 0x0D and then register 0x03 is set to a value of 0x07. The pattern continues until "00 00" which signals the end of the register list. The 8 bytes following that are the PATable, which is the signal strength for transmitting. In the AM650 example, the PATable is "00 C0 00 00 00 00 00 00" (the first byte means the OFF signal will be "00" strength, and the second byte means the ON signal will be "C0" strength, or about 10 dBm).
02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00
Modulation: "12 30" 6:4 MOD_FORMAT[2:0] => OOK
Bandwidth: "10 17" 7:6 CHANBW_E[1:0] & 5:4 CHANBW_M[1:0] => 650kHz
Data rate: "10 17" 3:0 DRATE_E[3:0] & "11 32" 7:0 DRATE_M[7:0] => 3,794
Decision boundary: "1D 91" 1:0 FILTER_LENGTH[1:0] => 8 dB
Sync: "12 30" 2:0 SYNC_MODE[2:0] => no preamble/sync
PATABLE: 00 C0 00 00 00 00 00 00 => OOK, max power [no ramp]
02 0D 03 47 08 32 0B 06 14 00 13 00 12 30 11 32 10 67 18 18 19 18 1D 40 1C 00 1B 03 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00
Modulation: "12 30" 6:4 MOD_FORMAT[2:0] => OOK
Bandwidth: "10 67" 7:6 CHANBW_E[1:0] & 5:4 CHANBW_M[1:0] => 270.833kHz
Data rate: "10 67" 3:0 DRATE_E[3:0] & "11 32" 7:0 DRATE_M[7:0] => 3,794
Decision boundary: "1D 40" 1:0 FILTER_LENGTH[1:0] => 4 dB
Sync: "12 30" 2:0 SYNC_MODE[2:0] => no preamble/sync
PATABLE: 00 C0 00 00 00 00 00 00 => OOK, max power [no ramp]
02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 83 10 67 15 04 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00
Modulation: "12 04" 6:4 MOD_FORMAT[2:0] => 2FSK
Deviation: "15 04" 6:4 DEVIATION_E[2:0] & 2:0 DEVIATION_M[2:0] => 2.380kHz
Bandwidth: "10 67" 7:6 CHANBW_E[1:0] & 5:4 CHANBW_M[1:0] => 270.833kHz
Data rate: "10 67" 3:0 DRATE_E[3:0] & "11 83" 7:0 DRATE_M[7:0] => 4,798
Channel filter samples: "1D 91" 1:0 FILTER_LENGTH[1:0] => 16
Sync: "12 04" 2:0 SYNC_MODE[2:0] => no preamble/sync, carrier-sense above threshold
PATABLE: C0 00 00 00 00 00 00 00 => 2FSK, max power [no ramp]
02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 83 10 67 15 47 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00
Modulation: "12 04" 6:4 MOD_FORMAT[2:0] => 2FSK
Deviation: "15 47" 6:4 DEVIATION_E[2:0] & 2:0 DEVIATION_M[2:0] => 47.607kHz
Bandwidth: "10 67" 7:6 CHANBW_E[1:0] & 5:4 CHANBW_M[1:0] => 270.833kHz
Data rate: "10 67" 3:0 DRATE_E[3:0] & "11 83" 7:0 DRATE_M[7:0] => 4,798
Channel filter samples: "1D 91" 1:0 FILTER_LENGTH[1:0] => 16
Sync: "12 04" 2:0 SYNC_MODE[2:0] => no preamble/sync, carrier-sense above threshold
PATABLE: C0 00 00 00 00 00 00 00 => 2FSK, max power [no ramp]
02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 83 10 67 15 24 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00
Modulation: "12 04" 6:4 MOD_FORMAT[2:0] => 2FSK
Deviation: "15 24" 6:4 DEVIATION_E[2:0] & 2:0 DEVIATION_M[2:0] => 9.521kHz
Bandwidth: "10 67" 7:6 CHANBW_E[1:0] & 5:4 CHANBW_M[1:0] => 270.833kHz
Data rate: "10 67" 3:0 DRATE_E[3:0] & "11 83" 7:0 DRATE_M[7:0] => 4,798
Channel filter samples: "1D 91" 1:0 FILTER_LENGTH[1:0] => 16
Sync: "12 04" 2:0 SYNC_MODE[2:0] => no preamble/sync, carrier-sense above threshold
PATABLE: C0 00 00 00 00 00 00 00 => 2FSK, max power [no ramp]
02 0D 03 47 08 32 0B 06 15 32 14 00 13 00 12 00 11 32 10 A7 18 18 19 1D 1D 92 1C 00 1B 04 20 FB 22 17 21 B6 00 00 00 12 0E 34 60 C5 C1 C0
Modulation: "12 00" 6:4 MOD_FORMAT[2:0] => 2FSK
Deviation: "15 32" 6:4 DEVIATION_E[2:0] & 2:0 DEVIATION_M[2:0] => 15.869kHz
Bandwidth: "10 A7" 7:6 CHANBW_E[1:0] & 5:4 CHANBW_M[1:0] => 135.417kHz
Data rate: "10 A7" 3:0 DRATE_E[3:0] & "11 32" 7:0 DRATE_M[7:0] => 3,794
Channel filter samples: "1D 92" 1:0 FILTER_LENGTH[1:0] => 32
Sync: "12 00" 2:0 SYNC_MODE[2:0] => no preamble/sync
PATABLE: 00 12 0E 34 60 C5 C1 C0 + "22 17" 2:0 PA_POWER[2:0] => Ramp to full power
02 0D 07 04 08 32 0B 06 10 64 11 93 12 0C 13 02 14 00 15 15 18 18 19 16 1B 07 1C 00 1D 91 20 FB 21 56 22 10 00 00 C0 00 00 00 00 00 00 00
Modulation: "12 0C" 6:4 MOD_FORMAT[2:0] => 2FSK
Deviation: "15 15" 6:4 DEVIATION_E[2:0] & 2:0 DEVIATION_M[2:0] => 5.157kHz
Bandwidth: "10 64" 7:6 CHANBW_E[1:0] & 5:4 CHANBW_M[1:0] => 270.833kHz
Data rate: "10 64" 3:0 DRATE_E[3:0] & "11 93" 7:0 DRATE_M[7:0] => 625
Channel filter samples: "1D 91" 1:0 FILTER_LENGTH[1:0] => 16
Sync: "12 0C" 2:0 SYNC_MODE[2:0] => no preamble/sync, carrier-sense above threshold
Manchester_en: "12 0C" 3 MANCHESTER_EN => Enabled (but "async" disabled per 27.1)
Maybe treated as "12 04"?
PATABLE: C0 00 00 00 00 00 00 00 => 2FSK, max power [no ramp]
02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 36 10 69 15 32 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00
Modulation: "12 04" 6:4 MOD_FORMAT[2:0] => 2FSK
Deviation: "15 32" 6:4 DEVIATION_E[2:0] & 2:0 DEVIATION_M[2:0] => 15.869kHz
Bandwidth: "10 69" 7:6 CHANBW_E[1:0] & 5:4 CHANBW_M[1:0] => 270.833kHz
Data rate: "10 69" 3:0 DRATE_E[3:0] & "11 36" 7:0 DRATE_M[7:0] => 15,373
Channel filter samples: "1D 91" 1:0 FILTER_LENGTH[1:0] => 16
Sync: "12 04" 2:0 SYNC_MODE[2:0] => no preamble/sync, carrier-sense above threshold
PATABLE: C0 00 00 00 00 00 00 00 => 2FSK, max power [no ramp]
02 0D 0B 06 08 32 07 04 14 00 13 02 12 07 11 36 10 E9 15 32 18 18 19 16 1D 92 1C 40 1B 03 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00
Modulation: "12 07" 6:4 MOD_FORMAT[2:0] => 2FSK
Deviation: "15 32" 6:4 DEVIATION_E[2:0] & 2:0 DEVIATION_M[2:0] => 15.869kHz
Bandwidth: "10 E9" 7:6 CHANBW_E[1:0] & 5:4 CHANBW_M[1:0] => 67.708kHz
Data rate: "10 E9" 3:0 DRATE_E[3:0] & "11 36" 7:0 DRATE_M[7:0] => 15,373
Channel filter samples: "1D 92" 1:0 FILTER_LENGTH[1:0] => 32
Sync: "12 07" 2:0 SYNC_MODE[2:0] => 30/32 + carrier-sense (but "30/32" disabled per 27.1)
Maybe treated as "12 04"?
PATABLE: C0 00 00 00 00 00 00 00 => 2FSK, max power [no ramp]
The CC1101 datasheet contains detailed information about the various registers.
| Register (in hex) | Description |
|---|---|
| 12 | Modulation: 2FSK or ASK/OOK |
| 10-11 | Bandwidth & Data rate |
| 15 | Deviation (for 2FSK) |
| 22 + PATABLE (last 8 bytes) | broadcast power & ramp |
| 1B-1D | RX: AGC, ASK decision boundaries |
| 21 | Front-end RX config. 0xB6 is Flipper recommendation, 0x56 is CC1101 default. |
| 02 + 08 | "02 0D 08 32" - GDO0 output pin is async data w/infinite packet length. |
| 07 | "07 04" - CRC_AutoFlush (used in most 2FSK profiles, but not relevant for async?) |
| 0B | "0B 06" = 152,343Hz Intermediate frequency. Perhaps the CC1101 in the Flipper is tuned to work best at this frequency? The default is 381kHz. |
| 18 | "18 18" = Calibrate 150us, idle to Rx/TX. |
| 19* | "19 18" = OOK must have lowest two bits off. |
| 19* | "19 16" = 3K, K/2, Bwchan/4 -- Freq offset (Rx vs Tx diff). |
| 19* | "19 1D" = 4K, K/2, BWchan/8 -- Freq offset (Rx vs Tx diff). |
| 20 | "20 FB" = Wake on Radio (WOR @ hours) |
| 03 | "03 47" is same as "03 07". Same as defaults. Sets the unused FIFO buffers & can be ignored for async? |
| 14 + 13 | "14 00 13 02" = 101,562Hz - Channel Spacing (2FSK) |
| 14 + 13 | "14 00 13 00" = 25,390Hz - Channel Spacing (OOK) |
| 0A | "0A 00" = (default value) Channel number |
| Register (in hex) | Details |
|---|---|
| 12 | "12 #x" - The first digit (Most Significant) is: 0 = 2-FSK. 3 = ASK/OOK. 1 = GFSK. 7 = MSK (>26kBaud) |
| 12 | "12 x#" - The second digit (Least Significant) is: 0 = No preamble/sync. 4 = No preamble/sync, carrier-sense above threshold. C = Manchester, 15/16, carrier-sense above threshold (not valid for async). 7 = 30/32, carrier-sense above threshold (not valid for async). |
| 22* | "22 11" = PA1 [OOK]. |
| 22* | "22 10" = PA0 [2FSK, NO RAMP]. |
| 22* | "22 17" = PA0 [2FSK, WITH RAMP (7 values)]. |
Register 0x10 and 0x11 set the bandwidth and data rate.
Bandwidth is the set using the highest 4-bits of register 0x10. For a bandwidth of 650KHz set register 0x10 to 10-1F. For a bandwidth of 270KHz set register 0x10 to 60-6F. The max bandwidth is 812KHz (set register 0x10 to 00-0F). The min bandwidth is 58KHz (set register 0x10 to F0-FF).
Higher data rates will make the RAW value timings more accurate, as shown in this YouTube video. External CC1101 seems unstable at rates above the 15K-20K range using some firmware.
Data rate is set using the lowest 4-bits of register 0x10 for the Exponent (scale) and all 8-bits of register 0x11 for the Mantissa.
For a data rate of 4798 baud, set register 0x10 to [0-F]7 and Register 0x11 to 0x83. For a data rate of 3794 baud, set register 0x10 to [0-F]7 and Register 0x11 to 0x32. For a data rate of 15373, set register 0x10 to [0-F]9 and Register 0x11 to 0x36. For a data rate of 19985 baud, set register 0x10 to [0-F]9 and Register 0x11 to 0x93. For a data rate of 115051 baud, set register 0x10 to [0-F]C and Register 0x11 to 0x22.
For FSK (FM modulated) signals, deviation is the frequency away from the carrier frequency. Register 0x15 stores the deviation; with the exponent in the high bits and the mantissa in the low bits. For a deviation of 2380Hz set Register 0x15 to 0x04. For a deviation of 47607Hz set Register 0x15 to 0x47. For a deviation of 15869Hz set Register 0x15 to 0x32.
| Hz | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | E (high nibble) |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 1,587 | 3,174 | 6,348 | 12,695 | 25,391 | 50,781 | 101,563 | 203,125 | |
| 1 | 1,785 | 3,571 | 7,141 | 14,282 | 28,564 | 57,129 | 114,258 | 228,516 | |
| 2 | 1,984 | 3,967 | 7,935 | 15,869 | 31,738 | 63,477 | 126,953 | 253,906 | |
| 3 | 2,182 | 4,364 | 8,728 | 17,456 | 34,912 | 69,824 | 139,648 | 279,297 | |
| 4 | 2,380 | 4,761 | 9,521 | 19,043 | 38,086 | 76,172 | 152,344 | 304,688 | |
| 5 | 2,579 | 5,157 | 10,315 | 20,630 | 41,260 | 82,520 | 165,039 | 330,078 | |
| 6 | 2,777 | 5,554 | 11,108 | 22,217 | 44,434 | 88,867 | 177,734 | 355,469 | |
| 7 | 2,975 | 5,951 | 11,902 | 23,804 | 47,607 | 95,215 | 190,430 | 380,859 | |
| M (low nibble) |
The lowest 2-bits of register 0x1D set the decision boundary. For ASK (AM modulation) this is the difference between an ON/OFF signal in dB. For FSK (FM modulation) this is the channel filter samples.
| Lowest two bits of Register 0x1D | Description (FSK) | Description (ASK) |
|---|---|---|
| 00 | 8 samples | 4dB |
| 01 | 16 samples | 8dB |
| 10 | 32 samples | 12dB |
| 11 | 64 samples | 16dB |
The value to use in PATable is somewhat complex. This PATable document explains the values. You should avoid using 0x61 to 0x6F for the CC1101, even though the values are listed in the table. The two most common values use are "00" and "C0". For most frequencies, a value of "C0" represents about 10 dBm (or 10 mW) of power. A value of "00" represents little-to-no power.
For calculating a ramp of power, it is typically recommended to use SmartRF Studio 7 from Texas Instruments, with the CC1101 chip configured for your frequency, modulation, bandwidth, etc. rather than try to manually create a ramp.
The table in the document uses dBm, which is a log scale. To convert to mW the formula is 10^(dBm/10). Here is a table of common dBm values...
| dBm | mW |
|---|---|
| 10 dBm | 10 mW |
| 7 dBm | 5.0mW |
| 5 dBm | 3.2 mW |
| 0 dBm | 1.0 mW |
| -5 dBm | 0.316 mW |
| -10 dBm | 0.100 mW (100 μW) |
| -15 dBm | 0.032 mW (32 μW) |
| -20 dBm | 0.01 mW (10 μW) |
| -30 dBm | 0.001 mW (1 μW) |
The Flipper Zero Documentation does a good job describing the file formats in detail.
One of my subscribers wrote a script to convert a SUB file into a CSV file.
- SUB to CSV script
Here is a Princeton encoded file:
Filetype: Flipper SubGhz Key File
Version: 1
Frequency: 433920000
Preset: FuriHalSubGhzPresetOok650Async
Protocol: Princeton
Bit: 24
Key: 00 00 00 00 00 52 81 1C
TE: 154
The Key 52811C can be written out as binary 010100101000000100011100. This is a demodulated signal.
Here is the same remote, but using Read RAW:
Filetype: Flipper SubGhz RAW File
Version: 1
Frequency: 433920000
Preset: FuriHalSubGhzPresetOok650Async
Protocol: RAW
RAW_Data: 133 -4806 163 -468 453 -166 123 -500 437 -200 145 -470 129 -472 439 -198 145 -474 441 -176 139 -456 173 -468 133 -490 131 -466 139 -500 129 -472 475 -166 145 -474 127 -472 165 -450 469 -168 459 -174 427 -168 177 -436 161 -472 131 -4816 131 -486 471 -168 145 -476 417 -210 137 -450 173 -466 439 -178 139 -458 473 -168 139 -490 129 -476 141 -476 133 -490 129 -478 133 -494 455 -176 143 -490 135 -452 173 -446 445 -210 429 -168 453 -162 153 -470 131 -482 155 -4802
The positive numbers are tones, and the negative numbers are silence. Starting with the data after the big silence (-4806)...
| Tone | Silence | Value (0=mostly silence, 1=mostly tone) | Signal |
|---|---|---|---|
| 163 | -468 | 0 | 0 |
| 453 | -166 | 1 | 01 |
| 123 | -500 | 0 | 010 |
| 437 | -200 | 1 | 0101 |
| 145 | -470 | 0 | 01010 |
| 129 | -472 | 0 | 010100 |
| 439 | -198 | 1 | 0101001 |
| 145 | -474 | 0 | 01010010 |
| 441 | -176 | 1 | 010100101 |
| 139 | -456 | 0 | 0101001010 |
| 173 | -468 | 0 | 01010010100 |
| 133 | -490 | 0 | 010100101000 |
| 131 | -466 | 0 | 0101001010000 |
| 139 | -500 | 0 | 01010010100000 |
| 129 | -472 | 0 | 010100101000000 |
| 475 | -166 | 1 | 0101001010000001 |
| 145 | -474 | 0 | 01010010100000010 |
| 127 | -472 | 0 | 010100101000000100 |
| 165 | -450 | 0 | 0101001010000001000 |
| 469 | -168 | 1 | 01010010100000010001 |
| 459 | -174 | 1 | 010100101000000100011 |
| 427 | -168 | 1 | 0101001010000001000111 |
| 177 | -436 | 0 | 01010010100000010001110 |
| 161 | -472 | 0 | 010100101000000100011100 |
| 131 | -4816 | x |
010100101000000100011100 matches the Key 52811C from the other SUB file.
In Read there is an option for "Bin_RAW". Here is what the SUB file would look like (assuming Princeton wasn't registered in protocol_items.c):
Filetype: Flipper SubGhz Key File
Version: 1
Frequency: 433920000
Preset: FuriHalSubGhzPresetOok650Async
Protocol: BinRAW
Bit: 130
TE: 145
Bit_RAW: 130
Data_RAW: 00 00 00 00 01 1D 1D 11 D1 D1 11 11 1D 11 1D DD 11
If you write DATA_RAW out in binary, we get:
0000 0001 0001 1101 0001 1101
0001 0001 1101 0001 1101 0001 0001 0001 0001 0001
0001 1101 0001 0001 0001 1101 1101 1101 0001 0001
If we ignore the last bit and then regroup we get:
1000 1110 1000 1110 1000 1000 1110 1000
1110 1000 1000 1000 1000 1000 1000 1110
1000 1000 1000 1110 1110 1110 1000 1000 1
The 1 represents a tone and the 0 represents silence. So short tone followed by long silence is 1000 (which we can encode as a 0) and long tone followed by short silence is 1110 (which we can encode as a 1). That gives us 010100101000000100011100, which matches the previous files.
Princeton uses "Short tone+Long silence" for 0 and "Long tone+Short silence for 1". Other protocols may use a fixed length tone or a fixed length silence, or even something more complex. Hopefully this helps you understand the relationship between ReadRAW (tone/silence timings), ReadBin (tone/silence represented by series of 1 or 0, with TE being the microsecond duration), and Princeton (or another encoding) where the bits are combined to represent data.
The Flipper Zero has an internal CC110 sub-ghz radio. The firmware now also supports attaching an external CC1101 radio to the SPI pins. NOTE: Many of the external radios are tunes to a specific band, such as 433MHz. They will likely perform better than the Flipper Zero at that band, but worse than the Flipper Zero at the other bands.
Include these header files:
#include <lib/subghz/devices/devices.h>
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>To select the internal radio device, use the following:
// Populate the CC101 device list.
subghz_devices_init();
// Get the internal radio device.
const SubGhzDevice* device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);TODO: I will add information about the external radio later. But the basic steps are: To select the external radio device, you would use SUBGHZ_DEVICE_CC1101_EXT_NAME instead. You would then want to enable +5 pin (if the external device is using a voltage regulator to convert 5-volts to 3.3-volts). You would also want to add detection to ensure the device is connected. Any when your application exits, it should disable the 5-volt pin.
The CC1101 supports asynchronous transfer mode, which uses a GPIO pin for transmitting data asynchronously. The Sub-GHz tool uses the method to transfer .SUB files.
Include these header files:
#include <lib/subghz/transmitter.h>
#include <lib/subghz/protocols/protocol_items.h>Determine the name of the protocol you want to use for transmitting. You can find it by opening ./lib/subghz/protocols/protocol_items.c in your firmware. Go to the definition of the entry you want, for example subghz_protocol_princeton. There you will see a .name field getting set, for example .name = SUBGHZ_PROTOCOL_PRINCETON_NAME,. Go to the definition of the name, for example #define SUBGHZ_PROTOCOL_PRINCETON_NAME "Princeton".
Get the SubGhzTransmitter for the protocol we want to transmit:
// TODO: I think we could use subghz_protocol_princeton.name instead of "Priceton"?
char* protocol = "Princeton";
SubGhzEnvironment* environment = subghz_environment_alloc();
subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
SubGhzTransmitter* transmitter = subghz_transmitter_alloc_init(environment, protocol);Next, you need to determine all of the properties that the deserialize will access. It is often helpful to look at a .SUB file to understand the fields to set. You can also look at the protocols deserialize method, which may contain additional fields typically not present in the .SUB file; such as Repeat. For example, subghz_protocol_encoder_princeton_deserialize
Load the data into the FlipperFormat:
FlipperFormat* flipper_format = flipper_format_string_alloc();
uint32_t bits = 24;
uint32_t te = 390;
uint32_t repeat = 5;
uint32_t key = 0x967AB4;
uint8_t data[8] = {0};
data[5] = (uint8_t)((key >> 16) & 0xFFU);
data[6] = (uint8_t)((key >> 8) & 0xFFU);
data[7] = (uint8_t)(key & 0xFFU);
flipper_format_insert_or_update_string_cstr(flipper_format, "Protocol", "Princeton");
flipper_format_insert_or_update_uint32(flipper_format, "Bit", &bits, 1);
flipper_format_insert_or_update_hex(flipper_format, "Key", data, COUNT_OF(data));
flipper_format_insert_or_update_uint32(flipper_format, "TE", &te, 1);
flipper_format_insert_or_update_uint32(flipper_format, "Repeat", &repeat, 1);
flipper_format_rewind(flipper_format);Deserialize the FlipperFormat into your SubGhzTransmitter. For example, for Princeton this will fill out the SubGHzTransmitter's SubGhzProtocolDecoderPrinceton (which includes SubGhzBlockGeneric data) based on parsing the FlipperFormat. It will also populate initance->encoder.upload[] with duration and level information (You can think of this as the RAW data timings).
SubGhzProtocolStatus status = subghz_transmitter_deserialize(transmitter, flipper_format);
furi_assert(status == SubGhzProtocolStatusOk);Next do some initialization:
subghz_devices_begin(device);
subghz_devices_reset(device);Then load the CC1101 preset that you wish to use. Use one of the presets in ./lib/subghz/devices/preset.h. If the first argument is FuriHalSubGhzPresetCustom, then the second argument is a custom register table (Reg, value, Reg, value, ...,0, 0, PATable [0..7] entries).
subghz_devices_load_preset(device, FuriHalSubGhzPresetOok650Async, NULL);Set the frequency you want to transmit on:
uint32_t frequency = 433920000;
// Set the frequency, RF switch path (band), calibrates the oscillator on the CC1101.
frequency = subghz_devices_set_frequency(device, frequency);Stop charging the battery while transmitting:
furi_hal_power_suppress_charge_enter();Transmit the data:
// Start transmitting (keeps the DMA buffer filled with the encoder.upload[] data)
if(subghz_devices_start_async_tx(device, subghz_transmitter_yield, transmitter)) {
// Wait for the transmission to complete.
while(!(subghz_devices_is_async_complete_tx(device))) {
furi_delay_ms(100);
}
// Stop transmitting, debug log (tag="FuriHalSubGhz") the duty cycle information.
subghz_devices_stop_async_tx(device);
}Shutdown the device:
subghz_devices_sleep(device);
subghz_devices_end(device);Remove the devices from the registry:
subghz_devices_deinit();Allow the battery to charge again:
furi_hal_power_suppress_charge_exit();Free resources we allocated:
flipper_format_free(flipper_format);
subghz_transmitter_free(transmitter);
subghz_environment_free(environment);The CC1101 supports synchronous transfer mode, where the data is sent to the CC1101 and the protocol is processed on the chip. The Sub-GHz CLI chat application uses the method to transfer text messages to nearby Flipper Zeros.
Include this header file:
#include <lib/subghz/subghz_tx_rx_worker.h>Allocate the worker:
subghz_txrx = subghz_tx_rx_worker_alloc();Start the worker thread:
uint32_t frequency = 433920000;
if(subghz_tx_rx_worker_start(subghz_txrx, device, frequency)) {
subghz_tx_rx_worker_set_callback_have_read(
demo_context->subghz_txrx, subghz_demo_worker_update_rx_event_callback, demo_context);
}Stop charging the battery while transmitting:
furi_hal_power_suppress_charge_enter();Convert message to byte stream:
FuriString* furi_message = furi_string_alloc();
furi_string_printf(furi_message, "Hello Sub-GHz CLI Chat!");
uint8_t* message = (uint8_t*)furi_string_get_cstr(furi_message);
size_t length = strlen((char*)message);
furi_assert(length < 60);Send the message:
while(!subghz_tx_rx_worker_write(subghz_txrx, message, length)) {
// Wait a few milliseconds on failure before trying to send again.
furi_delay_ms(20);
}Stop and free the worker thread:
if(subghz_tx_rx_worker_is_running(demo_context->subghz_txrx)) {
subghz_tx_rx_worker_stop(demo_context->subghz_txrx);
}
subghz_tx_rx_worker_free(demo_context->subghz_txrx);Remove the devices from the registry:
subghz_devices_deinit();Allow the battery to charge again:
furi_hal_power_suppress_charge_exit();Free resources we allocated:
furi_string_free(furi_message);