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
#define
#define
#define
#define
#define

MY_MAC_0
MY_MAC_1
MY_MAC_2
MY_MAC_3
MY_MAC_4
MY_MAC_5

0xEF
0xFF
0xC0
0xAA
0x18
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
if(a
if(a
if(a
if(a
if(a
if(a
if(a

&
&
&
&
&
&
&
&

0x80)
0x40)
0x20)
0x10)
0x08)
0x04)
0x02)
0x01)

return v;
}

v
v
v
v
v
v
v
v

|=
|=
|=
|=
|=
|=
|=
|=

0x01;
0x02;
0x04;
0x08;
0x10;
0x20;
0x40;
0x80;

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,
nrf_cmd(0x21,
nrf_cmd(0x22,
nrf_cmd(0x23,
nrf_cmd(0x24,
nrf_cmd(0x26,
nrf_cmd(0x27,
nrf_cmd(0x3C,
nrf_cmd(0x3D,
nrf_cmd(0x31,
nrf_cmd(0x22,
buf[0]
buf[1]
buf[2]
buf[3]

=
=
=
=

0x12);
0x00);
0x00);
0x02);
0x00);
0x06);
0x3E);
0x00);
0x00);
32);
0x01);

//on, no crc, int on RX/TX done


//no auto-acknowledge
//no RX
//5-byte address
//no auto-retransmit
//1MBps at 0dBm
//clear various flags
//no dynamic payloads
//no features
//always RX 32 bytes
//RX on pipe 0

0x30;
swapbits(0x8E);
swapbits(0x89);
swapbits(0xBE);

//set addresses

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++]
buf[L++]
buf[L++]
buf[L++]
buf[L++]
buf[L++]

=
=
=
=
=
=

MY_MAC_0;
MY_MAC_1;
MY_MAC_2;
MY_MAC_3;
MY_MAC_4;
MY_MAC_5;

buf[L++] = 2;

//flags (LE-only, limited discovery

mode)
buf[L++] = 0x01;
buf[L++] = 0x05;
buf[L++]
buf[L++]
buf[L++]
buf[L++]
buf[L++]
buf[L++]
buf[L++]
buf[L++]

=
=
=
=
=
=
=
=

7;
0x08;
'n';
'R';
'F';
' ';
'L';
'E';

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

//CRC start value: 0x555555

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);
sbi(PORTB, PIN_CE);
delay_ms(10);
cbi(PORTB, PIN_CE);
quickly)
}

//tx on
//do tx
(in preparation of switching to RX

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

@Nik
have at it

for

android
G:

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-nRF24L01BLE


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
CSN
SCK
MOSI
MISO
and

->
->

initialize

Digital
Digital
->
->
->
RF24

radio(8,10)

pin
pin

in

8
10
ICSP9
ICSP10
ICSP11
my

sketch.

Hope this helps!

Nataraja
Thu, 27 Feb 2014 16:34:56 +0000
I'm
using
arduino
tried
below
codes
from
Sandeep
https://github.com/sandeepmistry/arduino-nRF24L01-BLE
https://github.com/floe/BTLE

&
&

nrf24l01+
Floe
.

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
0x20,0x21,0x22
etc
0x00,

dont

#define
#define

exists

void

replaced

PIN_CE
PIN_nCS

#define
#define
#define
#define
#define
#define

commands
...
with
0x01,0x02

8
10

//Output
//Output

MY_MAC_0
MY_MAC_1
MY_MAC_2
MY_MAC_3
MY_MAC_4
MY_MAC_5

btLeCrc(const

uint8_t*

uint8_t

data,

0xEF
0xFF
0xC0
0xAA
0x18
0x00

uint8_t

v,

len,

uint8_t*

t,

dst){
d;

while(len--){
d
for(v
t
dst[0]
if(dst[1]
dst[1]
if(dst[2]
dst[2]

if(t
dst[2]
dst[1]
}
}
}
}
uint8_t

=
=

0;
=

<

8;

v++,

dst[0]

>>=

>>

7;

<<=
&

0x80)

dst[0]

|=

dst[1]

|=

<<=
&

0x80)
<<=

!=

(d
^=
^=

swapbits(uint8_t

&

*data++;
1){

1;
1;
1;
1;
1;

1)){
0x5B;
0x06;

a){

uint8_t

if(a
if(a
if(a
if(a
if(a
if(a
if(a
if(a

&
&
&
&
&
&
&
&

0x80)
0x40)
0x20)
0x10)
0x08)
0x04)
0x02)
0x01)

0;

v
v
v
v
v
v
v
v

|=
|=
|=
|=
|=
|=
|=
|=

0x01;
0x02;
0x04;
0x08;
0x10;
0x20;
0x40;
0x80;

return
}
void

v;

btLeWhiten(uint8_t*

data,

uint8_t

len,

uint8_t

whitenCoeff){

uint8_t

m;

while(len--){
for(m

1;

if(whitenCoeff
whitenCoeff
(*data)
}
whitenCoeff
}
data++;
}
}

m;

<<=

1){

&

0x80){

^=
^=

0x11;
m;

<<=

1;

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

//
shiftOut(11,

Serial.println("SPI
13,

BYTE");
byte);

MSBFIRST,

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
{
digitalWrite(PIN_nCS,
spi_byte(cmd);
digitalWrite(PIN_nCS,
}
void

nrf_simplebyte(uint8_t
LOW);

//cbi(PORTB,

PIN_nCS);

HIGH);

//sbi(PORTB,

PIN_nCS);

nrf_manybytes(uint8_t*

digitalWrite(PIN_nCS,
do{

cmd)

data,

uint8_t

len){

LOW);

//

cbi(PORTB,

PIN_nCS);

HIGH);

//

sbi(PORTB,

PIN_nCS);

spi_byte(*data++);
}while(--len);
digitalWrite(PIN_nCS,
}

