2.4G芯片做蓝牙广播发送数据(伪蓝牙)

1 篇文章 0 订阅
1 篇文章 1 订阅

伪蓝牙LE(Faking Bluetooth LE)

Bluetooth LE是蓝牙4.0规范中引入的一项新技术。 除了名称外,它与蓝牙完全无关。它是为低功耗而设计的,设计表明了这一点。它不像真正的蓝牙那样,会按照精确的时间表进行跳频,而不管任何事情,LE都会在发送一定数量的数据包后进行跳频,因此无需唤醒即可保持运行时钟来知道下一跳的位置。实际上,LE允许设备在保持连接的同时,长时间完全关闭其无线电。这对于键盘和鼠标以及所有其他类似的东西来说非常棒。LE的另一个很酷的特性是,设备可以主动发送小块数据的广播。与真正的蓝牙不同,在现实世界中扫描设备可能是被动的——你只需在正确的频道上收听广播包,就能听到所有的广播。

LE的简单信道跳变行为意味着我们可能假装成没有正常蓝牙所需的复杂无线电的LE设备。 频率为2.4GHz,信道间隔为1MHz,调制为GFSK,数据速率为1MBps,前导码为10101010或01010101(基于第一个数据字节),并使用32位地址进行寻址。 哎呀,我们不知道可以做到所有这些的设备吗? 当然可以! 广受欢迎的北欧nRF24L01 +。 因此,让我们看一下LE需求与我们拥有的需求之间的区别:

LE的信道跳频行为意味着我们可以假装是一个LE设备,而不需要普通bluetoth所需要的复杂无线电。频率为2.4GHz,信道间隔为1MHz,调制为GFSK,数据速率为1MBps,前导码为10101010或01010101(基于第一个数据字节),使用32位地址进行寻址。

本文使用芯片nRF24L01+来实现。所以让我们来看看LE的需求和nRF24L01+之间的区别:

  • CRC:校验:BTLE使用24位CRC,这是nRF24不能做到的。对我们来说幸运的是,nRF24允许我们禁用CRC,然后我们可以发送自己的CRC并手动检查收到的CRC。
  • Frequency Hopping:跳频:BTLE需要能够快速调频-150us或更快。不幸的是nRF24让我们失望了。 每次传输后,它关闭PLL,然后在每次发送或接收命令时重新启动。。 这需要130us。 遗憾的是,我们剩下的时间太少了。 幸运的是,这不是主要问题。
  • Packet Length:数据包长度:BTLE数据包大小各异,实际上有效载荷最多39字节。 但是我们真正想要支持的一个数据包是CONNECT_REQ数据包,实际上是34字节有效负载+ 3 CRC,这意味着如果我们要检查CRC,就需要接收37字节,否则只需要接收34。nRF24不会处理超过32个字节的数据包。 遗憾的是,最后两个字节很重要(跳频的大小和通道映射的部分)。 这就是我们不能实现的地方。

所以...我们不能接收连接,但不会丢失所有... BTLE允许未经请求的广播,因此我们仍然可以通过使它们向将收听的任何人广播数据来做一些很酷的事情。

让我们计算出我们能发送多少数据...在我们32字节的预算中:3字节:CRC,2字节:ADV_NONCONN_IND数据包头,6字节:MAC地址,剩下21字节的有效负载。 但是,此有效负载必须具有结构。 假设所需的头最少,如果我们不需要广播设备名称,则可以发送19个字节的数据。 如果需要广播设备名称,我们有17个字节可以在名称(在UTF-8中)和数据之间分割。如果我们想更好地遵守规范和广播设备属性,我们将有14个字节的名称和数据或16个字节的纯数据。

然后让我们整理细节……首先,BTLE和nRF24以相反的顺序在空中发送数据位,因此我们必须将所有位都反转。 其次,BTLE使用数据白化,而nRF24不使用数据白化,因此我们也需要手动进行。 最后,前面提到的24位CRC。

所有LE广播都发送到相同的“地址”:0x8E89BED6,也称为“bed6”。 当然,对我们来说,它将是位反转。 BTLE将CRC应用于整个有效负载,而不是地址。 白化应用于有效载荷和CRC。 因此,知道了这一点,就为我们提供了组装完整工作包所需的事件顺序。 广播包在3个通道上发送:37、38和39,分别为2.402HGz,2.426GHz和2.480GHz。 我们将在他们之间轮换,有条不紊地广播。

BTLE CRC在C语言中不太难实现,使用初始值0x555555,看起来像这样:

void btLeCrc(const uint8_t* data, uint8_t len, uint8_t* dst){

	uint8_t v, t, d;

	while(len--){
		d = *data++;
		for(v = 0; v < 8; v++, d >>= 1){
		
			t = dst[0] >> 7;
			dst[0] <<= 1;
			if(dst[1] & 0x80) dst[0] |= 1;
			dst[1] <<= 1;
			if(dst[2] & 0x80) dst[1] |= 1;
			dst[2] <<= 1;
			
			if(t != (d & 1)){
			
				dst[2] ^= 0x5B;
				dst[1] ^= 0x06;
			}
		}	
	}
}

