Original library sources.
This commit is contained in:
parent
87830b42e3
commit
9e41194f84
256
BTLE.cpp
Normal file
256
BTLE.cpp
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Florian Echtler <floe@butterbrot.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* version 3 as published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <BTLE.h>
|
||||||
|
|
||||||
|
|
||||||
|
const uint8_t channel[3] = {37,38,39}; // logical BTLE channel number (37-39)
|
||||||
|
const uint8_t frequency[3] = { 2,26,80}; // physical frequency (2400+x MHz)
|
||||||
|
|
||||||
|
|
||||||
|
// This is a rather convoluted hack to extract the month number from the build date in
|
||||||
|
// the __DATE__ macro using a small hash function + lookup table. Since all inputs are
|
||||||
|
// const, this can be fully resolved by the compiler and saves over 200 bytes of code.
|
||||||
|
#define month(m) month_lookup[ (( ((( (m[0] % 24) * 13) + m[1]) % 24) * 13) + m[2]) % 24 ]
|
||||||
|
const uint8_t month_lookup[24] = { 0,6,0,4,0,1,0,17,0,8,0,0,3,0,0,0,18,2,16,5,9,0,1,7 };
|
||||||
|
|
||||||
|
|
||||||
|
// change buffer contents to "wire bit order"
|
||||||
|
void BTLE::swapbuf( uint8_t len ) {
|
||||||
|
|
||||||
|
uint8_t* buf = (uint8_t*)&buffer;
|
||||||
|
|
||||||
|
while (len--) {
|
||||||
|
|
||||||
|
uint8_t a = *buf;
|
||||||
|
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;
|
||||||
|
|
||||||
|
*(buf++) = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
BTLE::BTLE( RF24* _radio ):
|
||||||
|
radio(_radio),
|
||||||
|
current(0)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
// Simple converter from arduino float to a nRF_Float.
|
||||||
|
// Supports values from -167772 to +167772, with two decimal places.
|
||||||
|
nRF_Float
|
||||||
|
BTLE::to_nRF_Float(float t) {
|
||||||
|
int32_t ret;
|
||||||
|
int32_t exponent = -2;
|
||||||
|
ret = ((exponent & 0xff) << 24) | (((int32_t)(t * 100)) & 0xffffff);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set BTLE-compatible radio parameters
|
||||||
|
void BTLE::begin( const char* _name ) {
|
||||||
|
|
||||||
|
name = _name;
|
||||||
|
radio->begin();
|
||||||
|
|
||||||
|
// set standard parameters
|
||||||
|
radio->setAutoAck(false);
|
||||||
|
radio->setDataRate(RF24_1MBPS);
|
||||||
|
radio->disableCRC();
|
||||||
|
radio->setChannel( frequency[current] );
|
||||||
|
radio->setRetries(0,0);
|
||||||
|
radio->setPALevel(RF24_PA_MAX);
|
||||||
|
|
||||||
|
// set advertisement address: 0x8E89BED6 (bit-reversed -> 0x6B7D9171)
|
||||||
|
radio->setAddressSize(4);
|
||||||
|
radio->openReadingPipe(0,0x6B7D9171);
|
||||||
|
radio->openWritingPipe( 0x6B7D9171);
|
||||||
|
|
||||||
|
radio->powerUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the current channel (from 37 to 39)
|
||||||
|
void BTLE::setChannel( uint8_t num ) {
|
||||||
|
current = min(2,max(0,num-37));
|
||||||
|
radio->setChannel( frequency[current] );
|
||||||
|
}
|
||||||
|
|
||||||
|
// hop to the next channel
|
||||||
|
void BTLE::hopChannel() {
|
||||||
|
current++;
|
||||||
|
if (current >= sizeof(channel)) current = 0;
|
||||||
|
radio->setChannel( frequency[current] );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Broadcast an advertisement packet with optional payload
|
||||||
|
// Data type will be 0xFF (Manufacturer Specific Data)
|
||||||
|
bool BTLE::advertise( void* buf, uint8_t buflen ) {
|
||||||
|
return advertise(0xFF, buf, buflen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Broadcast an advertisement packet with a specific data type
|
||||||
|
// Standardized data types can be seen here:
|
||||||
|
// https://www.bluetooth.org/en-us/specification/assigned-numbers/generic-access-profile
|
||||||
|
bool BTLE::advertise( uint8_t data_type, void* buf, uint8_t buflen ) {
|
||||||
|
// name & total payload size
|
||||||
|
uint8_t namelen = strlen(name);
|
||||||
|
uint8_t pls = 0;
|
||||||
|
|
||||||
|
// insert pseudo-random MAC address
|
||||||
|
buffer.mac[0] = ((__TIME__[6]-0x30) << 4) | (__TIME__[7]-0x30);
|
||||||
|
buffer.mac[1] = ((__TIME__[3]-0x30) << 4) | (__TIME__[4]-0x30);
|
||||||
|
buffer.mac[2] = ((__TIME__[0]-0x30) << 4) | (__TIME__[1]-0x30);
|
||||||
|
buffer.mac[3] = ((__DATE__[4]-0x30) << 4) | (__DATE__[5]-0x30);
|
||||||
|
buffer.mac[4] = month(__DATE__);
|
||||||
|
buffer.mac[5] = ((__DATE__[9]-0x30) << 4) | (__DATE__[10]-0x30);
|
||||||
|
|
||||||
|
// add device descriptor chunk
|
||||||
|
chunk(buffer,pls)->size = 0x02; // chunk size: 2
|
||||||
|
chunk(buffer,pls)->type = 0x01; // chunk type: device flags
|
||||||
|
chunk(buffer,pls)->data[0]= 0x05; // flags: LE-only, limited discovery mode
|
||||||
|
pls += 3;
|
||||||
|
|
||||||
|
// add "complete name" chunk
|
||||||
|
chunk(buffer,pls)->size = namelen+1; // chunk size
|
||||||
|
chunk(buffer,pls)->type = 0x09; // chunk type
|
||||||
|
for (uint8_t i = 0; i < namelen; i++)
|
||||||
|
chunk(buffer,pls)->data[i] = name[i];
|
||||||
|
pls += namelen+2;
|
||||||
|
|
||||||
|
// add custom data, if applicable
|
||||||
|
if (buflen > 0) {
|
||||||
|
chunk(buffer,pls)->size = buflen+1; // chunk size
|
||||||
|
chunk(buffer,pls)->type = data_type; // chunk type
|
||||||
|
for (uint8_t i = 0; i < buflen; i++)
|
||||||
|
chunk(buffer,pls)->data[i] = ((uint8_t*)buf)[i];
|
||||||
|
pls += buflen+2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// total payload size must be 21 bytes or less
|
||||||
|
if (pls > 21)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// assemble header
|
||||||
|
buffer.pdu_type = 0x42; // PDU type: ADV_NONCONN_IND, TX address is random
|
||||||
|
buffer.pl_size = pls+6; // set final payload size in header incl. MAC
|
||||||
|
|
||||||
|
// calculate CRC over header+MAC+payload, append after payload
|
||||||
|
uint8_t* outbuf = (uint8_t*)&buffer;
|
||||||
|
crc( pls+8, outbuf+pls+8);
|
||||||
|
|
||||||
|
// whiten header+MAC+payload+CRC, swap bit order
|
||||||
|
whiten( pls+11 );
|
||||||
|
swapbuf( pls+11 );
|
||||||
|
|
||||||
|
// flush buffers and send
|
||||||
|
radio->stopListening();
|
||||||
|
radio->write( outbuf, pls+11 );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// listen for advertisement packets
|
||||||
|
bool BTLE::listen(int timeout) {
|
||||||
|
|
||||||
|
radio->startListening();
|
||||||
|
delay(timeout);
|
||||||
|
|
||||||
|
if (!radio->available())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool done = false;
|
||||||
|
uint8_t total_size = 0;
|
||||||
|
uint8_t* inbuf = (uint8_t*)&buffer;
|
||||||
|
|
||||||
|
while (!done) {
|
||||||
|
|
||||||
|
// fetch the payload, and check if there are more left
|
||||||
|
done = radio->read( inbuf, sizeof(buffer) );
|
||||||
|
|
||||||
|
// decode: swap bit order, un-whiten
|
||||||
|
swapbuf( sizeof(buffer) );
|
||||||
|
whiten( sizeof(buffer) );
|
||||||
|
|
||||||
|
// size is w/o header+CRC -> add 2 bytes header
|
||||||
|
total_size = inbuf[1]+2;
|
||||||
|
uint8_t in_crc[3];
|
||||||
|
|
||||||
|
// calculate & compare CRC
|
||||||
|
crc( total_size, in_crc );
|
||||||
|
for (uint8_t i = 0; i < 3; i++)
|
||||||
|
if (inbuf[total_size+i] != in_crc[i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// see BT Core Spec 4.0, Section 6.B.3.2
|
||||||
|
void BTLE::whiten( uint8_t len ) {
|
||||||
|
|
||||||
|
uint8_t* buf = (uint8_t*)&buffer;
|
||||||
|
|
||||||
|
// initialize LFSR with current channel, set bit 6
|
||||||
|
uint8_t lfsr = channel[current] | 0x40;
|
||||||
|
|
||||||
|
while (len--) {
|
||||||
|
uint8_t res = 0;
|
||||||
|
// LFSR in "wire bit order"
|
||||||
|
for (uint8_t i = 1; i; i <<= 1) {
|
||||||
|
if (lfsr & 0x01) {
|
||||||
|
lfsr ^= 0x88;
|
||||||
|
res |= i;
|
||||||
|
}
|
||||||
|
lfsr >>= 1;
|
||||||
|
}
|
||||||
|
*(buf++) ^= res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// see BT Core Spec 4.0, Section 6.B.3.1.1
|
||||||
|
void BTLE::crc( uint8_t len, uint8_t* dst ) {
|
||||||
|
|
||||||
|
uint8_t* buf = (uint8_t*)&buffer;
|
||||||
|
|
||||||
|
// initialize 24-bit shift register in "wire bit order"
|
||||||
|
// dst[0] = bits 23-16, dst[1] = bits 15-8, dst[2] = bits 7-0
|
||||||
|
dst[0] = 0xAA;
|
||||||
|
dst[1] = 0xAA;
|
||||||
|
dst[2] = 0xAA;
|
||||||
|
|
||||||
|
while (len--) {
|
||||||
|
|
||||||
|
uint8_t d = *(buf++);
|
||||||
|
|
||||||
|
for (uint8_t i = 1; i; i <<= 1, d >>= 1) {
|
||||||
|
|
||||||
|
// save bit 23 (highest-value), left-shift the entire register by one
|
||||||
|
uint8_t t = dst[0] & 0x01; dst[0] >>= 1;
|
||||||
|
if (dst[1] & 0x01) dst[0] |= 0x80; dst[1] >>= 1;
|
||||||
|
if (dst[2] & 0x01) dst[1] |= 0x80; dst[2] >>= 1;
|
||||||
|
|
||||||
|
// if the bit just shifted out (former bit 23) and the incoming data
|
||||||
|
// bit are not equal (i.e. bit_out ^ bit_in == 1) => toggle tap bits
|
||||||
|
if (t != (d & 1)) {
|
||||||
|
// toggle register tap bits (=XOR with 1) according to CRC polynom
|
||||||
|
dst[2] ^= 0xDA; // 0b11011010 inv. = 0b01011011 ^= x^6+x^4+x^3+x+1
|
||||||
|
dst[1] ^= 0x60; // 0b01100000 inv. = 0b00000110 ^= x^10+x^9
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
95
BTLE.h
Normal file
95
BTLE.h
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Florian Echtler <floe@butterbrot.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* version 3 as published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _BTLE_H_
|
||||||
|
#define _BTLE_H_
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include <RF24.h>
|
||||||
|
|
||||||
|
// float as used on the nRF8001 and nRF51822 platforms
|
||||||
|
// and on the "nRF Master Control Panel" and "nRF Temp 2.0" apps.
|
||||||
|
// This float representation has the first 8 bits as a base-10 exponent
|
||||||
|
// and the last 24 bits as the mantissa.
|
||||||
|
typedef int32_t nRF_Float;
|
||||||
|
|
||||||
|
// Service UUIDs used on the nRF8001 and nRF51822 platforms
|
||||||
|
#define NRF_TEMPERATURE_SERVICE_UUID 0x1809
|
||||||
|
#define NRF_BATTERY_SERVICE_UUID 0x180F
|
||||||
|
#define NRF_DEVICE_INFORMATION_SERVICE_UUID 0x180A
|
||||||
|
|
||||||
|
// helper struct for sending temperature as BT service data
|
||||||
|
struct nrf_service_data {
|
||||||
|
int16_t service_uuid;
|
||||||
|
nRF_Float value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// advertisement PDU
|
||||||
|
struct btle_adv_pdu {
|
||||||
|
|
||||||
|
// packet header
|
||||||
|
uint8_t pdu_type; // PDU type
|
||||||
|
uint8_t pl_size; // payload size
|
||||||
|
|
||||||
|
// MAC address
|
||||||
|
uint8_t mac[6];
|
||||||
|
|
||||||
|
// payload (including 3 bytes for CRC)
|
||||||
|
uint8_t payload[24];
|
||||||
|
};
|
||||||
|
|
||||||
|
// payload chunk in advertisement PDU payload
|
||||||
|
struct btle_pdu_chunk {
|
||||||
|
uint8_t size;
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t data[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// helper macro to access chunk at specific offset
|
||||||
|
#define chunk(x,y) ((btle_pdu_chunk*)(x.payload+y))
|
||||||
|
|
||||||
|
|
||||||
|
class BTLE {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
BTLE( RF24* _radio );
|
||||||
|
|
||||||
|
// convert an arduino float to a nRF_Float
|
||||||
|
static nRF_Float to_nRF_Float(float t);
|
||||||
|
|
||||||
|
void begin( const char* _name ); // set BTLE-compatible radio parameters & name
|
||||||
|
|
||||||
|
void setChannel( uint8_t num ); // set the current channel (from 36 to 38)
|
||||||
|
void hopChannel(); // hop to the next channel
|
||||||
|
|
||||||
|
// Broadcast an advertisement packet with a specific data type
|
||||||
|
// Standardized data types can be seen here:
|
||||||
|
// https://www.bluetooth.org/en-us/specification/assigned-numbers/generic-access-profile
|
||||||
|
bool advertise( uint8_t data_type, void* buf, uint8_t len );
|
||||||
|
|
||||||
|
// Broadcast an advertisement packet with optional payload
|
||||||
|
// Data type will be 0xFF (Manufacturer Specific Data)
|
||||||
|
bool advertise( void* buf, uint8_t len );
|
||||||
|
bool listen( int timeout = 100 ); // listen for advertisement packets (if true: result = buffer)
|
||||||
|
|
||||||
|
btle_adv_pdu buffer; // buffer for received BTLE packet (also used for outgoing!)
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void whiten( uint8_t len );
|
||||||
|
void swapbuf( uint8_t len );
|
||||||
|
void crc( uint8_t len, uint8_t* dst );
|
||||||
|
|
||||||
|
RF24* radio; // pointer to the RF24 object managing the radio
|
||||||
|
uint8_t current; // current channel index
|
||||||
|
const char* name; // name of local device
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _BTLE_H_
|
||||||
|
|
16
README.md
16
README.md
@ -1,3 +1,15 @@
|
|||||||
# BTLE
|
BTLE
|
||||||
|
====
|
||||||
|
|
||||||
Bluetoot LE library for the NRF24L01+
|
This is a fork of floe's BTLE library; it has some improvements to make it able
|
||||||
|
to mimick nRF8001 and nRF51822 beacon packets.
|
||||||
|
More info on http://simonebaracchi.eu/posts/temperature-beacon/ .
|
||||||
|
|
||||||
|
Arduino library for basic Bluetooth Low Energy support using the nRF24L01+
|
||||||
|
(basic support = sending & receiving on the advertising broadcast channel)
|
||||||
|
|
||||||
|
You will also need to install the RF24 library from https://github.com/floe/RF24
|
||||||
|
|
||||||
|
Note: the BTLE class and the examples are licensed under GPLv3. However, the
|
||||||
|
helper functions in btle.inc are (C) 2012 by Dmitry Grinberg under a separate
|
||||||
|
license (see file for details).
|
||||||
|
38
bluez_adv.sh
Executable file
38
bluez_adv.sh
Executable file
@ -0,0 +1,38 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# bring up the host controller
|
||||||
|
sudo hciconfig hci0 up
|
||||||
|
|
||||||
|
# enable non-connectable undirected advertisements (only works with recent hciconfig)
|
||||||
|
#sudo hciconfig hci0 leadv 3
|
||||||
|
|
||||||
|
# custom hci commands take two parameters:
|
||||||
|
|
||||||
|
# 0x08 opcode group (LE)
|
||||||
|
# 0x0008 opcode command (set LE adv data)
|
||||||
|
|
||||||
|
# set random device address
|
||||||
|
sudo hcitool -i hci0 cmd 0x08 0x0005 12 34 56 78 9A BC
|
||||||
|
|
||||||
|
# set advertising parameters
|
||||||
|
|
||||||
|
# 00 08 00 08 min/max adv. interval
|
||||||
|
# 03 non-connectable undirected advertising
|
||||||
|
# 01 own address is random (see previous command)
|
||||||
|
# 00 target address is public (not used for undirected advertising)
|
||||||
|
# 00 00 00 ... target address (not used for undirected advertising)
|
||||||
|
# 07 adv. channel map (enable all)
|
||||||
|
# 00 filter policy (allow any)
|
||||||
|
sudo hcitool -i hci0 cmd 0x08 0x0006 00 08 00 08 03 01 00 00 00 00 00 00 00 07 00
|
||||||
|
|
||||||
|
# enable advertising (00 = disable)
|
||||||
|
sudo hcitool -i hci0 cmd 0x08 0x000A 01
|
||||||
|
|
||||||
|
# set advertisement data (_after_ advertising is enabled)
|
||||||
|
|
||||||
|
# 0e adv data length (should be at most 0x15 for compatibility with NRF24L01+)
|
||||||
|
# 02 01 05 flags (LE-only device, non-connectable)
|
||||||
|
# 07 09 ... name
|
||||||
|
# 02 ff fe custom data (02 length, ff type, fe data)
|
||||||
|
# 00 00 ... padding (to 32 bytes including length)
|
||||||
|
sudo hcitool -i hci0 cmd 0x08 0x0008 0e 02 01 05 07 09 66 6f 6f 62 61 72 02 ff fe 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
101
btle.inc
Normal file
101
btle.inc
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Dmitry Grinberg
|
||||||
|
*
|
||||||
|
* http://dmitry.gr/index.php?r=05.Projects&proj=11.%20Bluetooth%20LE%20fakery
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Some additional comments by Florian Echtler <floe@butterbrot.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// see BT Core Spec 4.0, Section 6.B.3.1.1
|
||||||
|
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 = bit 23 (highest-value)
|
||||||
|
t = dst[0] >> 7;
|
||||||
|
|
||||||
|
// left-shift the entire register by one
|
||||||
|
// (dst[0] = bits 23-16, dst[1] = bits 15-8, dst[2] = bits 7-0
|
||||||
|
dst[0] <<= 1;
|
||||||
|
if(dst[1] & 0x80) dst[0] |= 1;
|
||||||
|
dst[1] <<= 1;
|
||||||
|
if(dst[2] & 0x80) dst[1] |= 1;
|
||||||
|
dst[2] <<= 1;
|
||||||
|
|
||||||
|
// if the bit just shifted out (former bit 23)
|
||||||
|
// and the incoming data bit are not equal:
|
||||||
|
// => bit_out ^ bit_in = 1
|
||||||
|
if (t != (d & 1)) {
|
||||||
|
// toggle register bits (=XOR with 1) according to CRC polynom
|
||||||
|
dst[2] ^= 0x5B; // 0b01011011 - x^6+x^4+x^3+x+1
|
||||||
|
dst[1] ^= 0x06; // 0b00000110 - x^10+x^9
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// see BT Core Spec 4.0, Section 6.B.3.2
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
|
30
examples/recv/recv.ino
Normal file
30
examples/recv/recv.ino
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#include <SPI.h>
|
||||||
|
#include <RF24.h>
|
||||||
|
#include <BTLE.h>
|
||||||
|
|
||||||
|
RF24 radio(9,10);
|
||||||
|
|
||||||
|
BTLE btle(&radio);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
|
||||||
|
Serial.begin(9600);
|
||||||
|
while (!Serial) { }
|
||||||
|
Serial.println("BTLE advertisement receiver");
|
||||||
|
|
||||||
|
btle.begin("");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
|
||||||
|
Serial.print("Listening... ");
|
||||||
|
|
||||||
|
if (btle.listen()) {
|
||||||
|
Serial.print("Got payload: ");
|
||||||
|
for (uint8_t i = 0; i < (btle.buffer.pl_size)-6; i++) { Serial.print(btle.buffer.payload[i],HEX); Serial.print(" "); }
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("done.");
|
||||||
|
btle.hopChannel();
|
||||||
|
}
|
||||||
|
|
23
examples/send/send.ino
Normal file
23
examples/send/send.ino
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#include <SPI.h>
|
||||||
|
#include <RF24.h>
|
||||||
|
#include <BTLE.h>
|
||||||
|
|
||||||
|
RF24 radio(9,10);
|
||||||
|
|
||||||
|
BTLE btle(&radio);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
|
||||||
|
Serial.begin(9600);
|
||||||
|
while (!Serial) { }
|
||||||
|
Serial.println("BTLE advertisement sender");
|
||||||
|
|
||||||
|
btle.begin("foobar");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
btle.advertise(0,0);
|
||||||
|
btle.hopChannel();
|
||||||
|
Serial.print(".");
|
||||||
|
}
|
||||||
|
|
46
examples/temperature/temperature.ino
Normal file
46
examples/temperature/temperature.ino
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Emulates a nRF8001 temperature beacon;
|
||||||
|
* reads temperature from a DHT11 and sends it via BTLE.
|
||||||
|
* Compatible with Nordic Semiconductor apps such as
|
||||||
|
* nRF Master Control Panel or nRF Temp 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <BTLE.h>
|
||||||
|
|
||||||
|
#include <SPI.h>
|
||||||
|
#include <RF24.h>
|
||||||
|
#include <dht.h>
|
||||||
|
|
||||||
|
RF24 radio(9,10);
|
||||||
|
BTLE btle(&radio);
|
||||||
|
|
||||||
|
dht DHT;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(57600);
|
||||||
|
while (!Serial) { }
|
||||||
|
Serial.println("BTLE temperature sender");
|
||||||
|
Serial.end();
|
||||||
|
|
||||||
|
// 8 chars max
|
||||||
|
btle.begin("SimoTemp");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
DHT.read11(A0);
|
||||||
|
|
||||||
|
nrf_service_data buf;
|
||||||
|
buf.service_uuid = NRF_TEMPERATURE_SERVICE_UUID;
|
||||||
|
buf.value = BTLE::to_nRF_Float(DHT.temperature);
|
||||||
|
|
||||||
|
if(!btle.advertise(0x16, &buf, sizeof(buf))) {
|
||||||
|
Serial.begin(57600);
|
||||||
|
Serial.println("BTLE advertisement failure");
|
||||||
|
Serial.end();
|
||||||
|
}
|
||||||
|
btle.hopChannel();
|
||||||
|
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user