diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt
index 851e021db20f3163521f1f974063f1db91d3c61a..143f326621c2aa53823c63e3cd52326b7a188636 100644
--- a/firmware/application/CMakeLists.txt
+++ b/firmware/application/CMakeLists.txt
@@ -217,6 +217,7 @@ set(CPPSRC
 	apps/ui_adsb_rx.cpp
 	apps/ui_adsb_tx.cpp
 	apps/ui_afsk_rx.cpp
+	apps/ui_btle_rx.cpp
 	apps/ui_nrf_rx.cpp
 	apps/ui_aprs_tx.cpp
 	apps/ui_bht_tx.cpp
diff --git a/firmware/application/apps/ui_btle_rx.cpp b/firmware/application/apps/ui_btle_rx.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d4b9afeed50235fa393b75b190ce658024c74ffe
--- /dev/null
+++ b/firmware/application/apps/ui_btle_rx.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
+ * Copyright (C) 2017 Furrtek
+ * Copyright (C) 2020 Shao
+ *
+ * This file is part of PortaPack.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "ui_btle_rx.hpp"
+#include "ui_modemsetup.hpp"
+
+#include "modems.hpp"
+#include "audio.hpp"
+#include "rtc_time.hpp"
+#include "baseband_api.hpp"
+#include "string_format.hpp"
+#include "portapack_persistent_memory.hpp"
+
+using namespace portapack;
+using namespace modems;
+
+namespace ui {
+
+void BTLERxView::focus() {
+	field_frequency.focus();
+}
+
+void BTLERxView::update_freq(rf::Frequency f) {
+	receiver_model.set_tuning_frequency(f);
+}
+
+BTLERxView::BTLERxView(NavigationView& nav) {
+	baseband::run_image(portapack::spi_flash::image_tag_btle_rx);
+	
+	add_children({
+		&rssi,
+		&channel,
+		&field_rf_amp,
+		&field_lna,
+		&field_vga,
+		&field_frequency,
+		&text_debug,
+		&button_modem_setup,
+		&record_view,
+		&console
+	});
+	
+	// DEBUG
+	record_view.on_error = [&nav](std::string message) {
+		nav.display_modal("Error", message);
+	};
+	record_view.set_sampling_rate(24000);
+	
+	// Auto-configure modem for LCR RX (will be removed later)
+	update_freq(2426000000);
+	auto def_bell202 = &modem_defs[0];
+	persistent_memory::set_modem_baudrate(def_bell202->baudrate);
+	serial_format_t serial_format;
+	serial_format.data_bits = 7;
+	serial_format.parity = EVEN;
+	serial_format.stop_bits = 1;
+	serial_format.bit_order = LSB_FIRST;
+	persistent_memory::set_serial_format(serial_format);
+	
+	field_frequency.set_value(receiver_model.tuning_frequency());
+	field_frequency.set_step(100);
+	field_frequency.on_change = [this](rf::Frequency f) {
+		update_freq(f);
+	};
+	field_frequency.on_edit = [this, &nav]() {
+		auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
+		new_view->on_changed = [this](rf::Frequency f) {
+			update_freq(f);
+			field_frequency.set_value(f);
+		};
+	};
+
+	button_modem_setup.on_select = [&nav](Button&) {
+		nav.push<ModemSetupView>();
+	};
+	
+	
+	// Auto-configure modem for LCR RX (will be removed later)
+	baseband::set_btle(persistent_memory::modem_baudrate(), 8, 0, false);
+	
+	audio::set_rate(audio::Rate::Hz_24000);
+	audio::output::start();
+	
+	receiver_model.set_sampling_rate(4000000);
+	receiver_model.set_baseband_bandwidth(4000000);
+	receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
+	receiver_model.enable();
+}
+
+void BTLERxView::on_data(uint32_t value, bool is_data) {
+	//std::string str_console = "\x1B";
+	std::string str_console = "";
+	if (is_data) {
+		// Colorize differently after message splits
+		//str_console += (char)((console_color & 3) + 9);
+		
+		//value &= 0xFF;											// ABCDEFGH
+		//value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4);	// EFGHABCD
+		//value = ((value & 0xCC) >> 2) | ((value & 0x33) << 2);	// GHEFCDAB
+		//value = ((value & 0xAA) >> 1) | ((value & 0x55) << 1);	// HGFEDCBA
+		//value &= 0x7F;											// Ignore parity, which is the MSB now
+		
+		//if ((value >= 32) && (value < 127)) {
+		//	str_console += (char)value;							// Printable
+		//} 
+		
+		//str_console += (char)'A';
+		//str_console += (char)value;
+		//str_console += "[" + to_string_hex(value, 2) + "]";	
+		str_console += ":" + to_string_hex(value, 2) ;	
+		console.write(str_console);
+		
+		
+		
+		/*if ((value != 0x7F) && (prev_value == 0x7F)) {
+			// Message split
+			console.writeln("");
+			console_color++;
+			
+			
+		}*/
+		//prev_value = value;
+	} else {
+		// Baudrate estimation
+		//text_debug.set("~" + to_string_dec_uint(value)); 
+		if (value == 'A')
+		{console.write("mac");}
+		else if (value == 'B')
+		{console.writeln("");}
+		//console.writeln("");
+	}
+}
+
+BTLERxView::~BTLERxView() {
+	audio::output::stop();
+	receiver_model.disable();
+	baseband::shutdown();
+}
+
+} /* namespace ui */
diff --git a/firmware/application/apps/ui_btle_rx.hpp b/firmware/application/apps/ui_btle_rx.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9e331eefcee58c8274c9213f744778a292bc32f2
--- /dev/null
+++ b/firmware/application/apps/ui_btle_rx.hpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
+ * Copyright (C) 2017 Furrtek
+ * Copyright (C) 2020 Shao
+ *
+ * This file is part of PortaPack.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __UI_BTLE_RX_H__
+#define __UI_BTLE_RX_H__
+
+#include "ui.hpp"
+#include "ui_navigation.hpp"
+#include "ui_receiver.hpp"
+#include "ui_record_view.hpp"	// DEBUG
+
+#include "utility.hpp"
+
+namespace ui {
+
+class BTLERxView : public View {
+public:
+	BTLERxView(NavigationView& nav);
+	~BTLERxView();
+
+	void focus() override;
+
+	std::string title() const override { return "BTLE RX"; };
+	
+private:
+	void on_data(uint32_t value, bool is_data);
+	
+	uint8_t console_color { 0 };
+	uint32_t prev_value { 0 };
+	std::string str_log { "" };
+
+	RFAmpField field_rf_amp {
+		{ 13 * 8, 0 * 16 }
+	};
+	LNAGainField field_lna {
+		{ 15 * 8, 0 * 16 }
+	};
+	VGAGainField field_vga {
+		{ 18 * 8, 0 * 16 }
+	};
+	RSSI rssi {
+		{ 21 * 8, 0, 6 * 8, 4 },
+	};
+	Channel channel {
+		{ 21 * 8, 5, 6 * 8, 4 },
+	};
+	
+	FrequencyField field_frequency {
+		{ 0 * 8, 0 * 16 },
+	};
+	
+	Text text_debug {
+		{ 0 * 8, 1 * 16, 10 * 8, 16 },
+		"DEBUG"
+	};
+	
+	
+	Button button_modem_setup {
+		{ 12 * 8, 1 * 16, 96, 24 },
+		"Modem setup"
+	};
+	
+	// DEBUG
+	RecordView record_view {
+		{ 0 * 8, 3 * 16, 30 * 8, 1 * 16 },
+		u"AFS_????", RecordView::FileType::WAV, 4096, 4
+	};
+	
+	Console console {
+		{ 0, 4 * 16, 240, 240 }
+	};
+
+	void update_freq(rf::Frequency f);
+	//void on_data_afsk(const AFSKDataMessage& message);
+	
+	MessageHandlerRegistration message_handler_packet {
+		Message::ID::AFSKData,
+		[this](Message* const p) {
+			const auto message = static_cast<const AFSKDataMessage*>(p);
+			this->on_data(message->value, message->is_data);
+		}
+	};
+};
+
+} /* namespace ui */
+
+#endif/*__UI_BTLE_RX_H__*/
diff --git a/firmware/application/baseband_api.cpp b/firmware/application/baseband_api.cpp
index be969ac828c357de8f12b075e1b96b03e4d6b1da..502047d2374993fd41cada71c15a426932dec93e 100644
--- a/firmware/application/baseband_api.cpp
+++ b/firmware/application/baseband_api.cpp
@@ -130,6 +130,16 @@ void set_afsk(const uint32_t baudrate, const uint32_t word_length, const uint32_
 	send_message(&message);
 }
 
+void set_btle(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word) {
+	const BTLERxConfigureMessage message {
+		baudrate,
+		word_length,
+		trigger_value,
+		trigger_word
+	};
+	send_message(&message);
+}
+    
 void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word) {
 	const NRFRxConfigureMessage message {
 		baudrate,
@@ -139,7 +149,7 @@ void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t
 	};
 	send_message(&message);
 }
