ESPHome 2025.12 now has native support for CC1101 ![]()
My current (work in progress) configuration seems to work without any additional libraries on a esp32 and cc1101 to receive fan speed changes.
# https://esphome.io/components/cc1101/
# Configuration based on IthoCC1101::initReceiveMessage()
# See: https://github.com/letscontrolit/ESPEasy/blob/15830d38268eb28c3fe234413a81f6cf015aeda9/lib/Itho/IthoCC1101.cpp#L238
cc1101:
cs_pin: GPIO22
# Base/carrier frequency: 868.299866MHz
frequency: 868.2999MHz
# 2-FSK modulation
modulation_type: 2-FSK
# Symbol rate: MDMCFG4=0x5A, MDMCFG3=0x83 = 38.3835 kBaud
symbol_rate: 38383
# Filter bandwidth: ~203kHz (based on MDMCFG4=0x5A)
filter_bandwidth: 203kHz
# Frequency deviation: DEVIATN=0x50 = 50.78125kHz
fsk_deviation: 50kHz
# Output power
output_power: 10
# Enable packet mode
gdo0_pin: GPIO16
packet_mode: true
# Packet mode configuration
sync_mode: 16/16 # MDMCFG2=0x02 = 16-bit sync word
sync1: 0xB3 # SYNC1=179 (0xB3)
sync0: 0x2A # SYNC0=42 (0x2A)
packet_length: 63 # Fixed packet length (sync bytes removed by CC1101)
crc_enable: false # CRC disabled in ITHO
whitening: false # Data whitening disabled
on_packet:
then:
- lambda: |-
// ITHO packet decoder based on IthoCC1101::messageDecode()
// Decode Manchester-like encoding with 1-0 pattern every 8 bits
// STARTBYTE=2: Skip first 2 bytes after sync word
if (x.size() != 63) {
ESP_LOGW("itho", "Invalid packet length: %d", x.size());
return;
}
// Decoded data buffer
uint8_t decoded[32] = {0};
uint8_t decoded_len = 0;
// Decoding parameters - start from byte 2 (STARTBYTE)
const int STARTBYTE = 2;
uint8_t out_i = 0; // byte index
uint8_t out_j = 4; // bit index (start at bit 4)
uint8_t in_bitcounter = 0; // process per 10 input bits
// Decode the packet starting from STARTBYTE
for (int i = STARTBYTE; i < x.size(); i++) {
for (int j = 7; j >= 0; j--) {
// Select even bits (0, 2, 4, 6) for output
if (in_bitcounter == 0 || in_bitcounter == 2 ||
in_bitcounter == 4 || in_bitcounter == 6) {
uint8_t bit = (x[i] >> j) & 0x01;
decoded[out_i] |= (bit << out_j);
out_j++;
if (out_j > 7) { out_j = 0; }
if (out_j == 4) {
out_i++;
if (out_i >= 32) break;
}
}
in_bitcounter++;
if (in_bitcounter > 9) { in_bitcounter = 0; }
}
if (out_i >= 32) break;
}
decoded_len = out_i;
// Log decoded packet
std::string decoded_hex = format_hex_pretty(decoded, decoded_len);
ESP_LOGI("itho", "Decoded (%d bytes): %s", decoded_len, decoded_hex.c_str());
// Check for valid ITHO packet (min 12 bytes)
if (decoded_len < 12) {
ESP_LOGW("itho", "Decoded packet too short");
return;
}
// Extract command bytes - check bytes 5-10 against command patterns
// Based on IthoCC1101::checkIthoCommand() and command byte arrays
std::string command = "Unknown";
// Command bytes are at decoded[5..10]
// Low: 22 F1 03 00 02 04 (0x22, 0xF1, 0x03, 0x00, 0x02, 0x04)
// Medium: 22 F1 03 00 03 04 (0x22, 0xF1, 0x03, 0x00, 0x03, 0x04)
// High: 22 F1 03 00 04 04 (0x22, 0xF1, 0x03, 0x00, 0x04, 0x04)
// Timer: 22 F3 03 00 00 0A (0x22, 0xF3, 0x03, 0x00, 0x00, 0x0A/14/1E)
if (decoded_len >= 11) {
// Check common bytes: decoded[5]==0x22 and decoded[7]==0x03
if (decoded[5] == 0x22 && decoded[7] == 0x03) {
// Timer: byte[6]==0xF3, byte[9]==0x00, byte[10]==0x0A/0x14/0x1E
if (decoded[6] == 0xF3 && decoded[9] == 0x00) {
command = "Timer";
id(itho_command).publish_state("Timer");
id(itho_last_command).publish_state("Timer");
}
// Low/Medium/High: byte[6]==0xF1, byte[10]==0x04
else if (decoded[6] == 0xF1 && decoded[10] == 0x04) {
// Distinguish by byte[9]
if (decoded[9] == 0x02) {
command = "Low";
id(itho_command).publish_state("Low");
id(itho_last_command).publish_state("Low");
} else if (decoded[9] == 0x03) {
command = "Medium";
id(itho_command).publish_state("Medium");
id(itho_last_command).publish_state("Medium");
} else if (decoded[9] == 0x04) {
command = "High";
id(itho_command).publish_state("High");
id(itho_last_command).publish_state("High");
}
}
}
}
ESP_LOGI("itho", "Command: %s", command.c_str());
// Clear command state after 2 seconds
id(clear_command_timer).execute();
spi:
clk_pin: GPIO18 # CC1101 SCK
miso_pin: GPIO19 # CC1101 MISO/GDO1
mosi_pin: GPIO23 # CC1101 MOSI
# Text sensors for ITHO commands
text_sensor:
- platform: template
name: "ITHO Command"
id: itho_command
icon: "mdi:fan"
- platform: template
name: "ITHO Last Command"
id: itho_last_command
icon: "mdi:fan-clock"
# Script to clear command state after 2 seconds
script:
- id: clear_command_timer
mode: restart
then:
- delay: 2s
- lambda: |-
id(itho_command).publish_state("");
todo: add pairing button and buttons to set speed/timer