CC1101 Cap
Cap CC1101 is an expansion cap that combines a CC1101 Sub-GHz radio and an ST25R3916 NFC reader.
The MicroPython API is split by function:
CC1101Capfor Sub-GHz transmit and receive.NFCCapfor NFC card detection and read/write operations.
Support the following products:
Frequency Range
CC1101Cap supports the following measured frequency ranges:
Range |
Band |
|---|---|
|
315MHz band |
|
433MHz band |
|
868MHz band |
The RF switch is updated automatically when freq_khz changes.
UiFlow2 Example
CC1101 RF TX
Open the cc1101_cap_rf_tx_example.m5f2 project in UiFlow2.
This example sends a payload once per second at 868MHz and displays SEQ, OK and FAIL counters.
UiFlow2 Code Block:
Example output:
None
CC1101 RF RX
Open the cc1101_cap_rf_rx_example.m5f2 project in UiFlow2.
This example receives packets at 868MHz and displays payload, RSSI, LQI and CRC status.
UiFlow2 Code Block:
Example output:
None
NFC Card Detect
Open the cc1101_cap_nfc_read_id_example.m5f2 project in UiFlow2.
This example scans ISO14443A cards and displays UID, card type and user memory size.
UiFlow2 Code Block:
Example output:
None
MicroPython Example
CC1101 RF TX
This example sends a payload once per second at 868MHz and displays transmit counters.
MicroPython Code Block:
1# SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD 2# 3# SPDX-License-Identifier: MIT 4 5import os, sys, io 6import M5 7from M5 import * 8from cap import CC1101Cap 9import time 10 11 12label_title = None 13label_status = None 14label_seq = None 15label_ok = None 16label_fail = None 17label_payload = None 18cap_cc1101 = None 19 20 21seq = None 22last_tx_ms = None 23ok_count = None 24payload = None 25fail_count = None 26tx_ok = None 27 28 29def setup(): 30 global \ 31 label_title, \ 32 label_status, \ 33 label_seq, \ 34 label_ok, \ 35 label_fail, \ 36 label_payload, \ 37 cap_cc1101, \ 38 seq, \ 39 last_tx_ms, \ 40 ok_count, \ 41 payload, \ 42 fail_count, \ 43 tx_ok 44 45 M5.begin() 46 Widgets.fillScreen(0x101418) 47 label_title = Widgets.Label( 48 "CC1101 TX", 76, 3, 1.0, 0x22D3A6, 0x101418, Widgets.FONTS.Montserrat18 49 ) 50 label_status = Widgets.Label( 51 "Auto TX every 1s", 5, 28, 1.0, 0x22D3A6, 0x101418, Widgets.FONTS.Montserrat16 52 ) 53 label_seq = Widgets.Label("SEQ: 0", 5, 50, 1.0, 0xEAF2F8, 0x101418, Widgets.FONTS.Montserrat16) 54 label_ok = Widgets.Label("OK: 0", 5, 72, 1.0, 0x22D3A6, 0x101418, Widgets.FONTS.Montserrat16) 55 label_fail = Widgets.Label( 56 "FAIL: 0", 120, 72, 1.0, 0xFF5C5C, 0x101418, Widgets.FONTS.Montserrat16 57 ) 58 label_payload = Widgets.Label( 59 "TX:", 5, 100, 1.0, 0xEAF2F8, 0x101418, Widgets.FONTS.Montserrat18 60 ) 61 62 cap_cc1101 = CC1101Cap(868000, 2.4, 25.4, 58, 10, 64, 0x12, 0xAD) 63 seq = 0 64 ok_count = 0 65 fail_count = 0 66 last_tx_ms = 0 67 Speaker.begin() 68 Speaker.setVolumePercentage(0.5) 69 label_status.setText(str("Auto TX every 1s")) 70 label_seq.setText(str("SEQ: 0")) 71 label_ok.setText(str("OK: 0")) 72 label_fail.setText(str("FAIL: 0")) 73 label_payload.setText(str("TX: ")) 74 Speaker.tone(3600, 40) 75 time.sleep_ms(35) 76 Speaker.tone(4800, 45) 77 78 79def loop(): 80 global \ 81 label_title, \ 82 label_status, \ 83 label_seq, \ 84 label_ok, \ 85 label_fail, \ 86 label_payload, \ 87 cap_cc1101, \ 88 seq, \ 89 last_tx_ms, \ 90 ok_count, \ 91 payload, \ 92 fail_count, \ 93 tx_ok 94 M5.update() 95 if (time.ticks_diff((time.ticks_ms()), last_tx_ms)) >= 1000: 96 last_tx_ms = time.ticks_ms() 97 payload = str((str("CC1101, SEQ=") + str(seq))) + str(", Hello") 98 tx_ok = cap_cc1101.send(payload) 99 if tx_ok: 100 ok_count = (ok_count if isinstance(ok_count, (int, float)) else 0) + 1 101 label_status.setText(str("TX OK")) 102 Speaker.tone(3600, 25) 103 else: 104 fail_count = (fail_count if isinstance(fail_count, (int, float)) else 0) + 1 105 label_status.setText(str("TX failed")) 106 Speaker.tone(900, 100) 107 label_payload.setText(str((str("TX: ") + str(payload)))) 108 seq = (seq if isinstance(seq, (int, float)) else 0) + 1 109 label_seq.setText(str((str("SEQ: ") + str(seq)))) 110 label_ok.setText(str((str("OK: ") + str(ok_count)))) 111 label_fail.setText(str((str("FAIL: ") + str(fail_count)))) 112 113 114if __name__ == "__main__": 115 try: 116 setup() 117 while True: 118 loop() 119 except (Exception, KeyboardInterrupt) as e: 120 try: 121 from utility import print_error_msg 122 123 print_error_msg(e) 124 except ImportError: 125 print("please update to latest firmware")
Example output:
None
CC1101 RF RX
This example receives packets at 868MHz and displays payload, RSSI, LQI and CRC status.
MicroPython Code Block:
1# SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD 2# 3# SPDX-License-Identifier: MIT 4 5import os, sys, io 6import M5 7from M5 import * 8from cap import CC1101Cap 9import time 10 11 12label_title = None 13label_status = None 14label_counts = None 15label_rssi = None 16label_lqi = None 17label_payload = None 18cap_cc1101 = None 19 20 21cc1101_data = None 22rx_count = None 23crc_count = None 24last_rx_ms = None 25last_anim_ms = None 26 27 28def setup(): 29 global \ 30 label_title, \ 31 label_status, \ 32 label_counts, \ 33 label_rssi, \ 34 label_lqi, \ 35 label_payload, \ 36 cap_cc1101, \ 37 cc1101_data, \ 38 rx_count, \ 39 crc_count, \ 40 last_rx_ms, \ 41 last_anim_ms 42 43 M5.begin() 44 Widgets.fillScreen(0x101418) 45 label_title = Widgets.Label( 46 "CC1101 RX", 75, 3, 1.0, 0x22D3A6, 0x101418, Widgets.FONTS.Montserrat18 47 ) 48 label_status = Widgets.Label( 49 "Listening...", 5, 28, 1.0, 0x22D3A6, 0x101418, Widgets.FONTS.Montserrat16 50 ) 51 label_counts = Widgets.Label( 52 "RX:0 CRC:0", 5, 50, 1.0, 0xEAF2F8, 0x101418, Widgets.FONTS.Montserrat16 53 ) 54 label_rssi = Widgets.Label( 55 "RSSI: -", 5, 72, 1.0, 0xEAF2F8, 0x101418, Widgets.FONTS.Montserrat16 56 ) 57 label_lqi = Widgets.Label( 58 "LQI: -", 136, 72, 1.0, 0xEAF2F8, 0x101418, Widgets.FONTS.Montserrat16 59 ) 60 label_payload = Widgets.Label( 61 "RX:", 5, 100, 1.0, 0x22D3A6, 0x101418, Widgets.FONTS.Montserrat18 62 ) 63 64 cap_cc1101 = CC1101Cap(868000, 2.4, 25.4, 58, 10, 64, 0x12, 0xAD) 65 cap_cc1101.start_recv() 66 rx_count = 0 67 crc_count = 0 68 last_rx_ms = time.ticks_ms() 69 last_anim_ms = time.ticks_ms() 70 Speaker.begin() 71 Speaker.setVolumePercentage(0.5) 72 label_status.setText(str("Listening...")) 73 label_counts.setText(str("RX:0 CRC:0")) 74 label_rssi.setText(str("RSSI: -")) 75 label_lqi.setText(str("LQI: -")) 76 label_payload.setText(str("RX: ")) 77 Speaker.tone(3600, 40) 78 time.sleep_ms(35) 79 Speaker.tone(4800, 45) 80 81 82def loop(): 83 global \ 84 label_title, \ 85 label_status, \ 86 label_counts, \ 87 label_rssi, \ 88 label_lqi, \ 89 label_payload, \ 90 cap_cc1101, \ 91 cc1101_data, \ 92 rx_count, \ 93 crc_count, \ 94 last_rx_ms, \ 95 last_anim_ms 96 M5.update() 97 cc1101_data = cap_cc1101.recv(timeout_ms=0) 98 if cc1101_data: 99 if cc1101_data.crc_ok: 100 last_rx_ms = time.ticks_ms() 101 rx_count = (rx_count if isinstance(rx_count, (int, float)) else 0) + 1 102 label_status.setText(str("Packet received")) 103 label_counts.setText( 104 str((str((str("RX:") + str(rx_count))) + str((str(" CRC:") + str(crc_count))))) 105 ) 106 label_rssi.setText(str((str((str("RSSI: ") + str((cc1101_data.rssi)))) + str(" dBm")))) 107 label_lqi.setText(str((str("LQI: ") + str((cc1101_data.lqi))))) 108 label_payload.setText(str((str("RX:") + str(((cc1101_data.data).decode()))))) 109 Speaker.tone(5200, 35) 110 cap_cc1101.start_recv() 111 else: 112 crc_count = (crc_count if isinstance(crc_count, (int, float)) else 0) + 1 113 label_status.setText(str("CRC error")) 114 label_counts.setText( 115 str((str((str("RX:") + str(rx_count))) + str((str(" CRC:") + str(crc_count))))) 116 ) 117 Speaker.tone(900, 100) 118 cap_cc1101.start_recv() 119 else: 120 if (time.ticks_diff((time.ticks_ms()), last_anim_ms)) >= 1000: 121 last_anim_ms = time.ticks_ms() 122 label_status.setText(str("Listening...")) 123 124 125if __name__ == "__main__": 126 try: 127 setup() 128 while True: 129 loop() 130 except (Exception, KeyboardInterrupt) as e: 131 try: 132 from utility import print_error_msg 133 134 print_error_msg(e) 135 except ImportError: 136 print("please update to latest firmware")
Example output:
None
NFC Card Detect
This example scans ISO14443A cards and displays UID, card type and user memory size.
MicroPython Code Block:
1# SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD 2# 3# SPDX-License-Identifier: MIT 4 5import os, sys, io 6import M5 7from M5 import * 8from cap import NFCCap 9import time 10 11 12label_title = None 13label_status = None 14label_uid = None 15label_type = None 16label_mem = None 17cap_nfc = None 18last_scan_ms = None 19card_0 = None 20last_uid = None 21card_uid = None 22card_type = None 23card_size = None 24 25 26def setup(): 27 global \ 28 label_title, \ 29 label_status, \ 30 label_uid, \ 31 label_type, \ 32 label_mem, \ 33 cap_nfc, \ 34 last_scan_ms, \ 35 card_0, \ 36 last_uid, \ 37 card_uid, \ 38 card_type, \ 39 card_size 40 41 M5.begin() 42 Widgets.fillScreen(0x101418) 43 label_title = Widgets.Label( 44 "NFC card detect", 44, 3, 1.0, 0x22D3A6, 0x101418, Widgets.FONTS.Montserrat18 45 ) 46 label_status = Widgets.Label( 47 "Scanning...", 5, 28, 1.0, 0x22D3A6, 0x101418, Widgets.FONTS.Montserrat14 48 ) 49 label_uid = Widgets.Label("UID: -", 5, 50, 1.0, 0xEAF2F8, 0x101418, Widgets.FONTS.Montserrat14) 50 label_type = Widgets.Label( 51 "Type: -", 5, 72, 1.0, 0xEAF2F8, 0x101418, Widgets.FONTS.Montserrat14 52 ) 53 label_mem = Widgets.Label( 54 "Memory: -", 5, 94, 1.0, 0xEAF2F8, 0x101418, Widgets.FONTS.Montserrat14 55 ) 56 57 Speaker.begin() 58 Speaker.setVolumePercentage(0.6) 59 cap_nfc = NFCCap() 60 last_uid = "" 61 last_scan_ms = time.ticks_ms() 62 label_status.setText(str("Scanning...")) 63 label_uid.setText(str("UID: -")) 64 label_type.setText(str("Type: -")) 65 label_mem.setText(str("Memory: -")) 66 Speaker.tone(3600, 40) 67 time.sleep_ms(35) 68 Speaker.tone(4800, 45) 69 70 71def loop(): 72 global \ 73 label_title, \ 74 label_status, \ 75 label_uid, \ 76 label_type, \ 77 label_mem, \ 78 cap_nfc, \ 79 last_scan_ms, \ 80 card_0, \ 81 last_uid, \ 82 card_uid, \ 83 card_type, \ 84 card_size 85 M5.update() 86 if (time.ticks_diff((time.ticks_ms()), last_scan_ms)) >= 180: 87 last_scan_ms = time.ticks_ms() 88 card_0 = cap_nfc.detect() 89 if card_0: 90 card_uid = card_0.uid_str 91 if card_uid != last_uid: 92 last_uid = card_uid 93 card_type = card_0.type_name 94 card_size = card_0.user_memory 95 label_status.setText(str("Card detected")) 96 label_uid.setText(str((str("UID: ") + str(card_uid)))) 97 label_type.setText(str((str("Type: ") + str(card_type)))) 98 label_mem.setText(str((str((str("Memory: ") + str(card_size))) + str(" bytes")))) 99 Speaker.tone(4200, 45) 100 time.sleep_ms(35) 101 Speaker.tone(5600, 55) 102 cap_nfc.halt() 103 else: 104 last_uid = "" 105 label_status.setText(str("Scanning...")) 106 label_uid.setText(str("UID: -")) 107 label_type.setText(str("Type: -")) 108 label_mem.setText(str("Memory: -")) 109 110 111if __name__ == "__main__": 112 try: 113 setup() 114 while True: 115 loop() 116 except (Exception, KeyboardInterrupt) as e: 117 try: 118 from utility import print_error_msg 119 120 print_error_msg(e) 121 except ImportError: 122 print("please update to latest firmware")
Example output:
None
API
class CC1101Cap
- class cap.cc1101.CC1101Cap(freq_khz=868000, bitrate_kbps=2.4, freq_dev_khz=25.4, rx_bw_khz=58.0, output_power=10, preamble_length=16, sync_word_h=0x12, sync_word_l=0xAD)
Create a CC1101Cap object for Sub-GHz radio.
- Parameters:
freq_khz (int) – CC1101 RF frequency in kHz. Valid ranges:
315000~348000 kHz,415000~464000 kHz,830000~928000 kHz.bitrate_kbps (float) – Data rate in kbps, range from 0.6 to 6.0 kbps.
freq_dev_khz (float) – Frequency deviation in kHz, range from 1.6 to 380 kHz.
rx_bw_khz (float) – Receiver bandwidth in kHz, range from 58 to 812 kHz.
output_power (int) – Output power in dBm, range from -30 to 10 dBm.
preamble_length (int) – Preamble length in bits, options: 16, 24, 32, 48, 64, 96, 128, 192.
sync_word_h (int) – High byte of sync word (0x00 to 0xFF).
sync_word_l (int) – Low byte of sync word (0x00 to 0xFF).
UiFlow2 Code Block:

