You are on page 1of 20

http://dmitry.gr/index.php?r=05.Projects&proj=15&proj=11.

%20Bluetooth%20LE%20fakery

Faking Bluetooth LE
Bluetooth LE is a new technology, introduced in the Bluetooth 4.0 spec. It has absolutely
nothing to do with bluetooth besides the name. Now that we have that out of the way, why is
it cool? Well, it was made for low power, and the design shows. Unlike real bluetooth that does
frequency hoppping on a precise schedule, regardless of anything, LE hops after some number
of packets are sent, and thus one does not need to be awake to keep a running clock to know
where to hop to next. In fact, LE allows a device to completely shut down its radio for large
periods of time while maintaining a connection. This makes it awesome for keyboards and
mice and all kinds of other such things. Another cool feature in LE is that devices can send
unsolicited broadcasts of small chunks of data. Unlike real bluetooth, scanning for devices in
the LE world can be passive - you just listen for advertisement packets on the right channels
and you hear all the advertisements.

The simple channel hopping behaviour of LE means that we can probably pretend to be
an LE device without the complex radio that normal bluetoth requires. The frequency is
2.4GHz, chanels are 1MHz apart, modulation is GFSK, datarate is 1MBps, preamble is
10101010 or 01010101 based on first data byte, and addressing is done using a 32-bit address.
Gee, don't we know a device that can do all that? Of course we do! The ever-popular Nordic
nRF24L01+. So let's look at the differences between what LE needs and what we have:

 CRC: BTLE uses a 24-bit CRC that is not something nRF24 can do. Luckily for us,
nRF24 allows us to disable the CRC, and then we can send our own and check
received ones manually. Unpleasant? Sure, but doable.
 Frequency Hopping: BTLE needs ut to be able to hop quickly - 150us or faster.
Sadly here nRF24 fails us. After every transmission it shuts down its PLL, and then
restarts it on every transmit or receive command. This takes 130us. We are left with
too little time to do anything here, sadly. Luckily this is not the major issue.
 Packet Length: BTLE packets are of various sizes, up to 39 bytes of payload in fact.
But the one packet we would really want to support is the CONNECT_REQ packet,
which is in fact 34 bytes of payload + 3 of CRC, meaning we'd need to receive 37
bytes if we want to check CRCs, and even if we were willing to throw caution to the
wind and ignore them, we need to receive 34. nRF24 will not handle packets over 32
bytes. Sadly the last two bytes are kind of important (hop size and a part of the
channel map). This is where we lose.

So... we cannot accept connections, but not all is lost... BTLE allows unsolicited
advertisements, so we can still do some cool things by making them broadcast data to anyone
who'll listen. Let's work out just how much data we can send... Out of our 32-byte budget: 3 go
into CRC, 2 go into the ADV_NONCONN_IND packet header, and 6 go into the MAC
address, leaving us with 21 bytes of payload. This payload, however, must have structure.
Assuming minimum required headers, we can get away with sending 19 bytes of data if we do
not want our device to broadcast a name. If we do want a name, we have 17 bytes to split
between the name (in UTF-8) and our data. And if we want to comply with the spec better and
broadcast device attributes, we'll have 14 bytes to split between name and data or 16 bytes of
pure data - not too bad.
Let's sort out the details then... first of all, BTLE and nRF24 send data bits in the air in
opposite order, so we'll have to reverse all our bits. Second, BTLE uses data whitening, and
nRF24 does not, so we'll need to do that by hand too. Lastly, there is the previously-mentioned
24-bit CRC. All LE broadcasts get sent to the same "address": 0x8E89BED6, also known as
"bed six." Of course, for us it'll be bit-reversed. BTLE applies CRC to the whole payload but
not the address. Whitening is applied to the payload and the CRC. Knowing this, thus, gives us
the ordering of events needed to assemble a complete working packet. Advertisement packets
are sent on 3 channels: 37, 38, and 39, which are 2.402HGz, 2.426GHz, and 2.480GHz
respectively. We'll alternate between them, spewing our broadcast everywhere methodically.

