Bluetooth Low Energy (BLE) beacon with nRF24L01(+). Data is received and displayed by Android device (Android app source code is attached).

Dependencies:   DS1820 mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /*
00002 Using nRF24L01(+) as a Bluetooth Low Energy Advertiser/Broadcaster/Beacon
00003 by hacking an nRF24L01(+) module (which is under $1 per unit on eBay).
00004 
00005 See wiki page: <https://developer.mbed.org/users/hudakz/code/BLE_nRF24L01>
00006 
00007 Note: It works with both nRF24L01 and nRF24L01+ modules.
00008 
00009 Temperature readings measured by a DS1820 chip are broacasted over Bluetooth LE.
00010 (You can easily modify the code to broadcast some other custom data.
00011  Only make sure not to exceed the 32 bytes packet length.)
00012  
00013 The data can be received and displayed on an Android device (smartphone/tablet)
00014 with Bluetooth Low Energy (Bluetooth Smart) enabled. 
00015 In order to have Bluetooth 4.0 available (which implements Bluetooth LE)
00016 your device should run Android 4.3 or more recent.
00017  */
00018 
00019 
00020 #include "mbed.h"
00021 #include "DS1820.h"
00022 
00023 #define DEBUG 1
00024 
00025 #if DEBUG
00026 Serial serial(USBTX, USBRX);
00027 #endif
00028 
00029 // The MAC address of BLE advertizer -- just make one up
00030 // If you decide to ceate more Beacons make sure that MAC address (MY_MAC) is unique for each beacon
00031 
00032 #define MY_MAC_0    0x11
00033 #define MY_MAC_1    0x12
00034 #define MY_MAC_2    0x33
00035 #define MY_MAC_3    0x44
00036 #define MY_MAC_4    0x55
00037 #define MY_MAC_5    0x66
00038 
00039 #if defined(TARGET_LPC1768)
00040 SPI         spi(p11, p12, p13);     // MOSI, MISO, SCK
00041 DigitalOut  cs(p8);                 // CSN  (select SPI chip/slave)
00042 DigitalOut  ce(p14);                // CE   (enable nRF24L01 chip)
00043 DS1820      ds1820(p15);            // create a ds1820 sensor
00044 #elif defined(TARGET_NUCLEO_F103RB) || defined(TARGET_NUCLEO_L152RE) || defined(TARGET_NUCLEO_F030R8)  \
00045    || defined(TARGET_NUCLEO_F401RE) || defined(TARGET_NUCLEO_F302R8) || defined(TARGET_NUCLEO_L053R8)  \
00046    || defined(TARGET_NUCLEO_F411RE) || defined(TARGET_NUCLEO_F334R8) || defined(TARGET_NUCLEO_F072RB)  \
00047    || defined(TARGET_NUCLEO_F091RC) || defined(TARGET_NUCLEO_F303RE) || defined(TARGET_NUCLEO_F070RB)  \
00048    || defined(TARGET_KL25Z ) || defined(TARGET_KL46Z) || defined(TARGET_K64F) || defined(TARGET_KL05Z) \
00049    || defined(TARGET_K20D50M) || defined(TARGET_K22F) \
00050    || defined(TARGET_NRF51822) \
00051    || defined(TARGET_RZ_A1H)
00052 SPI         spi(D11, D12, D13);     // MOSI, MISO, SCK
00053 DigitalOut  cs(D6);                 // CSN  (select SPI chip/slave)
00054 DigitalOut  ce(D7);                 // CE   (enable nRF24L01 chip)
00055 DS1820      ds1820(D8);             // create a ds1820 sensor
00056 
00057 // If your board/plaform is not present yet then uncomment
00058 // the following five lines and replace TARGET_YOUR_BOARD, SPI_MOSI, SPI_MISO, SPI_SCK, SPIS_CS, CE_PIN and DS1820_DATA_PIN as appropriate.
00059 
00060 //#elif defined(TARGET_YOUR_BOARD)
00061 //SPI         spi(SPI_MOSI, SPI_MISO, SPI_SCK);
00062 //DigitalOut  cs(SPI_CS);             // CSN  (select SPI chip/slave)
00063 //DigitalOut  cs(CE_PIN);             // CE   (enable nRF24L01+ chip)
00064 //DS1820      ds1820(DS1820_DATA_PIN);// create a ds1820 sensor
00065 
00066 #endif
00067 
00068 uint8_t     buf[32];
00069 
00070 /**
00071  * @brief   Implements CRC with LFSR
00072  * @note
00073  * @param   data:   packet data
00074  *          len:    packet length
00075  *          dst:    destination/location of CRC
00076  * @retval
00077  */
00078 void bleCRC(const uint8_t* data, uint8_t len, uint8_t* dst) {
00079     uint8_t v, t, d;
00080 
00081     while(len--) {
00082         d = *data++;
00083         for(v = 0; v < 8; v++, d >>= 1) {
00084             t = dst[0] >> 7;
00085             dst[0] <<= 1;
00086             if(dst[1] & 0x80)
00087                 dst[0] |= 1;
00088             dst[1] <<= 1;
00089             if(dst[2] & 0x80)
00090                 dst[1] |= 1;
00091             dst[2] <<= 1;
00092 
00093             if(t != (d & 1)) {
00094                 dst[2] ^= 0x5B;
00095                 dst[1] ^= 0x06;
00096             }
00097         }
00098     }
00099 }
00100 
00101 /**
00102  * @brief   Reverses bit order in a single byte
00103  * @note
00104  * @param   a:  byte to be reveresed
00105  * @retval  byte with reveresed bit order
00106  */
00107 uint8_t swapBits(uint8_t a) {
00108     uint8_t v = 0;
00109     if(a & 0x80)
00110         v |= 0x01;
00111     if(a & 0x40)
00112         v |= 0x02;
00113     if(a & 0x20)
00114         v |= 0x04;
00115     if(a & 0x10)
00116         v |= 0x08;
00117     if(a & 0x08)
00118         v |= 0x10;
00119     if(a & 0x04)
00120         v |= 0x20;
00121     if(a & 0x02)
00122         v |= 0x40;
00123     if(a & 0x01)
00124         v |= 0x80;
00125     return v;
00126 }
00127 
00128 /**
00129  * @brief   Implements whitening with LFSR
00130  * @note
00131  * @param   data:   location of the data to be whiten
00132  *          len:    data length
00133  *          whitenCoeff:    whitening coefficient
00134  * @retval
00135  */
00136 void bleWhiten(uint8_t* data, uint8_t len, uint8_t whitenCoeff) {
00137     uint8_t m;
00138     while(len--) {
00139         for(m = 1; m; m <<= 1) {
00140             if(whitenCoeff & 0x80) {
00141                 whitenCoeff ^= 0x11;
00142                 (*data) ^= m;
00143             }
00144 
00145             whitenCoeff <<= 1;
00146         }
00147 
00148         data++;
00149     }
00150 }
00151 
00152 /**
00153  * @brief   Starts whitening
00154  * @note    the value we actually use is what BT'd use left shifted one...makes our life easier
00155  * @param   chan:   BT channel
00156  * @retval  single byte
00157  */
00158 static inline uint8_t bleWhitenStart(uint8_t chan) {
00159     return swapBits(chan) | 2;
00160 }
00161 
00162 /**
00163  * @brief   Assembles the packet to be transmitted
00164  * @note
00165  * @param   data:   packet data
00166  *          len:    packet length
00167  *          dst:    BLE channel
00168  * @retval
00169  */
00170 void blePacketEncode(uint8_t* packet, uint8_t len, uint8_t chan) {
00171     // Length is of packet, including crc. pre-populate crc in packet with initial crc value!
00172     uint8_t i, dataLen = len - 3;
00173     bleCRC(packet, dataLen, packet + dataLen);
00174     for(i = 0; i < 3; i++, dataLen++)
00175         packet[dataLen] = swapBits(packet[dataLen]);
00176     bleWhiten(packet, len, bleWhitenStart(chan));
00177     for(i = 0; i < len; i++)
00178         packet[i] = swapBits(packet[i]);    // the byte order of the packet should be reversed as well
00179 }
00180 
00181 /**
00182  * @brief   Sends cmommand to nRF24L01
00183  * @note
00184  * @param   cmd:    Command
00185  *          data:   Data associated with the command
00186  * @retval
00187  */
00188 void nrfCmd(uint8_t cmd, uint8_t data) {
00189 
00190     // Write to nRF24's register
00191 
00192     cs = 0;
00193     spi.write(cmd);
00194     spi.write(data);
00195     cs = 1;
00196 }
00197 
00198 /**
00199  * @brief   Transfers one byte to nRF24L01
00200  * @note
00201  * @param   cmd: the byte to be transferred
00202  * @retval
00203  */
00204 void nrfWriteByte(uint8_t cmd) {
00205     // transfer only one byte
00206     cs = 0;
00207     spi.write(cmd);
00208     cs = 1;
00209 }
00210 
00211 /**
00212  * @brief   Transfers several bytes to nRF24L01
00213  * @note
00214  * @param   data:   location of bytes to be transferred
00215  *          len:    number of bytes to be transferred
00216  * @retval
00217  */
00218 void nrfWriteBytes(uint8_t* data, uint8_t len) {
00219     // transfer several bytes in a row
00220     cs = 0;
00221     do
00222     {
00223         spi.write(*data++);
00224     } while(--len);
00225     cs = 1;
00226 }
00227 
00228 int main() {
00229     static const uint8_t    chRf[] = { 2, 26, 80 };
00230     static const uint8_t    chLe[] = { 37, 38, 39 };
00231     
00232     uint8_t  i = 0;
00233     uint8_t  j = 0;
00234     uint8_t  ch = 0;
00235     uint8_t  data[4];  
00236     float*   temp = reinterpret_cast <float*>(&data[0]);
00237     int      error = 0;
00238     
00239     // Chip must be deselected
00240     cs = 1;
00241  
00242     // Setup the spi for 8 bit data, high steady state clock,
00243     // second edge capture, with a 10MHz clock rate
00244     spi.format(8,0);
00245     spi.frequency(10000000);
00246  
00247     ce = 0;
00248 
00249     // Initialize nRF24L01+, setting general parameters
00250     nrfCmd(0x20, 0x12);    // on, no crc, int on RX/TX done
00251     nrfCmd(0x21, 0x00);    // no auto-acknowledge
00252     nrfCmd(0x22, 0x00);    // no RX
00253     nrfCmd(0x23, 0x02);    // 4-byte address
00254     nrfCmd(0x24, 0x00);    // no auto-retransmit
00255     nrfCmd(0x26, 0x06);    // 1MBps at 0dBm
00256     nrfCmd(0x27, 0x3E);    // clear various flags
00257     nrfCmd(0x3C, 0x00);    // no dynamic payloads
00258     nrfCmd(0x3D, 0x00);    // no features
00259     nrfCmd(0x31, 32);      // always RX 32 bytes
00260     nrfCmd(0x22, 0x01);    // RX on pipe 0
00261 
00262     // Set access addresses (TX address in nRF24L01) to BLE advertising 0x8E89BED6
00263     // Remember that both bit and byte orders are reversed for BLE packet format
00264     buf[0] = 0x30;
00265     buf[1] = swapBits(0x8E);
00266     buf[2] = swapBits(0x89);
00267     buf[3] = swapBits(0xBE);
00268     buf[4] = swapBits(0xD6);
00269     nrfWriteBytes(buf, 5);
00270     buf[0] = 0x2A;          // set RX address in nRF24L01, doesn't matter because RX is ignored in this case
00271     nrfWriteBytes(buf, 5);
00272     
00273     if(!ds1820.begin()) {
00274 #if DEBUG        
00275         serial.printf("No DS1820 sensor found!\r\n");
00276 #endif
00277         return 1;
00278     }
00279     
00280           
00281     while(1) { 
00282         ds1820.startConversion();   // Start temperature conversion
00283         wait(1.0);                  // let DS1820 complete the temperature conversion
00284         error = ds1820.read(*temp); // read temperature from DS1820 and perform cyclic redundancy check (CRC)
00285 
00286 #if DEBUG        
00287         switch(error) {
00288         case 0:    // no errors -> '*temp' contains the value of measured temperature
00289             serial.printf("temp = %3.1f\r\n", *temp);
00290             break;
00291         case 1:    // no sensor present -> '*temp' is not updated
00292             serial.printf("no sensor present\n\r");
00293             break;
00294         case 2:    // CRC error -> '*temp' is not updated
00295             serial.printf("CRC error\r\n");
00296         } 
00297 #endif
00298 
00299         for(ch = 0; ch < (sizeof(chRf) / sizeof(*chRf)); ch++) {
00300             i = 0;
00301             buf[i++] = 0x42;            // PDU type, given address is random; 0x42 for Android and 0x40 for iPhone
00302             buf[i++] = 25;              // number of following data bytes, max 29  (CRC is not included)
00303             
00304             //----------------------------
00305             buf[i++] = MY_MAC_0;
00306             buf[i++] = MY_MAC_1;
00307             buf[i++] = MY_MAC_2;
00308             buf[i++] = MY_MAC_3;
00309             buf[i++] = MY_MAC_4;
00310             buf[i++] = MY_MAC_5;
00311         
00312             buf[i++] = 2;               // flags (LE-only, limited discovery mode)
00313             buf[i++] = 0x01;
00314             buf[i++] = 0x05;
00315         
00316             buf[i++] = 9;               // length of the name, including type byte
00317             buf[i++] = 0x08;            // TYPE_NAME_SHORT
00318             buf[i++] = 'n';
00319             buf[i++] = 'R';
00320             buf[i++] = 'F';
00321             buf[i++] = '2';
00322             buf[i++] = '4';
00323             buf[i++] = 'L';
00324             buf[i++] = '0';
00325             buf[i++] = '1';
00326         
00327             buf[i++] = 5;               // length of custom data, including type byte
00328             buf[i++] = 0xff;            // TYPE_CUSTOMDATA
00329     
00330             buf[i++] = data[0];         // temperature floating point value (four bytes)
00331             buf[i++] = data[1];         
00332             buf[i++] = data[2];         
00333             buf[i++] = data[3];         
00334             //----------------------------
00335             
00336             buf[i++] = 0x55;            // CRC start value: 0x555555
00337             buf[i++] = 0x55;
00338             buf[i++] = 0x55;
00339         
00340             nrfCmd(0x25, chRf[ch]);
00341             nrfCmd(0x27, 0x6E);         // Clear flags
00342             blePacketEncode(buf, i, chLe[ch]);
00343             nrfWriteByte(0xE2);         // Clear RX Fifo
00344             nrfWriteByte(0xE1);         // Clear TX Fifo
00345             
00346             cs = 0;
00347             spi.write(0xA0);
00348             for(j = 0; j < i; j++)
00349                 spi.write(buf[j]);
00350             cs = 1;
00351         
00352             nrfCmd(0x20, 0x12);         // TX on
00353             ce = 1;                     // Enable Chip
00354             wait_ms(50);    
00355             ce = 0;                     // (in preparation of switching to RX quickly)
00356         }
00357     }
00358 }