//void
//{
//
//
//
//
//
//
//
//}

fob_init

(void)

DDRA
DDRB
PORTA

(uint8_t)~(1<<5);
0b00000110;
0b10001111;
PIN_CE);
(1<<CS00);
(1<<SM1)|(1<<SE);
sei();

=
=
cbi(PORTB,

TCCR0B
MCUCR

void
//
set
pinMode(PIN_nCS,
pinMode(PIN_CE,
pinMode(11,
pinMode(13,

=
=

setup()
digital

the

pin

as

{
output:
OUTPUT);
OUTPUT);
OUTPUT);
OUTPUT);

Serial.begin(9600);
Serial.println("HELLO");
}
void
//{
static
static
uint8_t
uint8_t

loop()

const
const

//int

uint8_t
uint8_t
L,

i,

chRf[]
chLe[]
ch

main
=

26,80};
{37,38,39};
0;
buf[32];
fob_init();

//
//

buf[0]
buf[1]

{2,
=

//

nrf_cmd(0x00,
nrf_cmd(0x01,
nrf_cmd(0x02,
nrf_cmd(0x03,
nrf_cmd(0x04,
nrf_cmd(0x06,
nrf_cmd(0x07,
nrf_cmd(0x1C,
nrf_cmd(0x1D,
nrf_cmd(0x11,
nrf_cmd(0x02,

(void)

DDRA
PORTA

|=
|=

4;
4;

0x12);

//on,
no
crc,
int
on
RX/TX
done
0x00);
//no
auto-acknowledge
0x00);
//no
RX
0x02);
//5-byte
address
0x00);
//no
auto-retransmit
0x06);
//1MBps
at
0dBm
0x3E);
//clear
various
flags
0x00);
//no
dynamic
payloads
0x00);
//no
features
32);
//always
RX
32
bytes
0x01);
//RX
on
pipe
0

0x10;
=

//set

addresses
swapbits(0x8E);

buf[2]
buf[3]
buf[4]
nrf_manybytes(buf,
buf[0]
nrf_manybytes(buf,

=
=
=

swapbits(0x89);
swapbits(0xBE);
swapbits(0xD6);
5);
0x0A;
5);

while(1){
float
float

=
t
=

=
=

0x42;
0x11

//PDU
/*+
8*/

buf[L++]
buf[L++]
buf[L++]
buf[L++]
buf[L++]
buf[L++]
buf[L++]
buf[L++]
buf[L++]

0;

type,
+

given
address
10;
//17
bytes

=
=
=
=
=
=
=

2;

buf[L++]
buf[L++]

//flags

buf[L++]
buf[L++]
buf[L++]
buf[L++]
buf[L++]
buf[L++]

(LE-only,
=
=

limited

7;//
=

mode)
0x01;
0x05;

8;
0x08;
'n';
'R';
'F';
';
'L';
'E';

'

9;
0xff;
4);
4;
4);
4;

=
&h,
+=
&t,
+=

0x55;

random
payload

discovery

=
=

is
of

MY_MAC_0;
MY_MAC_1;
MY_MAC_2;
MY_MAC_3;
MY_MAC_4;
MY_MAC_5;

=
=
=

buf[L++]
buf[L++]
memcpy(&buf[L],
L
memcpy(&buf[L],
L

buf[L++]
buf[L++]

;
random();

L
buf[L++]
buf[L++]

random()

//CRC
=

start

value:

0x555555
0x55;

buf[L++]

//if(++ch

==

0x55;

sizeof(chRf))

nrf_cmd(0x05,
nrf_cmd(0x07,

0x7E);

//clear

btLePacketEncode(buf,

L,

nrf_simplebyte(0xE2);
nrf_simplebyte(0xE1);
digitalWrite(PIN_nCS,
spi_byte(0xA0);
for(i
=
0
digitalWrite(PIN_nCS,

ch

chRf[ch]);
flags
chLe[ch]);

//Clear
//Clear

0;

RX
TX

LOW);

//

i
<
HIGH);

L
//

Fifo
Fifo

cbi(PORTB,

PIN_nCS);

i++)
sbi(PORTB,

spi_byte(buf[i]);
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
and
how
is
everyone
I
mean
which
App
are
you

I'm

going
testing
using
on

wrong
it
Android

?
?
OS

Thanks :)

Nataraja G
Thu, 27 Feb 2014 16:39:34 +0000
inline
with
my
even this App is not able to read the broadcast messages from Arduino

comments

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
Regards,

have

an

interesting

project

to

discuss

with

you

Javier
Wed, 07 May 2014 02:15:55 +0000
Hello,
I
was
looking
at
http://hackaday.com/2014/02/16/nrf24l01-using-3-attiny85-pins/

this

post:

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
Have

you

made

any

Dmitry,
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
Matteo

you

very

much,

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