BTLE CRC is not too hard to implement in C, uses the initial value of 0x555555, and
looks something like this:

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;
}
}
}
}

The data whitening function is also not too complicated. It is a 7-bit linear-shift feedback
style and is initialized by the value that is equal to (channelNum << 1) + 1. The code goes
something like this:

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++;
}
}

The advertisement packet payload looks like this:

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
}

So if we lump all this together, we'll end up with a packet with the above header. We then
CRC it using the above CRC function. We then whiten it using the above whitening function.
After this we send it. Let's see... yup, it works. An iPad3 running BTLExplorer shows that our
device is visible and is a BTLE device. Cool! One caveat: if you do not broadcast a device
name, BTLExplorer will crash - this is their bug, so do not worry.

What is the payload data format? you may ask. Well, data is made of chunks, each of
which has a 2-byte header: length and type (in that order, length includes the length of the type
byte). Types you care about are:

 2: Flags - length will be 2 and the lone byte's value you want is 5 - this means a
single-mode (not BTLE/BT combo) device in limited discovery mode.
 8 or 9: Name - length is the length of the name in UTF-8 without NULL-termination.
8 is for "shortened name" and 9 is for "complete name."
 255: Custom data. This is where you can shove your custom data. On iPhone you'll
have complete access to this.

Why all this? Well, if you know a simpler way for an embedded project to communicate
data to an iPhone, let me know. WiFi is power hungry and messy. Real bluetooth is locked
down in iPhone, as are serial ports. This method works. If and when Android gets a BTLE API,
I am sure broadcast data will be available to you too - it just makes sense.

Future work: I just got some non-nordic 2.4GHz parts that support packets up to 64 bytes
and can keep their PPLs on, meaning that a full BTLE stack may be possible on them. I am
working on this as you read this.

All the code as well as the research that went into this and is published here is under this
license: you may use it in any way you please if and only if it is for non-commercial purposes,
you must provide a link to this page as well. Any commercial use must be discussed with me.

Full sample code, that will run on the nordic-fob from Sparkfun looks approximately like
this:

#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;X�//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;
}
Jacob
Mon, 25 Feb 2013 16:37:17 +0000
Excellent work on this! Saw it months ago and finally found a good project for this trick.

My favorite spec in the BLE specifications was the free space in the advertising data but all the
existing implementations use a very heavy setup function to set advertising data presumably
once.

I want to change advertising data based on sensor input, etc, and this 'bit banging' method seems
really powerful for that.

What AVR chip did you use for your code?

Thanks,
Jacob
Dmitry Grinberg
Mon, 25 Feb 2013 19:02:43 +0000
@Jacob: Glad you like it:)

I used an ATTiny24, specifically, this code runs unmodified on this board:


https://www.sparkfun.com/products/8602
Baza
Mon, 18 Mar 2013 18:08:16 +0000
Dmitry,

This looks really interesting. I really want to see how this pans out with the final hardware. Are
you close to getting this up and running?

You say the nRF24 will not handle packets over 32 bytes, but how is that device normally used
if it cannot accept connections?

Cheers,
Baza.

PS: Great site.


Dmitry Grinberg
Thu, 21 Mar 2013 23:15:41 +0000
@Baza: to send data in adv packets

i have another device that can do better packet length that i am playing with (but i am busy so
it is not moving quickly)
Hugh O'Brien
Sat, 20 Apr 2013 23:54:04 +0000
This is excellent, and I thought bit-banging the HCI was tricky.
Vladimir
Sat, 21 Sep 2013 12:18:38 +0000
Hi! This looks really good.
Can we simultaneously receive the data of the same type? What's the best way of doing that?
phonytony
Sat, 21 Sep 2013 14:19:37 +0000
Any progress on the alternative transceiver?
Dmitry Grinberg
Sat, 21 Sep 2013 15:13:59 +0000
@pgonytony: the chip was A7125:
http://www.elecfreaks.com/wiki/index.php?title=2.4G_Wireless_easy_radio_A7125
Theo
Sat, 21 Sep 2013 18:30:01 +0000
You say there isnt an api for android, but isnt this it
http://developer.android.com/guide/topics/connectivity/bluetooth-le.html and there seems to
be a similar app on the play store: ble explorer. Ive had a go trying your code out but im not
getting anything with that app, however I did have to rewrite a fair piece of it to run it on a
different microcontroller so im not sure if its my modifications or android.
Nik G
Sun, 22 Sep 2013 00:01:14 +0000
Mind if I wrap your code up in an Arduino library and post it? I'll make sure and give you full
credit.
Gr0b
Mon, 23 Sep 2013 03:58:59 +0000
This is awesome, I have been using these little Radios for years now and was looking to start
using BLE radios as well but I can add BLE without changing my hardware.

