Monday, November 12, 2012

Turning Point Clicker Emulation with Arduino and nRF24L01

Turning Point Clicker Emulation with Arduino and nRF24L01

The Turning Point ResponseCard RF, often referred to as a clicker, sends user input to a hub for polling purposes. The user can select a button labeled 1-9 and 0, or A-J and the clicker will send the result to the hub which collects all of the responses. Travis Goodspeed details some of the inter-workings of the Turning Point ResponseCard RF in his blog. He found out that the device always sends it’s answer to the MAC address 0x123456. Then the payload itself is preambled with the clickers MAC address and then the user input, which is ascii 0 through 9 or a question mark. Finally the packet is terminated with a 2-byte CRC.

The heart of the clicker is a Nordic chip, nRF24E1 or nRF24LE1 for later models. This chip includes a nRF24L01 wireless transceiver, which is readily available on ebay for about $2 and can be quickly attached to an Arduino or other microcontroller. This article will discuss how to use an nRF24L01 chip along with an Arduino to emulate and extend the functionality of the Turning Point ResponseCard RF.

The code for the nRF24L01 Clicker Emulator is here. The Arduino code with the nRF24L01 chip can do the following:

  • Listen to audience responses and generate statistics
  • Block all audience responses
  • Respond as a single clicker with a select answer
  • Respond as all clickers with a select answer
  • Respond as all clickers, each with a random answer
  • Display all active audience members MAC addresses
  • Automatically respond with the most popular answer


Connecting the nRF24L01 with the Arduino is quite easy. There are a total of 8 pins that need to be connected. Vcc is connected to the 3.3v source and ground to ground. The nRF24L01 then communicates through 4-wire SPI, so those wires are connected to the proper pins based on your version of Arduino. Finally the CE line can be hooked to any unused digital pin and is used for enabling RX and TX operations. The IRQ line is active low and can also be hooked to any digital pin. It is used to inform when TX has taken place or there are packets in the RX buffer and other similar matters.

The inputs on the nRF24L01 are 5v tolerant, so no additional circuitry is needed between the two devices. Also, note that your SPI pins may or may not be the same as those reflected in the Arduino documentation. I used a knock-off Arduino Nano ($12 on ebay) and several of the pins were reversed. Your best bet is to look at the Atmega328 datasheet to determine the correct SPI pins.


Writing code for the Arduino is also pretty simple because Arduino is already distributed with a SPI library, which can communicate with the nRF24L01 at it’s full 8mhz. Which is very useful when packets are being sent as hundreds of different clickers. In order to interact with the clicker hub, it is important that the nRF24L01 talk in the same format. Below are some of the variables that need changed:

  • CRC should be set to 2 bytes in length or disabled if jamming
  • PRIM_RX is set for receiving and unset for transmitting
  • Auto-Acknowledge should be disabled
    • Having this enabled forces the nRF24L01 to send Enhanced ShockBurst packets, which are incompatible
    • Acknowledge packets should be manually generated
  • The address width is set to “01” corresponding with 3 byte MAC addresses
  • RF_DR is set to 1Mbps
  • RF_PWR can be set to 0dBm at power cost but longer range
  • The status register must frequently be cleared in order to reset the interrupts

After all of the registers have been set, it is important to also set the destination address and the payload. There is no length field for the payload. It will transmit as many bytes as you send to it, up to 32. In the case of the clicker emulator, I send 4 bytes. Three of the bytes are the source MAC, or the emulated source MAC, followed by a 1 byte answer.

To receive an acknowledgment, the source MAC address must be stored in the nRF24L01. Two source MAC addresses can be stored at a time plus four more that only vary from the second MAC by the last byte. Currently this program ignores acknowledgements and assumes that the packet went through, which is OK in low traffic situations, but may cause failures at long distances or with lots of audience members responding. To overcome this, the nRF24L01 sends multiple copies of each packet by using the REUSE_TX_PL command and holding CE high. More copies can be sent by holding CE high for longer.

Eventually acknowledgement packets will be accounted for after some real world testing. Much of the development of this was done without a hub to test against, so advancements will come with more access to the hub. The acknowledgement packet is sent to the clickers MAC and is composed of a one byte status and a two byte CRC. The value 0x04 lights a red light on the clicker, 0x06 is a green light and 0x53 is an orange blinking light. The others cause no response in the LED and it is not clear what their functions are. The only issue now is determining when to listen for the acknowledgement packet and after how long to give up or try again.


There really is no security in the Turning Point devices. Once the MAC address for the hub was known, it was simple to just listen for packets on that MAC address. There is nothing to stop a user from then emulating that devices MAC address, which can be done with a single SPI command. While one of the Turning Point devices is sold at campus bookstores for around 50 USD, this device can be created for 14 USD and has extended functionality compared to the clickers. While the code in the clickers themselves is locked, it is possible to program all of this functionality directly into the nRF24LE01 chip.


  1. So I uploaded the code to my Arduino Micro and connected the tranciever to the Arduino. When I connect the Arduino to the serial monitor I get nothing. Adding in a "while(!serial)" before the "serial.println(freeram)))" I just get a "1024".

    What do you recommend I try?

    1. Try placing the "while(!Serial);" right below "Serial.begin(115200);" . The help() function and PRINT_STRING() use the Serial class also. Make sure that serial monitor is set to 115200 baud or change the function to 9600 and set the serial monitor to 9600 baud. I've compiled this with Arduino 1.0.1, I'm not sure if other version would have trouble with this code or not.

      I am interested to see what is acting up, so please let me know if any of those solutions work.

  2. This comment has been removed by the author.

  3. I have this running on an arduino Uno, with an nrf24L01+, but it doesn't seem to "see" my clicker... I've tried various channels and such but with no luck. Any suggestions?

    1. It turns out I had two pins switched! Seems to work now. Thanks!

    2. Hi, I am currently has the same issue as you do. Could you please tell how did you fix the issue.


  4. interesting article, but I am curious, is there any way to also listen to see what channel the clickers are on, or does it have to be hard coded

    1. Yeah, I imagine you could implement a hopping algorithm pretty easily. Just set the receiving MAC address to 0x123456 and wait for a packet with a correct size and checksum. If it doesn't hear anything, then hop to the next frequency. Right now you can manually change channels using the f command followed by the channel number and a period.

  5. So I have some of your program working and reading off the answer and mac, but it varies slightly, like the packet is malformed or something. This is when CRC is disabled. I believe setting the CRC length would take care of weeding out the bad packets, but whenever I set the CRC to 16 I never get any data at all, and ideas?

  6. Does this work for Iclicker2? Doesn't seem to receive anything or do you need a base?

    1. I got the serial port to open up and set fAA. for frequency but no values after hitting t