数据白化功能也不太复杂。 它是一种7位线性移位反馈样式,并通过等于(channelNum << 1)+ 1的值进行初始化。代码如下所示:

void btLeWhiten(uint8_t* data, uint8_t len, uint8_t whitenCoeff){
	uint8_t  m;
	
	while(len--){
	
		for(m = 1; m; m <<= 1){
		
			if(whitenCoeff & 0x80){
				
				whitenCoeff ^= 0x11;
				(*data) ^= m;
			}
			whitenCoeff <<= 1;
		}
		data++;
	}
}

广播包的有效载荷如下所示:

struct adv_hdr{
	uint8_t header; //we use 0x40 to say it is a non-connectable undirected
			//advertisement and address we're sending is random (not assigned)
	uint8_t dataLen; //length of following data (including MAC addr)
	uint8_t MAC[6]; //the mac address
}

因此,如果将所有这些都放在一起,最终将得到带有上述标头的数据包。 然后,我们使用上述CRC函数对其进行CRC处理。 然后,使用上面的btLeWhiten函数将其白化。 之后,我们将其发送。 让我们看看...是的,它有效。 运行BTLExplorer的iPad3显示我们的设备可见并且是BTLE设备。 凉! 一个警告:如果您不广播设备名称,则BTLExplorer将崩溃-这是它们的错误,因此请不要担心。

有效载荷数据格式是什么? 你可能会问。 好吧,数据是由块组成的,每个块由3部分组成,长度、类型、数据。

长度:包括类型+数据的长度

类型:用来表示数据的作用,比如为08或者09时代表数据部分属于设备名称

数据:不同类型代表不同意义的数据

我们一般只使用以下几个类型。

  • 1:      标志-> 长度为2,数据为5 这表示处于受限发现模式的单模式(不是BTLE / BT组合)设备。
  • 8或9:名称-> 长度为数据长度加1,数据是设备名称(ascii码)。 8表示“简称”,9表示“全称”。
  • 255:自定义数据。 您可以在此处推送自定义数据。

   参考:Assigned Numbers | Bluetooth® Technology Website

完整的示例代码大致如下所示:

#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#define F_CPU	8000000
#include <avr/delay.h>


#define PIN_CE	1 //Output
#define PIN_nCS	2 //Output


#define MY_MAC_0	0xEF
#define MY_MAC_1	0xFF
#define MY_MAC_2	0xC0
#define MY_MAC_3	0xAA
#define MY_MAC_4	0x18
#define MY_MAC_5	0x00


ISR(PCINT0_vect)
{
	//useless
}

void btLeCrc(const uint8_t* data, uint8_t len, uint8_t* dst){

	uint8_t v, t, d;

	while(len--){
	
		d = *data++;
		for(v = 0; v < 8; v++, d >>= 1){
		
			t = dst[0] >> 7;
			
			dst[0] <<= 1;
			if(dst[1] & 0x80) dst[0] |= 1;
			dst[1] <<= 1;
			if(dst[2] & 0x80) dst[1] |= 1;
			dst[2] <<= 1;
			
		
			if(t != (d & 1)){
			
				dst[2] ^= 0x5B;
				dst[1] ^= 0x06;
			}
		}	
	}
}

uint8_t  swapbits(uint8_t a){

	uint8 v = 0;
	
	if(a & 0x80) v |= 0x01;
	if(a & 0x40) v |= 0x02;
	if(a & 0x20) v |= 0x04;
	if(a & 0x10) v |= 0x08;
	if(a & 0x08) v |= 0x10;
	if(a & 0x04) v |= 0x20;
	if(a & 0x02) v |= 0x40;
	if(a & 0x01) v |= 0x80;

	return v;
}

void btLeWhiten(uint8_t* data, uint8_t len, uint8_t whitenCoeff){

	uint8_t  m;
	
	while(len--){
	
		for(m = 1; m; m <<= 1){
		
			if(whitenCoeff & 0x80){
				
				whitenCoeff ^= 0x11;
				(*data) ^= m;
			}
			whitenCoeff <<= 1;
		}
		data++;
	}
}

static inline uint8_t btLeWhitenStart(uint8_t chan){
	//the value we actually use is what BT'd use left shifted one...makes our life easier

	return swapbits(chan) | 2;	
}

void btLePacketEncode(uint8_t* packet, uint8_t len, uint8_t chan){
	//length is of packet, including crc. pre-populate crc in packet with initial crc value!

	uint8_t i, dataLen = len - 3;
	
	btLeCrc(packet, dataLen, packet + dataLen);
	for(i = 0; i < 3; i++, dataLen++) packet[dataLen] = swapbits(packet[dataLen]);
	btLeWhiten(packet, len, btLeWhitenStart(chan));
	for(i = 0; i < len; i++) packet[i] = swapbits(packet[i]);
	
}

uint8_t spi_byte(uint8_t byte){

	uint8_t i = 0;
	
	do{
		PORTB &=~ (uint8_t)(1 << 6);
		if(byte & 0x80) PORTB |= (uint8_t)(1 << 6);
		CLK |= (uint8_t)(1 << 4);
		byte <<= 1;
		if(PINA & (uint8_t)32) byte++;
		CLK &=~ (uint8_t)(1 << 4);
	
	}while(--i);

	return byte;
}

void nrf_cmd(uint8_t cmd, uint8_t data)
{
	cbi(PORTB, PIN_nCS);
	spi_byte(cmd);
	spi_byte(data);
	sbi(PORTB, PIN_nCS); //Deselect chip
}

void nrf_simplebyte(uint8_t cmd)
{
	cbi(PORTB, PIN_nCS);
	spi_byte(cmd);
	sbi(PORTB, PIN_nCS);
}

void nrf_manybytes(uint8_t* data, uint8_t len){

	cbi(PORTB, PIN_nCS);
	do{
	
		spi_byte(*data++);
	
	}while(--len);
	sbi(PORTB, PIN_nCS);
}

void fob_init (void)
{
	DDRA = (uint8_t)~(1<<5);
	DDRB = 0b00000110;
	PORTA = 0b10001111;
	cbi(PORTB, PIN_CE);
	TCCR0B = (1<<CS00);
	MCUCR = (1<<SM1)|(1<<SE);
	sei();
}

int main (void)
{
	static const uint8_t chRf[] = {2, 26,80};
	static const uint8_t chLe[] = {37,38,39};
	uint8_t i, L, ch = 0;
	uint8_t buf[32];
	
	fob_init();
	
	DDRA |= 4;
	PORTA |= 4;
	
	nrf_cmd(0x20, 0x12);	//on, no crc, int on RX/TX done
	nrf_cmd(0x21, 0x00);	//no auto-acknowledge
	nrf_cmd(0x22, 0x00);	//no RX
	nrf_cmd(0x23, 0x02);	//5-byte address
	nrf_cmd(0x24, 0x00);	//no auto-retransmit
	nrf_cmd(0x26, 0x06);	//1MBps at 0dBm
	nrf_cmd(0x27, 0x3E);	//clear various flags
	nrf_cmd(0x3C, 0x00);	//no dynamic payloads
	nrf_cmd(0x3D, 0x00);	//no features
	nrf_cmd(0x31, 32);	//always RX 32 bytes
	nrf_cmd(0x22, 0x01);	//RX on pipe 0
	
	buf[0] = 0x30;			//set addresses
	buf[1] = swapbits(0x8E);
	buf[2] = swapbits(0x89);
	buf[3] = swapbits(0xBE);
	buf[4] = swapbits(0xD6);
	nrf_manybytes(buf, 5);
	buf[0] = 0x2A;
	nrf_manybytes(buf, 5);

	
	
	while(1){
		
		L = 0;
		
		buf[L++] = 0x40;	//PDU type, given address is random
        Xbuf[L++] = 11;     //17 bytes of payload
		
		buf[L++] = MY_MAC_0;
		buf[L++] = MY_MAC_1;
		buf[L++] = MY_MAC_2;
		buf[L++] = MY_MAC_3;
		buf[L++] = MY_MAC_4;
		buf[L++] = MY_MAC_5;
		
		buf[L++] = 2;		//flags (LE-only, limited discovery mode)
		buf[L++] = 0x01;
		buf[L++] = 0x05;
		
		buf[L++] = 7;
		buf[L++] = 0x08;
		buf[L++] = 'n';
		buf[L++] = 'R';
		buf[L++] = 'F';
		buf[L++] = ' ';
		buf[L++] = 'L';
		buf[L++] = 'E';
		
		buf[L++] = 0x55;	//CRC start value: 0x555555
		buf[L++] = 0x55;
		buf[L++] = 0x55;
		
		
		if(++ch == sizeof(chRf)) ch = 0;
		
		nrf_cmd(0x25, chRf[ch]);
		nrf_cmd(0x27, 0x6E);	//clear flags

		btLePacketEncode(buf, L, chLe[ch]);
		
		nrf_simplebyte(0xE2); //Clear RX Fifo
		nrf_simplebyte(0xE1); //Clear TX Fifo
	
		cbi(PORTB, PIN_nCS);
		spi_byte(0xA0);
		for(i = 0 ; i < L ; i++) spi_byte(buf[i]);
		sbi(PORTB, PIN_nCS);
	
		nrf_cmd(0x20, 0x12);	//tx on
		sbi(PORTB, PIN_CE);	 //do tx
		delay_ms(10);
		cbi(PORTB, PIN_CE);	(in preparation of switching to RX quickly)
	}
	
	
	return 0;
}

另外上传了类似功能的demo

"嗅探和解码 NRF24L01+和低功耗蓝牙"nrf24-btle-decoder.c_伪蓝牙-嵌入式文档类资源-CSDN下载

"国产2.4G芯片 XN297做蓝牙广播"XN297_TO_BLE.zip_xn297蓝牙广播包-嵌入式文档类资源-CSDN下载

  • 3
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dear_Wally

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值