an Arduino version of this would just be a cherry on the cake.

Dmitry Grinberg
Mon, 23 Sep 2013 16:34:01 +0000
@Theo:
at time of writing there was no public LE API for android

@Nik G:
have at it
Ralph Doncaster
Mon, 23 Sep 2013 19:14:30 +0000
I was confused by the comment that 5-byte addresses are used, when it is actually 4.
nrf_cmd(0x23, 0x02); //5-byte address
0x02 sets 4-byte addresses, and 0x03 would set a 5-byte address.
James
Thu, 26 Sep 2013 03:11:20 +0000
i just wanted to confirm which way the data goes? From the module to phone, or from phone
to module? or both?
Paul
Sun, 29 Sep 2013 21:17:45 +0000
The A7125 doesn't seem to support GFSK.

There are some other transceivers from that manufacturer that could work, such as the A7105
Mark Schwab
Sun, 06 Oct 2013 15:58:48 +0000
Thank You

I'm developing on the CC2541 and was struggling a little with the BLE packet structure. Your
code helped a lot. The spec had me mixed up with endiannesss
floe
Wed, 09 Oct 2013 10:54:10 +0000
Very awesome. Looking forward to trying this on my Arduino. BTW, what license is your code
published under?
Guan Yang
Sat, 12 Oct 2013 14:51:23 +0000
Do you think the Analog Devices ADF7242 could do this too?
Moustafa Berg
Sat, 12 Oct 2013 17:44:07 +0000
Great work !, but i had one comment on your website's styling, the background makes the text
unreadable !, so if you can try to find a workaround for that, it would be much clearer for people
to read, my eyes literally hurt trying to read through the whole post !

besides that, smart work !


Alex
Sun, 13 Oct 2013 15:36:36 +0000
Hi
1. Could you please explain why you use following addresses to configure the module:
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
instead of:
nrf_cmd(0x00, 0x12); //on, no crc, int on RX/TX done
nrf_cmd(0x01, 0x00); //no auto-acknowledge
nrf_cmd(0x02, 0x00); //no RX
nrf_cmd(0x03, 0x02); //5-byte address
...
2. These are "Name" bits, aren't they?
buf[L++] = 'n';
buf[L++] = 'R';
buf[L++] = 'F';
..
But where are "Data" bits then? The data that we want to transmit
3. What are these? What addresses?
buf[0] = 0x30; //set addresses
buf[1] = swapbits(0x8E);
buf[2] = swapbits(0x89);
buf[3] = swapbits(0xBE);
buf[4] = swapbits(0xD6);
4. Can I use this module as a basic device to transmit data TO Android phone(4.3)? I am
planning on connecting my phne to this module. Then, I will click on buttons to send certain
commands to my phone. Commands will be deciphered inside the app. Is that possible with
this module?
Sandeep
Sun, 27 Oct 2013 15:53:26 +0000
Hi Dmitry,

Your project inspired me to start this: https://github.com/sandeepmistry/arduino-nRF24L01-


BLE - using an Arduino + DHT22 + NRF

Did you have any luck using a transceiver with a 64 byte TX buffer? I was thinking of creating
a iBeacon using your technique, but 5 more bytes are needed. Here is the format:
https://github.com/sandeepmistry/node-bleacon#ibeacon-advertisement-format

