From 08f39fae67e2b5825f5f5e15f24a48938b340ac9 Mon Sep 17 00:00:00 2001 From: brendan <2bndy5@gmail.com> Date: Mon, 26 Oct 2020 19:37:50 -0700 Subject: [PATCH] undo nRF24#646; add broken FakeBLE example --- .github/workflows/build_arduino.yml | 30 ++- RF24.cpp | 40 +++ examples/FakeBLE/FakeBLE.ino | 249 ++++++++++++++++++ .../GettingStarted_CallResponse.ino | 0 .../rf24_ATTiny/rf24ping85/rf24ping85.ino | 0 .../timingSearch3pin/timingSearch3pin.ino | 0 utility/ATTiny/RF24_arch_config.h | 4 +- utility/ATTiny/spi.h | 55 ++++ 8 files changed, 373 insertions(+), 5 deletions(-) create mode 100644 examples/FakeBLE/FakeBLE.ino rename {examples_backup => examples}/GettingStarted_CallResponse/GettingStarted_CallResponse.ino (100%) rename {examples_backup => examples}/rf24_ATTiny/rf24ping85/rf24ping85.ino (100%) rename {examples_backup => examples}/rf24_ATTiny/timingSearch3pin/timingSearch3pin.ino (100%) create mode 100644 utility/ATTiny/spi.h diff --git a/.github/workflows/build_arduino.yml b/.github/workflows/build_arduino.yml index a04f1c0e2..884cc3d1c 100644 --- a/.github/workflows/build_arduino.yml +++ b/.github/workflows/build_arduino.yml @@ -74,9 +74,14 @@ jobs: - name: Compile examples uses: arduino/compile-sketches@main with: - # sketch-paths: | - # - examples_backup/GettingStarted - # - examples_backup/GettingStarted_CallResponse + sketch-paths: | + - examples/GettingStarted + - examples/GettingStarted_CallResponse + - examples/AcknowledgementPayloads + - examples/StreamingData + - examples/InterruptConfigure + - examples/MulticeiverDemo + - examples/FakeBLE # - examples_backup/GettingStarted_HandlingData # - examples_backup/GettingStarted_HandlingFailures # - examples_backup/pingpair_ack @@ -93,3 +98,22 @@ jobs: # - examples_backup/Usage/led_remote # - examples_backup/Usage/nordic_fob fqbn: ${{ matrix.fqbn }} + attiny: + stradegy: + fail-fast: false + + fqbn: + - arduino:avr:ATtinyX5 + - arduino:avr:ATtinyX4 + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Compile examples + uses: arduino/compile-sketches@main + with: + name: arduino:avr + source-url: https://github.com/damellis/attiny.git + sketch-paths: - examples/rf24_ATTiny + fqbn: ${{ matrix.fqbn }} \ No newline at end of file diff --git a/RF24.cpp b/RF24.cpp index ec6a01a34..79d27f8cc 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -1608,3 +1608,43 @@ void RF24::stopConstCarrier() write_register(RF_SETUP, (read_register(RF_SETUP)) & ~_BV(CONT_WAVE) & ~_BV(PLL_LOCK)); ce(LOW); } + +//ATTiny support code pulled in from https://github.com/jscrane/RF24 +#if defined(RF24_TINY) + +void SPIClass::begin() { + // set USCK and DO for output + // set DI for input + #if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) + DDRB |= (1 << PB2) | (1 << PB1); + DDRB &= ~(1 << PB0); + #elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) + DDRA |= (1 << PA4) | (1 << PA5); + DDRA &= ~(1 << PA6); + #elif defined(__AVR_ATtiny2313__) || defined(__AVR_ATtiny4313__) + DDRB |= (1 << PB7) | (1 << PB6); + DDRB &= ~(1 << PB5); + #elif defined(__AVR_ATtiny861__) + DDRB |= (1 << PB2) | (1 << PB1); + DDRB &= ~(1 << PB0); + #endif // defined(__AVR_ATtiny861__) + USICR = _BV(USIWM0); +} + +byte SPIClass::transfer(byte b) +{ + USIDR = b; + USISR = _BV(USIOIF); + do { + USICR = _BV(USIWM0) | _BV(USICS1) | _BV(USICLK) | _BV(USITC); + } + while ((USISR & _BV(USIOIF)) == 0); + return USIDR; +} + +void SPIClass::end() {} +void SPIClass::setDataMode(uint8_t mode){} +void SPIClass::setBitOrder(uint8_t bitOrder){} +void SPIClass::setClockDivider(uint8_t rate){} + +#endif \ No newline at end of file diff --git a/examples/FakeBLE/FakeBLE.ino b/examples/FakeBLE/FakeBLE.ino new file mode 100644 index 000000000..aaef8d627 --- /dev/null +++ b/examples/FakeBLE/FakeBLE.ino @@ -0,0 +1,249 @@ +/* + * See documentation at https://tmrh20.github.io/RF24 + * See License information at root directory of this library + * Author: Brendan Doherty (2bndy5) + */ + +/** + * A simple example of using the nRF24L01 transceiver as a BLE advertising + * beacon. + * + * Original research was done by Dmitry Grinberg; his research can be found at + * http://dmitry.gr/index.php?r=05.Projects&proj=11.%20Bluetooth%20LE%20fakery + * + * Some of this example's code has been taken from Dmitry Grinberg's research, + * while other bits of code has been derived from the + * BTLE library by Florian Echtler. If this example seems to limited for your + * projects needs, we highly recommend Florian Echtler's BTLE library, which + * can be found in the Arduino IDE and directly on github at + * https://github.com/floe/BTLE + */ +#include +#include "RF24.h" + +// instantiate an object for the nRF24L01 transceiver +RF24 radio(6, 5); // using pin 7 for the CE pin, and pin 8 for the CSN pin + +// BLE only operates on 3 specified frequencies channels. It usually employ a +// technique called "channel hopping" +const uint8_t frequency[3] = {2, 26, 80}; // translates {37, 38, 39} per BLE +uint8_t currentChannel = 0; // used for "channel hopping" + +// Because the nRF24L01 transceiver wasn't designed for BLE advertisements, +// this technique has some limits. The members of the following struct that +// are not declared as `const` can be adjusted (except checksum). +struct PayloadStruct { + uint8_t pduType = 0x42; // iPhone & Android compatible + const uint8_t size = 27; // actual size is 30 + size bytes + uint8_t mac[6]; // an arbitrary 6 byte MAC address + const uint8_t flags[3] = {0x02, 0x01, 0x05}; // 0x05 = discoverable & non-connectable + const uint8_t namePrefix[2] = {0x04, 0x08}; // appears as "short name" + uint8_t name[3] = {'n', 'R', 'F'}; // must be 3 bytes long in this example + const uint8_t prefixBatt[4] = {0x04, 0x16, 0x0F, 0x18}; // Battery service data header + uint8_t batteryPercent; // we can set this in loop() + const uint8_t prefixTemp[4] = {0x07, 0x16, 0x09, 0x18}; // temperature service data header + int8_t temperatureInC[4]; // we can set this with temperatureData() + uint8_t checksum[3] = {0x55, 0x55, 0x55}; // we set this with crc24() +}; +PayloadStruct payload; // instantiate the payload + +// we a buffer to manipulate the data without altering the payload values +uint8_t packet[32]; // allocate 32 bytes for copying the payload data + +void temperatureData(float celcius); +void crc(uint8_t *data, uint8_t len, uint8_t* checksum); +void swapbuf(uint8_t *data, uint8_t len); +void whiten(uint8_t *data, uint8_t len); + +void setup() { + + Serial.begin(115200); + while (!Serial) { + // some boards need to wait to ensure access to serial over USB + } + + // initialize the transceiver on the SPI bus + if (!radio.begin()) { + Serial.println(F("nRF24L01 is not responding!!")); + while(1) {} // hold in infinite loop + } + + // print example's introductory prompt + Serial.println(F("RF24/examples/FakeBLE")); + Serial.println(F("Advertising")); + + + // initialize the random number generator and use it to set the MAC address + // randomSeed(analogRead(A3)); // analog input pin A3 is open/unconnected + // for (uint8_t i = 0; i < 6; ++i) + // payload.mac[i] = random(255); // use a random byte + + // or make the mac address consistent + uint8_t mac[] = {0xa1, 0xb2, 0xc3, 0xd4, 0xe5, 0xf6}; + for (uint8_t i = 0; i < 6; ++i) + payload.mac[i] = mac[i]; // use a preset address + + payload.batteryPercent = 85; + temperatureData(42.0); + + memcpy(packet, &payload, 32); + crc(packet, 29, packet + 29); + for (uint8_t i = 0; i < 32; ++i) { + Serial.print("packet["); + Serial.print(i); + Serial.print("]"); + Serial.println(packet[i], HEX); + } + + // Set the PA Level low to try preventing power supply related problems + // because these examples are likely run with nodes in close proximity to + // each other. BLE specifications allow this to be changed. + radio.setPALevel(RF24_PA_LOW); // RF24_PA_MAX is default. + + // The following functions disable the features that are incompatible with BLE specifications + radio.disableDynamicPayloads(); // disable dynamic payloads + radio.setAutoAck(false); // disable auto-ack feature + radio.disableCRC(); // disable CRC checking + radio.setRetries(0, 0); // disable auto-retry feature + + // the following functions configure the nRF24L01 to be compatible with BLE + // specifications. + radio.setDataRate(RF24_1MBPS); + radio.setAddressWidth(4); + radio.openWritingPipe(0x6B7D9171); + radio.setChannel(frequency[currentChannel]); // we will manage this in the loop() + radio.stopListening(); // powerUp() into TX mode +} // setup + +void loop() { + // broadcast BLE advertisement, hop channel, then wait till next try + + // set the service data + payload.batteryPercent = 85; // must be a percentage in range [0, 100] + temperatureData(42.0); // degree of percision = 2 decimal places + + // prepare the payload for BLE compliance + memcpy(packet, &payload, 32); // copy payload to a temporary buffer + crc(packet, 29, packet + 29); // calculate checksum + whiten(packet, 32); // whiten (to avoid repeating bit patterns OTA) + swapbuf(packet, 32); // reverse the bit order + + // broadcast advertisement payload + if (radio.write(&packet, 32)) { + // print something to show the program isn't frozen + // Serial.println('*'); + + // hop to next frequency + currentChannel += currentChannel < 2 ? 1 : -2; // increment frequency index + radio.setChannel(frequency[currentChannel]); // set new frequency + delayMicroseconds(150); // let the change in channel settle + + } else { + // due to all the features that disabled, we will never get here. + // Serial.println(F("There must be a power supply problem.")); // if we do get here + } + + // slow down transmissions so save power + delay(500); // change this to your liking (preferrably in range [10, 10000] ms) +} + +void temperatureData(float celcius) { + // set the temperature as a 4 byte float + payload.temperatureInC[3] = ((int8_t)(-2) & 0xFF); // exponent + int32_t base = (int32_t)(celcius * 100); + payload.temperatureInC[2] = (base & 0xFF0000) >> 16; + payload.temperatureInC[1] = (base & 0xFF00) >> 8; + payload.temperatureInC[0] = (base & 0xFF); +} + +void crc(uint8_t *data, uint8_t len, uint8_t* dst) { + // calculate CRC24 with according to BT Core Spec 4.0, Section 6.B.3.1.1 + + uint8_t *buffer = (uint8_t*)&data; + // 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 = *(buffer++); + + 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 + } + } + } + // for (uint8_t i = 0; i < 3; ++i) { + // Serial.print("calc'd crc["); + // Serial.print(i); + // Serial.print("]"); + // Serial.println(dst[i], HEX); + // } +} + +void swapbuf(uint8_t *data, uint8_t len) { + + uint8_t *buffer = data; + + while (len--) { + // reverse all the bits for each byte + uint8_t a = *buffer; + 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; + + *(buffer++) = v; + } + // for (uint8_t i = 0; i < 31; ++i) { + // Serial.print("swapped["); + // Serial.print(i); + // Serial.print("]"); + // Serial.println(packet[i], HEX); + // } +} + +void whiten(uint8_t *data, uint8_t len) { + // whiten the payload so that there are less repeating bits (for OTA stability) + + // initialize LFSR with current channel, set bit 6 + uint8_t lfsr = (currentChannel + 37) | 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; + } + *(data++) ^= res; + } + // for (uint8_t i = 0; i < 31; ++i) { + // Serial.print("whitened["); + // Serial.print(i); + // Serial.print("]"); + // Serial.println(packet[i], HEX); + // } +} diff --git a/examples_backup/GettingStarted_CallResponse/GettingStarted_CallResponse.ino b/examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino similarity index 100% rename from examples_backup/GettingStarted_CallResponse/GettingStarted_CallResponse.ino rename to examples/GettingStarted_CallResponse/GettingStarted_CallResponse.ino diff --git a/examples_backup/rf24_ATTiny/rf24ping85/rf24ping85.ino b/examples/rf24_ATTiny/rf24ping85/rf24ping85.ino similarity index 100% rename from examples_backup/rf24_ATTiny/rf24ping85/rf24ping85.ino rename to examples/rf24_ATTiny/rf24ping85/rf24ping85.ino diff --git a/examples_backup/rf24_ATTiny/timingSearch3pin/timingSearch3pin.ino b/examples/rf24_ATTiny/timingSearch3pin/timingSearch3pin.ino similarity index 100% rename from examples_backup/rf24_ATTiny/timingSearch3pin/timingSearch3pin.ino rename to examples/rf24_ATTiny/timingSearch3pin/timingSearch3pin.ino diff --git a/utility/ATTiny/RF24_arch_config.h b/utility/ATTiny/RF24_arch_config.h index 585d066e5..cfb871aff 100644 --- a/utility/ATTiny/RF24_arch_config.h +++ b/utility/ATTiny/RF24_arch_config.h @@ -21,9 +21,9 @@ #include #endif -#include +#include -#include +#include "SPI.h" #define _SPI SPI diff --git a/utility/ATTiny/spi.h b/utility/ATTiny/spi.h new file mode 100644 index 000000000..91314e57c --- /dev/null +++ b/utility/ATTiny/spi.h @@ -0,0 +1,55 @@ +// ATTiny support code is from https://github.com/jscrane/RF24 + +/** + * @file spi.h + * \cond HIDDEN_SYMBOLS + * Class declaration for SPI helper files + */ + +#include +#include +#include + +#define SPI_CLOCK_DIV4 0x00 +#define SPI_CLOCK_DIV16 0x01 +#define SPI_CLOCK_DIV64 0x02 +#define SPI_CLOCK_DIV128 0x03 +#define SPI_CLOCK_DIV2 0x04 +#define SPI_CLOCK_DIV8 0x05 +#define SPI_CLOCK_DIV32 0x06 +//#define SPI_CLOCK_DIV64 0x07 + +#define SPI_MODE0 0x00 +#define SPI_MODE1 0x04 +#define SPI_MODE2 0x08 +#define SPI_MODE3 0x0C + +#define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR +#define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR +#define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR + +class SPIClass { +public: + static byte transfer(byte _data); + + // SPI Configuration methods + + inline static void attachInterrupt(); + + inline static void detachInterrupt(); // Default + + static void begin(); // Default + static void end(); + + static void setBitOrder(uint8_t); + + static void setDataMode(uint8_t); + + static void setClockDivider(uint8_t); +}; + +extern SPIClass SPI; + +/** + * \endcond + */ \ No newline at end of file