From: Pat Thoyts Date: Fri, 16 Oct 2015 22:08:01 +0000 (+0100) Subject: BTLE Beacon with 1-wire and I2C humidity sensors and LCD display. X-Git-Url: http://privyetmir.co.uk/gitweb.cgi?a=commitdiff_plain;h=c6e6d9a062dc2605ea7e3535175a82f060c5b0f8;p=arduino%2FnRF24_BLE_Temperature.git BTLE Beacon with 1-wire and I2C humidity sensors and LCD display. --- c6e6d9a062dc2605ea7e3535175a82f060c5b0f8 diff --git a/nRF24_BLE_Temperature.ino b/nRF24_BLE_Temperature.ino new file mode 100644 index 0000000..047ab18 --- /dev/null +++ b/nRF24_BLE_Temperature.ino @@ -0,0 +1,375 @@ +#include +#include +#include +#include +#include +#include + +#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(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> 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)); +}