Thanks.
floe
Tue, 05 Nov 2013 12:42:27 +0000
I've wrapped/rewritten Dmitry's code into an Arduino library: https://github.com/floe/BTLE

Feedback welcome!
wiktor
Sat, 09 Nov 2013 17:04:27 +0000
nice work Dimitry!

floe, i was about to do same thing before i saw your comment. thanks for the library, it saved
me a lot of work, however i cant manage to get the data on android - it only sees the name.

did you have any luck with receiving data on android?


the receive example works fine so i assume the problem lies on android side.
any help appreciated :)
Marcin
Fri, 06 Dec 2013 20:40:49 +0000
Hello! Thanks for your work a lot!. It really helps. I wanted to ask you that same question as
wiktor asked. Is it possible to send data via nRF24 to android mobile phone. Im really interested
in this idea because this module has really low power consumprion comparing to bluetooth.
And your idea is all what i need. Can you answer please? is this way of comunication possible?
Giuliano
Wed, 18 Dec 2013 12:28:40 +0000
Hi, this is really interesting. Is it possible to send data from smartphone to nrf24l01+ using
BLE?
George
Tue, 31 Dec 2013 23:02:50 +0000
Has anyone actually confirmed this working with an AVR and nRF24L01+ yet? If so could
you post a link with your hardware setup and code. The sample code did not compile for me
under avr gcc for Attiny24.
George
Thu, 02 Jan 2014 10:23:06 +0000
I can answer my own question now, I have run a successful demo on an Arduino Uno using
Sundeep's library at https://github.com/sandeepmistry/arduino-nRF24L01-BLE
Thanks to all.
Collider
Sat, 11 Jan 2014 01:40:05 +0000
Hi all!

Can a Bluetooth 2.1+EDR module wake a Bluetooth 4.0 Low Energy+Dual Mode (BLE+EDR)
module up, if that is in sleeping mode, if it tries to connect to the BLE+EDR? And can the
2.1+EDR module see the name of BLE+EDR in sleep mode at all?

BLE works together with an other BLE device only, but a BLE+EDR Dual Mode works with
Classic BT (2.1+EDR) too. My question would be: Could a Classic BT wake up a BLE+EDR
Dual Mode device if it is in sleeping mode by trying to connect to it?
CyberExplorer
Tue, 21 Jan 2014 20:17:19 +0000
Thanks for the information. It helped me with my NRF / BTLE sniffing project
http://blog.cyberexplorer.me/2014/01/sniffing-and-decoding-nrf24l01-and.html
floe
Thu, 30 Jan 2014 07:44:39 +0000
Just a quick note: I've found that Android can be very, very picky about the packet timing. E.g.
when running identical code from my library on an ATmega328 and a 32u4, only the older one
(328) is picked up by Android devices. An iPhone happily shows both devices... not yet sure
what the root cause is.

Dmitry, are you involved with Android? Any ideas?


UgoRaffaele
Fri, 21 Feb 2014 09:19:22 +0000
@floe

I ran the code on an Arduino Leonardo and my Android device picks packages without any
problems.

Anyway, I had to change pins when connecting my NRF24 to Leonardo as stated below:

CE -> Digital pin 8


CSN -> Digital pin 10
SCK -> ICSP9
MOSI -> ICSP10
MISO -> ICSP11

and initialize RF24 = radio(8,10) in my sketch.

Hope this helps!


Nataraja
Thu, 27 Feb 2014 16:34:56 +0000
I'm using arduino & nrf24l01+
tried below codes from Sandeep & Floe .
https://github.com/sandeepmistry/arduino-nRF24L01-BLE
https://github.com/floe/BTLE
strangely both of the them are not able broadcast message to my Google Nexus 5
running the below app
https://play.google.com/store/apps/details?id=com.propagation.keyfob.demo&hl=en

nrf commands
0x20,0x21,0x22 ...
etc dont exists replaced with
0x00, 0x01,0x02

#define PIN_CE 8 //Output


