--- /dev/null
+#include <Arduino.h>
+#include <Wire.h>
+#include <SPI.h>
+#include <OneWire.h>
+#include <DallasTemperature.h>
+#include <LiquidCrystal_I2C.h>
+
+#define LCD_ADDR 0x20
+#define LCD_EN_BIT 4
+#define LCD_RW_BIT 5
+#define LCD_RS_BIT 6
+#define LCD_D4_BIT 0
+#define LCD_BL_BIT 7
+
+LiquidCrystal_I2C lcd(LCD_ADDR, LCD_EN_BIT, LCD_RW_BIT, LCD_RS_BIT,
+LCD_D4_BIT, LCD_D4_BIT+1, LCD_D4_BIT+2, LCD_D4_BIT+3);
+
+#define PIN_CE 8 // chip enable
+#define PIN_CSN 10 // chip select (for SPI)
+#define PIN_LED 6
+#define PIN_ONEWIRE 4
+
+// The MAC address of BLE advertiser -- just make one up
+#define MY_MAC_0 0x01
+#define MY_MAC_1 0x02
+#define MY_MAC_2 0xef
+#define MY_MAC_3 0xbe
+#define MY_MAC_4 0xad
+#define MY_MAC_5 0xde
+
+// 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
+
+uint8_t buf[32];
+static const uint8_t chRf[] = {2, 26,80};
+static const uint8_t chLe[] = {37,38,39};
+uint8_t ch = 0; // RF channel for frequency hopping
+
+#define SENSOR_RESOLUTION 12
+OneWire w1bus(PIN_ONEWIRE);
+DallasTemperature sensor(&w1bus);
+DeviceAddress sensorAddress;
+bool have_sensor = false;
+float currentTemperature = 20.0f;
+uint8_t state = 0;
+
+// Converts a temperature value in 100ths degrees C into a nRF float (4 bytes)
+int32_t nrf_float(float t)
+{
+ const int32_t exponent = -2;
+ return ((exponent & 0xff) << 24) | (((int32_t)(t * 100)) & 0x00ffffff);
+}
+
+// implementing CRC with LFSR
+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;
+ }
+ }
+ }
+}
+
+// reverse the bit order in a single byte
+uint8_t swapbits(uint8_t x)
+{
+#ifdef __BUILTIN_AVR_INSERT_BITS
+ return __builtin_avr_insert_bits(0x01234567, x, 0);
+#else
+ uint8_t v = 0;
+ if(x & 0x80) v |= 0x01;
+ if(x & 0x40) v |= 0x02;
+ if(x & 0x20) v |= 0x04;
+ if(x & 0x10) v |= 0x08;
+ if(x & 0x08) v |= 0x10;
+ if(x & 0x04) v |= 0x20;
+ if(x & 0x02) v |= 0x40;
+ if(x & 0x01) v |= 0x80;
+ return v;
+#endif
+}
+
+// Implementing whitening with LFSR
+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 value we actually use is what BT'd use left shifted one...makes our life easier
+static inline uint8_t btLeWhitenStart(uint8_t chan)
+{
+ return swapbits(chan) | 2;
+}
+
+// Assemble the packet to be transmitted
+// Length is of packet, including crc. pre-populate crc in packet with initial crc value!
+void btLePacketEncode(uint8_t* packet, uint8_t len, uint8_t chan)
+{
+ 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]); // the byte order of the packet should be reversed as well
+}
+
+uint8_t spi_byte(uint8_t byte)
+{
+ // using Arduino's SPI library; clock out one byte
+ SPI.transfer(byte);
+ return byte;
+}
+
+void nrf_cmd(uint8_t cmd, uint8_t data)
+{
+ // Write to nRF24's register
+ digitalWrite(PIN_CSN, LOW);
+ spi_byte(cmd);
+ spi_byte(data);
+ digitalWrite(PIN_CSN, HIGH);
+}
+
+void nrf_simplebyte(uint8_t cmd)
+{
+ // transfer only one byte
+ digitalWrite(PIN_CSN, LOW);
+ spi_byte(cmd);
+ digitalWrite(PIN_CSN, HIGH);
+}
+
+void nrf_manybytes(uint8_t* data, uint8_t len)
+{
+ // transfer several bytes in a row
+ digitalWrite(PIN_CSN, LOW);
+ do{
+ spi_byte(*data++);
+ } while(--len);
+ digitalWrite(PIN_CSN, HIGH);
+}
+
+volatile int analogValue;
+static void Analog_Init()
+{
+ analogValue = 0;
+ // REFS[1:0]: 0b00: AREF; 0b01: AVCC; 0b11: internal 1V1
+ // ADLAR = 0: do not enable left adjust
+ // MUX[3:0]: using pin A1 (0b0001)
+ ADMUX = _BV(MUX0) | _BV(REFS0);// Internal 5V
+ // ADEN: enable the ADC
+ // ADATE: enable auto trigger
+ // ADIE: adc interrupt enabled
+ // ADPS[2:0]: adc clock prescale 16MHz/128 so 125kHz
+ ADCSRA = _BV(ADEN) | _BV(ADATE) | _BV(ADIE)
+ | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0);
+ // ADTS[2:0]: AutoTrigger Source: use free running mode
+ ADCSRB = 0;
+ // Disable the digital functions for pin A2
+ DIDR0 |= _BV(ADC2D);
+ // ADSC: start the freerunning conversion
+ // if not freerunning we could alternate between a set of sources
+ // like AD8 which is the internal temperature.
+ ADCSRA |= _BV(ADSC);
+}
+
+static int GetAnalogValue() { return analogValue; }
+static int GetAnalogMillivolts() { return (analogValue * 5120) / 1024; }
+
+static long GetHumidity()
+{
+ // Vref is externally provided and set to 5ishV.
+ long millivolts = (analogValue * 5120) / 1024;
+ // HU10 range is 1V == 0% to 3V == 100%.
+ long rh = ((millivolts - 1000) * 100000) / 2000;
+ return rh; /* value is percentage * 1000 */
+}
+
+ISR(ADC_vect)
+{
+ // NOTE: MUST read the low byte first, reading ADCH resets.
+ analogValue = ADCL | (ADCH << 8);
+ // If not in free-running mode then start another conversion
+ // ADCSRA |= _BV(ADSC);
+}
+
+static void UpdateDisplay()
+{
+ lcd.setCursor(0, 1);
+ lcd.print(currentTemperature);
+ lcd.print((char)0xdf);
+ lcd.print("C ");
+ lcd.print(static_cast<float>(GetHumidity())/1000.0f);
+ lcd.print(" RH");
+}
+
+void setup()
+{
+ pinMode(PIN_LED, OUTPUT);
+ pinMode(PIN_CSN, OUTPUT);
+ pinMode(PIN_CE, OUTPUT);
+ pinMode(11, OUTPUT);
+ pinMode(13, OUTPUT);
+ digitalWrite(PIN_LED, HIGH);
+ digitalWrite(PIN_CSN, HIGH);
+ digitalWrite(PIN_CE, LOW);
+
+ sensor.begin();
+ sensor.setResolution(SENSOR_RESOLUTION);
+ sensor.setWaitForConversion(false);
+ have_sensor = sensor.getAddress(sensorAddress, 0);
+ if (have_sensor)
+ sensor.requestTemperatures();
+
+ Serial.begin(57600);
+ Serial.println(F("# BTLE beacon"));
+ SPI.begin();
+ SPI.setBitOrder(MSBFIRST);
+
+ Analog_Init();
+
+ Wire.begin();
+ lcd.begin(16,2);
+ lcd.clear();
+ lcd.print("BTLE Beacon");
+
+ // Now initialize nRF24L01+, setting general parameters
+ 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); //4-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
+
+ // Set access addresses (TX address in nRF24L01) to BLE advertising 0x8E89BED6
+ // Remember that both bit and byte orders are reversed for BLE packet format
+ buf[0] = 0x30;
+ buf[1] = swapbits(0x8E);
+ buf[2] = swapbits(0x89);
+ buf[3] = swapbits(0xBE);
+ buf[4] = swapbits(0xD6);
+ nrf_manybytes(buf, 5);
+ buf[0] = 0x2A; // set RX address in nRF24L01, doesn't matter because RX is ignored in this case
+ nrf_manybytes(buf, 5);
+
+ for (int n = 0; n < 20; ++n)
+ {
+ digitalWrite(PIN_LED, !digitalRead(PIN_LED));
+ delay(100);
+ }
+
+ // must have waited a total of 750 ms
+ if (have_sensor)
+ {
+ currentTemperature = sensor.getTempC(sensorAddress);
+ }
+ UpdateDisplay();
+}
+
+void loop()
+{
+ // Channel hopping
+ for (ch=0; ch<sizeof(chRf); ch++)
+ {
+ uint8_t i, L=0;
+
+ buf[L++] = 0x42; //PDU type, given address is random; 0x42 for Android and 0x40 for iPhone
+ buf[L++] = 24; // length 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;
+
+ /* device descriptor chunk */
+ buf[L++] = 2; /* chunk size 2 bytes */
+ buf[L++] = 0x01; /* chunk type: device flags */
+ buf[L++] = 0x05; /* data: 0x05 == LE only, limited discovery mode */
+
+ /* name chunk */
+ buf[L++] = 6; /* chunk size: 7 bytes */
+ buf[L++] = 0x09; /* chunk type: complete name */
+ buf[L++] = 'n';
+ buf[L++] = 'R';
+ buf[L++] = 'F';
+ buf[L++] = '2';
+ buf[L++] = '4';
+
+ buf[L++] = 7;
+ buf[L++] = 0x16;
+ buf[L++] = (uint8_t)(NRF_TEMPERATURE_SERVICE_UUID);
+ buf[L++] = (uint8_t)((NRF_TEMPERATURE_SERVICE_UUID >> 8) & 0xff);
+ {
+ int32_t t = nrf_float(currentTemperature);
+ memcpy(&buf[L], &t, sizeof(t));
+ L += sizeof(t);
+ }
+
+ buf[L++] = 0x55; //CRC start value: 0x555555
+ buf[L++] = 0x55;
+ buf[L++] = 0x55;
+
+ 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
+
+ digitalWrite(PIN_CSN, LOW);
+ spi_byte(0xA0);
+ for(i = 0 ; i < L ; i++) spi_byte(buf[i]);
+ digitalWrite(PIN_CSN, HIGH);
+
+ nrf_cmd(0x20, 0x12); // TX on
+ digitalWrite(PIN_CE, HIGH); // Enable Chip
+ delay(2); //
+ digitalWrite(PIN_CE, LOW); // (in preparation of switching to RX quickly)
+ }
+
+ if (have_sensor)
+ {
+ switch (state)
+ {
+ case 0:
+ sensor.requestTemperatures();
+ state = 1;
+ break;
+ case 1:
+ state = 2; // just allow over 750ms for conversion.
+ break;
+ case 2:
+ currentTemperature = sensor.getTempC(sensorAddress);
+ Serial.println(currentTemperature);
+ UpdateDisplay();
+ state = 0;
+ }
+ }
+ delay(500);
+ digitalWrite(PIN_LED, !digitalRead(PIN_LED));
+}