-
+    
 void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phase_inc_mark, const uint32_t afsk_phase_inc_space,
 					const uint8_t afsk_repeat, const uint32_t afsk_bw, const uint8_t symbol_count) {
 	const AFSKTxConfigureMessage message {
diff --git a/firmware/application/baseband_api.hpp b/firmware/application/baseband_api.hpp
index 30ad60841b69afa6de1505f96e9a9734259c8d44..9592f8ca84b2aea8d462836f22b5d091462582b6 100644
--- a/firmware/application/baseband_api.hpp
+++ b/firmware/application/baseband_api.hpp
@@ -68,7 +68,11 @@ void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phas
 					const uint8_t afsk_repeat, const uint32_t afsk_bw, const uint8_t symbol_count);
 void kill_afsk();
 void set_afsk(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word);
+
+void set_btle(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word);
+
 void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word);
+
 void set_ook_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint8_t repeat,
 					const uint32_t pause_symbols);
 void set_fsk_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint32_t shift,
diff --git a/firmware/application/bitmap.hpp b/firmware/application/bitmap.hpp
index 0d11ceabe08706af76a8a7f25ece0029fc5402c6..9626bedecc5fb5ac52e030416537e24394bff3c2 100644
--- a/firmware/application/bitmap.hpp
+++ b/firmware/application/bitmap.hpp
@@ -1503,6 +1503,28 @@ static constexpr Bitmap bitmap_icon_replay {
 	{ 16, 16 }, bitmap_icon_replay_data
 };
 
+static constexpr uint8_t bitmap_icon_btle_data[] = {
+	0x00, 0x00, 
+	0x80, 0x00, 
+	0x80, 0x01, 
+	0x80, 0x07, 
+	0x80, 0x0C, 
+	0x98, 0x06, 
+	0xF0, 0x03, 
+	0xE0, 0x01, 
+	0xE0, 0x01, 
+	0xF0, 0x03, 
+	0x98, 0x06, 
+	0x80, 0x0C, 
+	0x80, 0x07, 
+	0x80, 0x01, 
+	0x80, 0x00, 
+	0x00, 0x00, 
+};
+static constexpr Bitmap bitmap_icon_btle {
+	{ 16, 16 }, bitmap_icon_btle_data
+};
+
 static constexpr uint8_t bitmap_icon_nrf_data[] = {
 	0x00, 0x00, 
 	0x00, 0x00, 
diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp
index 8e000fbf069f84b5d34b80b659c68f3a710f9083..0d89d1c4addd5a9643d2feebc125bda6f26e05a1 100644
--- a/firmware/application/ui_navigation.cpp
+++ b/firmware/application/ui_navigation.cpp
@@ -34,6 +34,7 @@
 #include "ui_adsb_rx.hpp"
 #include "ui_adsb_tx.hpp"
 #include "ui_afsk_rx.hpp"
+#include "ui_btle_rx.hpp"
 #include "ui_nrf_rx.hpp"
 #include "ui_aprs_tx.hpp"
 #include "ui_bht_tx.hpp"
@@ -348,6 +349,7 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) {
 		{ "ACARS", 		ui::Color::yellow(),	&bitmap_icon_adsb,		[&nav](){ nav.push<ACARSAppView>(); }, },
 		{ "AIS Boats",	ui::Color::green(),		&bitmap_icon_ais,		[&nav](){ nav.push<AISAppView>(); } },
 		{ "AFSK", 		ui::Color::yellow(),	&bitmap_icon_receivers,	[&nav](){ nav.push<AFSKRxView>(); } },
+		{ "BTLE",		ui::Color::yellow(),	&bitmap_icon_btle,		[&nav](){ nav.push<BTLERxView>(); } },
 		{ "NRF", 		ui::Color::yellow(),	&bitmap_icon_nrf,		[&nav](){ nav.push<NRFRxView>(); } }, 
 		{ "Audio", 		ui::Color::green(),		&bitmap_icon_speaker,	[&nav](){ nav.push<AnalogAudioView>(); } },
 		{ "ERT Meter", 	ui::Color::green(), 	&bitmap_icon_ert,		[&nav](){ nav.push<ERTAppView>(); } },
diff --git a/firmware/baseband/CMakeLists.txt b/firmware/baseband/CMakeLists.txt
index 0d1000b8b7b4f57499eaa20397ddbc671064259c..51f1bdb1d78a48036f2a3b839a5449392b466b7a 100644
--- a/firmware/baseband/CMakeLists.txt
+++ b/firmware/baseband/CMakeLists.txt
@@ -318,6 +318,12 @@ set(MODE_CPPSRC
 )
 DeclareTargets(PNRR nrfrx)
 
+### BTLE RX
+
+set(MODE_CPPSRC
+	proc_btlerx.cpp
+)
+DeclareTargets(PBTR btlerx)
 ### AIS
 
 set(MODE_CPPSRC
diff --git a/firmware/baseband/proc_btlerx.cpp b/firmware/baseband/proc_btlerx.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f4e15571b144e664c6388cdf845904f28c912d58
--- /dev/null
+++ b/firmware/baseband/proc_btlerx.cpp
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
+ * Copyright (C) 2016 Furrtek
+ * Copyright (C) 2020 Shao
+ *
+ * This file is part of PortaPack.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "proc_btlerx.hpp"
+#include "portapack_shared_memory.hpp"
+
+#include "event_m4.hpp"
+
+void BTLERxProcessor::execute(const buffer_c8_t& buffer) {
+	if (!configured) return;
+	
+	// FM demodulation
+
+	/*const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
+	const auto channel = decim_1.execute(decim_0_out, dst_buffer);
+
+	feed_channel_stats(channel);
+	
+	auto audio_oversampled = demod.execute(channel, work_audio_buffer);*/
+
+	const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
+	feed_channel_stats(decim_0_out);
+	
+	auto audio_oversampled = demod.execute(decim_0_out, work_audio_buffer);
+
+	/*std::fill(spectrum.begin(), spectrum.end(), 0);
+	for(size_t i=0; i<spectrum.size(); i++) {
+		spectrum[i] += buffer.p[i];
+	}
+	const buffer_c16_t buffer_c16 {spectrum.data(),spectrum.size(),buffer.sampling_rate};
+	feed_channel_stats(buffer_c16);
+	
+	auto audio_oversampled = demod.execute(buffer_c16, work_audio_buffer);*/
+	// Audio signal processing
+	for (size_t c = 0; c < audio_oversampled.count; c++) {
+		int result;
+
+		/*const int32_t sample_int = audio_oversampled.p[c] * 32768.0f;
+		int32_t current_sample = __SSAT(sample_int, 16);
+		current_sample /= 128;*/
+                
+		int32_t current_sample = audio_oversampled.p[c]; //if I directly use this, some results can pass crc but not correct.
+                rb_head++;
+	        rb_head=(rb_head)%RB_SIZE;
+
+                rb_buf[rb_head] = current_sample;
+
+		skipSamples = skipSamples - 1;
+
+
+		if (skipSamples<1)
+		{
+				
+
+			int32_t threshold_tmp=0;
+			for (int c=0;c<8;c++)
+			{
+				threshold_tmp = threshold_tmp + (int32_t)rb_buf[(rb_head+c)%RB_SIZE];
+			}
+			g_threshold = (int32_t)threshold_tmp/8;
+
+				
+			int transitions=0;
+			if (rb_buf[(rb_head+9)%RB_SIZE] > g_threshold)
+			{
+				for (int c=0;c<8;c++)
+				{
+					if (rb_buf[(rb_head + c)%RB_SIZE] > rb_buf[(rb_head + c + 1)%RB_SIZE])
+					transitions = transitions + 1;
+				}
+			}
+			else
+			{
+				for (int c=0;c<8;c++)
+				{
+					if (rb_buf[(rb_head + c)%RB_SIZE] < rb_buf[(rb_head + c + 1)%RB_SIZE])
+					transitions = transitions + 1;
+				}
+			}
+			
+			bool packet_detected=false;
+			//if ( transitions==4 && abs(g_threshold)<15500)
+			if ( transitions==4)
+			{
+
+				
+				uint8_t packet_data[500];
+				int packet_length;
+				uint32_t packet_crc;
+				uint32_t calced_crc;
+				uint64_t packet_addr_l;
+				uint32_t result;
+				uint8_t crc[3];
+				uint8_t packet_header_arr[2];
+
+				packet_addr_l=0;
+				for (int i=0;i<4;i++) 
+				{                   
+				    bool current_bit;
+				    uint8_t byte=0;
+				    for (int c=0;c<8;c++)
+				    {
+				        if (rb_buf[(rb_head + (i+1)*8 + c)%RB_SIZE] > g_threshold)
+				            current_bit = true;
+				        else
+				            current_bit = false;
+				        byte |= current_bit << (7-c);
+				    }
+				    uint8_t byte_temp = (uint8_t) (((byte * 0x0802LU & 0x22110LU) | (byte * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
+				    packet_addr_l|=((uint64_t)byte_temp)<<(8*i);
+				}
+
+				channel_number = 38;
+
+				
+				for (int t=0;t<2;t++)
+				{
+				    bool current_bit;
+				    uint8_t byte=0;
+				    for (int c=0;c<8;c++)
+				    {
+				        if (rb_buf[(rb_head + 5*8+t*8 + c)%RB_SIZE] > g_threshold)
+				            current_bit = true;
+				        else
+				            current_bit = false;
+				        byte |= current_bit << (7-c);
+				    }
+
+				    packet_header_arr[t] = byte;
+				}
+
+				
+
+				uint8_t byte_temp2 = (uint8_t) (((channel_number * 0x0802LU & 0x22110LU) | (channel_number * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
+				uint8_t lfsr_1 = byte_temp2 | 2;
+				int header_length = 2;
+				int header_counter = 0;
+				while(header_length--)
+				{
+				    for(uint8_t i = 0x80; i; i >>= 1)
+				    {
+				        if(lfsr_1 & 0x80)
+				        {
+				            lfsr_1 ^= 0x11;
+				            (packet_header_arr[header_counter]) ^= i;
+				        }
+				        lfsr_1 <<= 1;
+				    }
+				    header_counter = header_counter + 1;
+				}
+
+				
+
+				if (packet_addr_l==0x8E89BED6)
+				{  
+
+				    uint8_t byte_temp3 = (uint8_t) (((packet_header_arr[1] * 0x0802LU & 0x22110LU) | (packet_header_arr[1] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
+				    packet_length=byte_temp3&0x3F;
+				
+				} 
+				else 
+				{
+				    packet_length=0;
+				}
+
+				for (int t=0;t<packet_length+2+3;t++)
+				{
+				    bool current_bit;
+				    uint8_t byte=0;
+				    for (int c=0;c<8;c++)
+				    {
+				        if (rb_buf[(rb_head + 5*8+t*8 + c)%RB_SIZE] > g_threshold)
+				            current_bit = true;
+				        else
+				            current_bit = false;
+				        byte |= current_bit << (7-c);
+				    }
+
+				    packet_data[t] = byte;
+				}
+
+				
+
+				uint8_t byte_temp4 = (uint8_t) (((channel_number * 0x0802LU & 0x22110LU) | (channel_number * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
+				uint8_t lfsr_2 = byte_temp4 | 2;
+				int pdu_crc_length = packet_length+2+3;
+				int pdu_crc_counter = 0;
+				while(pdu_crc_length--)
+				{
+				    for(uint8_t i = 0x80; i; i >>= 1)
+				    {
+				        if(lfsr_2 & 0x80)
+				        {
+				            lfsr_2 ^= 0x11;
+				            (packet_data[pdu_crc_counter]) ^= i;
+				        }
+				        lfsr_2 <<= 1;
+				    }
+				    pdu_crc_counter = pdu_crc_counter + 1;
+				}
+
+				
+
+				if (packet_addr_l==0x8E89BED6)
+				{  
+				    crc[0]=crc[1]=crc[2]=0x55;
+				}
+				else 
+				{
+				    crc[0]=crc[1]=crc[2]=0;
+				}
+
+				uint8_t v, t, d, crc_length;
+				uint32_t crc_result=0;
+				crc_length = packet_length + 2;
+				int counter = 0;
+				while(crc_length--)
+				{
+				    uint8_t byte_temp5 = (uint8_t) (((packet_data[counter] * 0x0802LU & 0x22110LU) | (packet_data[counter] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
+				    d = byte_temp5;
+				    for(v = 0; v < 8; v++, d >>= 1)
+				    {
+				        t = crc[0] >> 7;
+				        crc[0] <<= 1;
+				        if(crc[1] & 0x80) crc[0] |= 1;
+				        crc[1] <<= 1;
+				        if(crc[2] & 0x80) crc[1] |= 1;
+				        crc[2] <<= 1;
+				        if(t != (d & 1))
+				        {
+				            crc[2] ^= 0x5B;
+				            crc[1] ^= 0x06;
+				        }
+				    }
+				    counter = counter + 1;
+				}
+				for (v=0;v<3;v++) crc_result=(crc_result<<8)|crc[v];
+				calced_crc = crc_result;
+
+				packet_crc=0;
+				for (int c=0;c<3;c++) packet_crc=(packet_crc<<8)|packet_data[packet_length+2+c];
+
+				if (packet_addr_l==0x8E89BED6)
+				//if (packet_crc==calced_crc)
+				{
+				    uint8_t mac_data[6];
+				    int counter = 0;
+				    for (int i = 7; i >= 2; i--) 
+				    {
+				        uint8_t byte_temp6 = (uint8_t) (((packet_data[i] * 0x0802LU & 0x22110LU) | (packet_data[i] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
+					//result = byte_temp6;
+					mac_data[counter] = byte_temp6;
+					counter = counter + 1;
+				    }
+
+				    data_message.is_data = false;
+			            data_message.value = 'A';
+				    shared_memory.application_queue.push(data_message);
+
+				    data_message.is_data = true;
+			            data_message.value = mac_data[0];
+				    shared_memory.application_queue.push(data_message);
+
+				    data_message.is_data = true;
+			            data_message.value = mac_data[1];
+				    shared_memory.application_queue.push(data_message);
+
+				    data_message.is_data = true;
+			            data_message.value = mac_data[2];
+				    shared_memory.application_queue.push(data_message);
+
+				    data_message.is_data = true;
+			            data_message.value = mac_data[3];
+				    shared_memory.application_queue.push(data_message);
+
+				    data_message.is_data = true;
+			            data_message.value = mac_data[4];
+				    shared_memory.application_queue.push(data_message);
+
+				    data_message.is_data = true;
+			            data_message.value = mac_data[5];
+				    shared_memory.application_queue.push(data_message);
+
+				    data_message.is_data = false;
+			            data_message.value = 'B';
+				    shared_memory.application_queue.push(data_message);
+
+				    packet_detected = true;
+				}
+				else
+				    packet_detected = false;
+			}
+
+			if (packet_detected) 
+			{
+				skipSamples=20;
+			}
+		}
+	}
+}
+
+void BTLERxProcessor::on_message(const Message* const message) {
+	if (message->id == Message::ID::BTLERxConfigure)
+		configure(*reinterpret_cast<const BTLERxConfigureMessage*>(message));
+}
+
+void BTLERxProcessor::configure(const BTLERxConfigureMessage& message) {	
+	decim_0.configure(taps_200k_wfm_decim_0.taps, 33554432);
+	decim_1.configure(taps_200k_wfm_decim_1.taps, 131072);
+	demod.configure(audio_fs, 5000);
+
+	configured = true;
+}
+
+int main() {
+	EventDispatcher event_dispatcher { std::make_unique<BTLERxProcessor>() };
+	event_dispatcher.run();
+	return 0;
+}
diff --git a/firmware/baseband/proc_btlerx.hpp b/firmware/baseband/proc_btlerx.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2bb48872b86885131ad4ef79b5f470924b99dfc4
--- /dev/null
+++ b/firmware/baseband/proc_btlerx.hpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
+ * Copyright (C) 2016 Furrtek
+ * Copyright (C) 2020 Shao
+ *
+ * This file is part of PortaPack.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __PROC_BTLERX_H__
+#define __PROC_BTLERX_H__
+
+#include "baseband_processor.hpp"
+#include "baseband_thread.hpp"
+#include "rssi_thread.hpp"
+
+#include "dsp_decimate.hpp"
+#include "dsp_demodulate.hpp"
+
+#include "audio_output.hpp"
+
+#include "fifo.hpp"
+#include "message.hpp"
+
+class BTLERxProcessor : public BasebandProcessor {
+public:
+	void execute(const buffer_c8_t& buffer) override;
+
+	void on_message(const Message* const message) override;
+	
+private:
+	static constexpr size_t baseband_fs = 4000000;
+	static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2;
+	
+	BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
+	RSSIThread rssi_thread { NORMALPRIO + 10 };
+	
+	std::array<complex16_t, 512> dst { };
+	const buffer_c16_t dst_buffer {
+		dst.data(),
+		dst.size()
+	};
+
+        std::array<complex16_t, 512> spectrum { };
+	const buffer_c16_t spectrum_buffer {
+		spectrum.data(),
+		spectrum.size()
+	};
+
+	const buffer_s16_t work_audio_buffer {
+		(int16_t*)dst.data(),
+		sizeof(dst) / sizeof(int16_t)
+	};
+
+
+	// Array size ok down to 375 bauds (24000 / 375)
+	std::array<int32_t, 64> delay_line { 0 };
+	std::array<int16_t, 1000> rb_buf { 0 };
+
+	/*dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
+	dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
+	dsp::decimate::FIRAndDecimateComplex channel_filter { };*/
+	dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { };
+	dsp::decimate::FIRC16xR16x16Decim2 decim_1 { };
+	
+	dsp::demodulate::FM demod { };
+	int rb_head {-1};
+	int32_t g_threshold {0};  
+	uint8_t channel_number {38};
+	int skipSamples {1000};
+	int RB_SIZE {1000};
+
+	bool configured { false };
+
+	
+	void configure(const BTLERxConfigureMessage& message);
+	
+	AFSKDataMessage data_message { false, 0 };
+};
+
+#endif/*__PROC_BTLERX_H__*/
diff --git a/firmware/common/message.hpp b/firmware/common/message.hpp
index e1a507d1c0200ec3d28f297f39c9393995a9a2ec..4ca93da9fa3a8b942f3dc7aebf34ce0cb5efc678 100644
--- a/firmware/common/message.hpp
+++ b/firmware/common/message.hpp
@@ -78,6 +78,7 @@ public:
 		AFSKRxConfigure = 22,
 		StatusRefresh = 23,
 		SamplerateConfig = 24,
+		BTLERxConfigure = 25,
 		NRFRxConfigure = 26,
 
 		TXProgress = 30,
@@ -723,6 +724,25 @@ public:
 	const bool trigger_word;
 };
 
+class BTLERxConfigureMessage : public Message {
+public:
+	constexpr BTLERxConfigureMessage(
+		const uint32_t baudrate,
+		const uint32_t word_length,
+		const uint32_t trigger_value,
+		const bool trigger_word
+	) : Message { ID::BTLERxConfigure },
+		baudrate(baudrate),
+		word_length(word_length),
+		trigger_value(trigger_value),
+		trigger_word(trigger_word)
+	{
+    }
+	const uint32_t baudrate;
+	const uint32_t word_length;
+	const uint32_t trigger_value;
+	const bool trigger_word;
+};
 
 class NRFRxConfigureMessage : public Message {
 public:
@@ -737,13 +757,13 @@ public:
 		trigger_value(trigger_value),
 		trigger_word(trigger_word)
 	{
-	}
-	
+    }
 	const uint32_t baudrate;
 	const uint32_t word_length;
 	const uint32_t trigger_value;
 	const bool trigger_word;
 };
+
 class PitchRSSIConfigureMessage : public Message {
 public:
 	constexpr PitchRSSIConfigureMessage(
diff --git a/firmware/common/spi_image.hpp b/firmware/common/spi_image.hpp
index ce2522c6cdd83177af8fe832050ab35a5ccf74ce..8c6ef60ffc28a33a18a312af7df4fb9fd433abcf 100644
--- a/firmware/common/spi_image.hpp
+++ b/firmware/common/spi_image.hpp
@@ -68,6 +68,7 @@ private:
 constexpr image_tag_t image_tag_acars				{ 'P', 'A', 'C', 'A' };
 constexpr image_tag_t image_tag_adsb_rx				{ 'P', 'A', 'D', 'R' };
 constexpr image_tag_t image_tag_afsk_rx				{ 'P', 'A', 'F', 'R' };
+constexpr image_tag_t image_tag_btle_rx				{ 'P', 'B', 'T', 'R' };
 constexpr image_tag_t image_tag_nrf_rx				{ 'P', 'N', 'R', 'R' };
 constexpr image_tag_t image_tag_ais					{ 'P', 'A', 'I', 'S' };
 constexpr image_tag_t image_tag_am_audio			{ 'P', 'A', 'M', 'A' };