#define PIN_nCS 10 //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

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_t 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{
// digitalWrite(11, LOW); //PORTB &=~ (uint8_t)(1 << 6);
// if(byte & 0x80) digitalWrite(11, HIGH); //PORTB |= (uint8_t)(1 << 6);
// digitalWrite(13, HIGH); //CLK |= (uint8_t)(1 << 4);
// byte <<= 1;
//// if(PINA & (uint8_t)32) byte++;
//// delay(10);
// digitalWrite(13, LOW);//CLK &=~ (uint8_t)(1 << 4);
//
// }while(--i);

// Serial.println("SPI BYTE");
shiftOut(11, 13, MSBFIRST, byte);

return byte;
}

void nrf_cmd(uint8_t cmd, uint8_t data)


{
digitalWrite(PIN_nCS, LOW); //cbi(PORTB, PIN_nCS);
spi_byte(cmd);
spi_byte(data);
digitalWrite(PIN_nCS, HIGH); // sbi(PORTB, PIN_nCS); //Deselect chip
}

void nrf_simplebyte(uint8_t cmd)


{
digitalWrite(PIN_nCS, LOW); //cbi(PORTB, PIN_nCS);
spi_byte(cmd);
digitalWrite(PIN_nCS, HIGH); //sbi(PORTB, PIN_nCS);
}

void nrf_manybytes(uint8_t* data, uint8_t len){

digitalWrite(PIN_nCS, LOW); // cbi(PORTB, PIN_nCS);


do{

spi_byte(*data++);

}while(--len);
digitalWrite(PIN_nCS, HIGH); // 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();
//}

void setup() {
// set the digital pin as output:
pinMode(PIN_nCS, OUTPUT);
pinMode(PIN_CE, OUTPUT);
pinMode(11, OUTPUT);
pinMode(13, OUTPUT);

Serial.begin(9600);
Serial.println("HELLO");

void loop() { //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(0x00, 0x12); //on, no crc, int on RX/TX done


nrf_cmd(0x01, 0x00); //no auto-acknowledge
nrf_cmd(0x02, 0x00); //no RX
nrf_cmd(0x03, 0x02); //5-byte address
nrf_cmd(0x04, 0x00); //no auto-retransmit
nrf_cmd(0x06, 0x06); //1MBps at 0dBm
nrf_cmd(0x07, 0x3E); //clear various flags
nrf_cmd(0x1C, 0x00); //no dynamic payloads
nrf_cmd(0x1D, 0x00); //no features
nrf_cmd(0x11, 32); //always RX 32 bytes
nrf_cmd(0x02, 0x01); //RX on pipe 0

buf[0] = 0x10; //set addresses


buf[1] = swapbits(0x8E);
buf[2] = swapbits(0x89);
buf[3] = swapbits(0xBE);
buf[4] = swapbits(0xD6);
nrf_manybytes(buf, 5);
buf[0] = 0x0A;
nrf_manybytes(buf, 5);

while(1){

float h = random() ;
float t = random();

L = 0;

buf[L++] = 0x42; //PDU type, given address is random


buf[L++] = 0x11 /*+ 8*/ + 10; //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;// + 8;
buf[L++] = 0x08;

buf[L++] = 'n';
buf[L++] = 'R';
buf[L++] = 'F';
buf[L++] = ' ';
buf[L++] = 'L';
buf[L++] = 'E';

buf[L++] = 9;
buf[L++] = 0xff;
memcpy(&buf[L], &h, 4);
L += 4;
memcpy(&buf[L], &t, 4);
L += 4;

buf[L++] = 0x55; //CRC start value: 0x555555


buf[L++] = 0x55;
buf[L++] = 0x55;

//if(++ch == sizeof(chRf)) ch = 0;

nrf_cmd(0x05, chRf[ch]);
nrf_cmd(0x07, 0x7E); //clear flags

btLePacketEncode(buf, L, chLe[ch]);

nrf_simplebyte(0xE2); //Clear RX Fifo


nrf_simplebyte(0xE1); //Clear TX Fifo

digitalWrite(PIN_nCS, LOW); // cbi(PORTB, PIN_nCS);


spi_byte(0xA0);
for(i = 0 ; i < L ; i++) spi_byte(buf[i]);
digitalWrite(PIN_nCS, HIGH); // sbi(PORTB, PIN_nCS);

nrf_cmd(0x00, 0x12); //tx on


digitalWrite(PIN_CE, HIGH); // sbi(PORTB, PIN_CE); //do tx
delay(10); //delay_ms(10);
digitalWrite(PIN_CE, LOW); // cbi(PORTB, PIN_CE); (in preparation of switching to RX
quickly)
}

// return 0;
}
Nataraja
Thu, 27 Feb 2014 16:37:36 +0000
Can anyone tell me where I'm going wrong ?
and how is everyone testing it ?
I mean which App are you using on Android OS

Thanks :)
Nataraja G
Thu, 27 Feb 2014 16:39:34 +0000
inline with my comments
even this App is not able to read the broadcast messages from Arduino
eric
Fri, 28 Mar 2014 23:05:38 +0000
Hi this is great! Can you email me about a semi commercial use of this? I'd like to transmit
sensor data in the payload as well. Thanks!
Andre Luiz
Fri, 02 May 2014 04:52:00 +0000
Hey Dima,