MicroPython Code Block:
from cap import CC1101Cap cap_cc1101 = CC1101Cap(868000, 2.4, 25.4, 58.0, 10, 64, 0x12, 0xAD)
- set_freq(freq_khz)
Set frequency in kHz and update the Cap RF switch automatically.
- Parameters:
freq_khz (int) – Frequency in kHz. Valid ranges:
315000~348000 kHz,415000~464000 kHz,830000~928000 kHz.
UiFlow2 Code Block:

MicroPython Code Block:
cap_cc1101.set_freq(868000)
- set_bitrate(bitrate_kbps)
Set data rate in kbps.
- Parameters:
bitrate_kbps (float) – Data rate in kbps, range from 0.6 to 6.0.
UiFlow2 Code Block:

MicroPython Code Block:
cap_cc1101.set_bitrate(2.4)
- set_freq_dev(freq_dev_khz)
Set frequency deviation in kHz.
- Parameters:
freq_dev_khz (float) – Frequency deviation in kHz, range from 1.6 to 380.
UiFlow2 Code Block:

MicroPython Code Block:
cap_cc1101.set_freq_dev(25.4)
- set_rx_bw(rx_bw_khz)
Set receiver bandwidth in kHz.
- Parameters:
rx_bw_khz (float) – Receiver bandwidth in kHz, range from 58 to 812.
UiFlow2 Code Block:

MicroPython Code Block:
cap_cc1101.set_rx_bw(58.0)
- set_output_power(output_power)
Set output power in dBm.
- Parameters:
output_power (int) – Output power in dBm, range from -30 to 10.
UiFlow2 Code Block:

MicroPython Code Block:
cap_cc1101.set_output_power(10)
- set_preamble_length(preamble_length)
Set preamble length in bits.
- Parameters:
preamble_length (int) – Preamble length in bits. Must be one of 16, 24, 32, 48, 64, 96, 128, 192.
UiFlow2 Code Block:

MicroPython Code Block:
cap_cc1101.set_preamble_length(64)
- set_sync_word(sync_word_h, sync_word_l)
Set the two-byte sync word.
- Parameters:
UiFlow2 Code Block:

MicroPython Code Block:
cap_cc1101.set_sync_word(0x12, 0xAD)
- send(packet)
Send data.
- Parameters:
packet (str | list | tuple | int | bytearray | bytes) – Data to send.
- Returns:
Trueif the packet was sent successfully, otherwiseFalse.- Return type:
UiFlow2 Code Block:

MicroPython Code Block:
cap_cc1101.send("Hello World")
- recv(timeout_ms=None)
Receive data.
- Parameters:
timeout_ms (int) – Timeout in milliseconds.
Noneuses non-blocking polling.- Returns:
Received packet instance or
None.- Return type:
CC1101Packet | None
UiFlow2 Code Block:

MicroPython Code Block:
packet = cap_cc1101.recv() if packet and packet.crc_ok: print(packet.decode(), packet.rssi, packet.lqi)
- start_recv()
Start receive mode.
UiFlow2 Code Block:

MicroPython Code Block:
cap_cc1101.start_recv()
- set_rx_irq_callback(callback)
Set the receive interrupt callback.
- Parameters:
callback – Callback invoked with a
CC1101Packetwhen a packet is received.
UiFlow2 Code Block:

MicroPython Code Block:
def on_rx(packet): print(packet.decode()) cap_cc1101.set_rx_irq_callback(on_rx)
- set_tx_irq_callback(callback)
Set the transmit callback.
- Parameters:
callback – Callback invoked after transmit. On CC1101Cap, GDO2 is used for RF switch control, so this API is kept mainly for compatibility.
UiFlow2 Code Block:

MicroPython Code Block:
def on_tx(_): print("TX done") cap_cc1101.set_tx_irq_callback(on_tx)
- standby()
Put CC1101 into standby mode.
UiFlow2 Code Block:

MicroPython Code Block:
cap_cc1101.standby()
- rx_irq_triggered()
Check whether RX IRQ has triggered.
- Returns:
Trueif RX IRQ has triggered, otherwiseFalse.- Return type:
UiFlow2 Code Block:

MicroPython Code Block:
cap_cc1101.rx_irq_triggered()
- tx_irq_triggered()
Check whether TX IRQ has triggered.
- Returns:
Trueif TX IRQ has triggered, otherwiseFalse.- Return type:
UiFlow2 Code Block:

MicroPython Code Block:
cap_cc1101.tx_irq_triggered()
class CC1101Packet
- class driver.cc1101.CC1101Packet
Packet object returned by
CC1101Cap.recv()and RX callbacks.- data
Raw packet data.
- rssi
Received signal strength in dBm.
- lqi
Link Quality Indicator.
- crc_ok
CRC result.
Truemeans the packet passed CRC check.
- decode()
Decode packet data as UTF-8 string.
- Returns:
Decoded string.
- Return type:
class NFCCap
- class cap.cc1101.NFCCap
Create an NFCCap object for the ST25R3916 NFC reader on Cap CC1101 hardware.
NFCCap uses ST25R3916 in SPI mode internally. The application does not need to pass an I2C bus or SPI object.
UiFlow2 Code Block:

MicroPython Code Block:
from cap import NFCCap nfc = NFCCap()
- detect()
Poll for an ISO14443A card.
- Returns:
A
Cardobject if a card is detected, otherwiseNone.
UiFlow2 Code Block:

MicroPython Code Block:
card = nfc.detect() if card: print(card.uid_str, card.type_name, card.user_memory)
- read(card, index, key=FACTORY_KEY)
Read one address unit from the detected card.
MIFARE Classic:
indexis the global block number and the return value is 16 bytes orNone.Type 2 / Ultralight / NTAG:
indexis the page number and the return value is 4 bytes orNone.
- Parameters:
- Returns:
bytesorNone.
UiFlow2 Code Block:

MicroPython Code Block:
data = nfc.read(card, 4)
- write(card, index, data, key=FACTORY_KEY)
Write one address unit to the detected card.
MIFARE Classic:
datamust be 16 bytes andindexis the global block number.Type 2 / Ultralight / NTAG:
datamust be 4 bytes andindexis the page number.
Use writable test cards only. Do not write UID pages, lock bytes, sector trailers, access-control blocks, payment cards, access cards, or identity cards.
- Parameters:
- Returns:
Trueon success, otherwiseFalse.- Return type:
UiFlow2 Code Block:

MicroPython Code Block:
ok = nfc.write(card, 4, bytes(16))
- halt()
Send HLTA to put the Type A card into HALT state.
UiFlow2 Code Block:

MicroPython Code Block:
nfc.halt()
- rf_off()
Turn off the NFC RF field.
UiFlow2 Code Block:

MicroPython Code Block:
nfc.rf_off()
- rf_on()
Turn on the NFC RF field.
UiFlow2 Code Block:

MicroPython Code Block:
nfc.rf_on()
Card Object
The Card object returned by NFCCap.detect() contains the detected card information.
UiFlow2 Code Block:
Attribute / Method |
Description |
|---|---|
|
Raw UID bytes. |
|
UID length. |
|
Uppercase hexadecimal UID string. |
|
ISO14443A ATQA value. |
|
ISO14443A SAK value. |
|
Card type ID. |
|
Card type name. |
|
Detected user memory size in bytes. |
|
Return |
|
Return |
UiFlow2 Code Block:





