Bluetooth Low Energy (BLE) beacon with nRF24L01(+). Data is received and displayed by Android device (Android app source code is attached).
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 }
Generated on Wed Jul 20 2022 03:38:19 by 1.7.2