Nice job there I wanted to have a skype talk with you, I am a CTO of a Ecosystem mobile
company I have an interesting project to discuss with you
Regards,
Javier
Wed, 07 May 2014 02:15:55 +0000
Hello,

I was looking at this post:


http://hackaday.com/2014/02/16/nrf24l01-using-3-attiny85-pins/

and yours and I was wondering if it's possible to send and receive BLE between an Arduino
and an Attiny85. Which library should I use? Floe's BLTE library with the arduino-nrf24l01
(https://github.com/stanleyseow/arduino-nrf24l01) or just one should work?

Thanks in advance
jamesk
Wed, 04 Mar 2015 21:44:48 +0000
anyone know if this still works? I can't see my NRF24L01 on an android 4.4 phone. Tried all
variations of code mentioned here in comments, can't tell if its an android limitation.
John S.
Mon, 30 Mar 2015 21:14:23 +0000
It's awesome to be able to do this with any radio, but isn't it a bit too much work? Can you hack
BLE like this to do iBeacons? That's what I'm trying to do with the information <a
href="http://www.argenox.com/bluetooth-low-energy-ble-v4-0-development/library/">here
</a>
saleem
Sun, 19 Apr 2015 00:46:20 +0000
may be...cc2550 or cc3000 work good for integrating full BT stack
A. Misbah
Fri, 28 Aug 2015 19:35:57 +0000
Hello Dmitry,

Have you made any progress with the A7125 Module??

Thanks.
John Doe
Sat, 27 Feb 2016 15:10:25 +0000
I know this is years later,but i live in a remote part of the world and still can't get access to a
beacon.This was exactly what i needed.You're brilliant to have thought of it so long ago:)
Thanks to you,i now have a solution.
Divyansh Lohia
Wed, 15 Jun 2016 08:10:05 +0000
with reference to blog. i had a query where is the actual data that we want to transmit.
Tulio
Thu, 23 Jun 2016 13:31:21 +0000
Excellent postage!!! How can I receive Bluetooth Packets with the NRF24L01?
Matteo
Wed, 27 Jul 2016 13:02:31 +0000
Excellent work!! :)

But Can I ask a question, How can nrf24l01 send data packages to my smartphone (My goal is
that my mobile phone can detect this beacon and there should be on the display "This is a
Ibeacon device",

Thank you very much,


Matteo
Dan Mc
Wed, 03 Aug 2016 19:08:19 +0000
Hi Dimitri
I'm building a prototype sensor network, and I'd like to use your code. You ask to chat for
commercial uses, but I see no way to reach you.

Dan
Dedy Yasriady
Mon, 29 Aug 2016 03:46:03 +0000
Hi Dmitry,

Do you have any idea to do bit-banging with LT8900 module?


This module much smaller and less power consumption, so powering with coin battery will
last longer ...

Rgrds/Ddy

You might also like