Monday, July 28, 2014

nRF24L01+ sniffer - part 3

In the first two parts of this series I explained the mechanism behind 'promiscuous' capturing of nRF24L01+ packets on air. I explained how to build hardware and install software to be able to capture the packets and analyze them using Wireshark. This post will dive deeper into the possibilities of the wireless sniffer and Wireshark combo and give you some insight in what this powerful tool has to offer.

Wireshark basics

If you followed the instructions to install the sniffer software and start Wireshark in the previous post, Wireshark should now be capturing:

The capture screen is divided in three parts: The packets captured, the details of the selected packet and the raw packet & payload data.
Packets captured each have a sequential number, and a timestamp (captured with microsecond resolution by the sniffer). These are shown in the first two columns of the packet list named No. and Time. Next two columns contain the Source node address and Destination node address (the base address has been omitted). As nRF24L01+ packets only contain a destination address, the source address will display a question mark. Later we will see that when a higher-level protocol contains the source node address this will be filled. Next is the Protocol column. When capturing the screenshot I only had the nrf24 dissector installed (nrf24.dll in the Wireshark plugins directory), so Wireshark only recognizes nRF24 packages. By adding extra dissectors (e.g. for MySensors) it will also recognize protocols that use the nRF24 as transport medium. Next column is the total packet Length, in bytes followed by the Info column which contains a short, textual description of the packet data. I chose to display the full address (base+node), payload length, pid and ack-flag (inverted NO_ACK).
Packets of length 0 are interpreted by the nRF24 as acknowledgement packets.
The nrf24 dissector calculates the CRC value of every packet. This value should match with the CRC stored in the packet. The CRC for packet 165 apparently failed, therefore it is marked as 'CRC Error' in the info column and its contents cannot be trusted.
When a packet is selected its details appear in the section below. Here you can see, bit by bit, how the packet is dissected.
Click on one of the fields and its location  in the raw packet data will be shown in the section at the bottom. When a packet contains a payload that can further be dissected its raw data are shown in a separate tab (e.g. 'Frame (22bytes)' for the raw frame data and 'NRF24 Payload Data (12 bytes)' for the actual payload data.
This observation is important as the nRF24 has a header which is not a multiple of 8 bits. Any bytes stored as payload will appear bit-shifted in the 'Frame' data, but are shown as regular bytes in the 'NRF24 Payload Data'. In the screenshot the 'NRF24 Payload Data' is shown, in which you might even recognize a MySensors payload: a temperature reading of 24.9 degrees.


One of the most powerful features of Wireshark is filtering. Packets captured can be filtered using expressions on the details of the packet data. The nrf24 dissector recognizes the folowing filter keywords:

  • nrf24.node - full node addres, including base address
  • nrf24.ctrl - control field value
  • nrf24.ctrl.len - payload length
  • - pid value
  • nrf24.ctrl.noack - NO_ACK flag
  • nrf24.crc - CRC value
  • nrf24.crcvalid - flag indicating if CRC is valid or not
Filter expressions are entered in the 'Filter:' editbox on the top of the screen.

Now lets display all packets captured with CRC errors:

Enter 'nrf24.crcvalid == false' and press Apply.
Or filter on all packets with payload of 22 bytes or more, and valid CRC:

You probably get it by now, right? For the filter syntax please refer to the Wireshark user's guide.

IO Graph and statistics

Another nice feature for long term capturing is the IO graph, available through the menu Statistics -> IO Graph.
After capturing for a while and moving the sensor node around a bit, the graph looked like:

The black line shows the number of packets per 10 sec. over a timespan of a little over 700 seconds. When the sensor node started, the number of packets peaked at approx. 200 packets. It then settled at around 40, to increase again after 450 sec.
I enabled 'Graph 2' with the previously explored CRC invalid expression, which is drawn as red bars in the graph. Apparently the location where I moved the sensor node after 450 sec. lead to a clear increase in CRC errors!

Wireshark has many, many more possibilities to analyze your network traffic. I suggest to play around for a while and explore the user manual.

MySensors dissector

Along with the nrf24 dissector I also wrote two dissectors for the MySensors protocol. The stable version 1.3 will be dissected by mysensors1.dll, the beta 1.4 by mysensors2.dll. As said before, only have one of the mysensors dissectors active in the plugins directory, as Wireshark might otherwise mixup protocol versions 1 & 2 in a single capture. Below is a screenshot of a MySensors 1.4b communication capture, between nodes 0 (gateway) and 124 (sensor):

In the selected packet an acknowledge of a temperature value is sent by the gateway to the sensor node. Note that both Source and Destination node addresses are now set for the packets of the mysensors Protocol.
For version 1 (1.3) the following filters are supported:
  • mysensors.crc - CRC value
  • mysensors.crcvalid - CRC Valid flag
  • mysensors.version - Version (always 1)
  • mysensors.binary - Binary flag
  • mysensors.sender - Sender node address
  • mysensors.dest - Destination node address
  • mysensors.last - Last node address
  • mysensors.childid - ChildID for sensor value
  • mysensors.msgtype - Message type
  • mysensors.subtype - Sub type

For version 2 (1.4) the following filters are supported:
  • mysensors.paylen - Length of payload, in bytes
  • mysensors.version - Version (always 2)
  • mysensors.datatype - Data type
  • mysensors.cmdtype - Command type
  • mysensors.ack - Ack flag
  • mysensors.sender - Sender node address
  • mysensors.last - Last node address
  • mysensors.dest - Destination node address
  • mysensors.type - Type
  • mysensors.sensor - Sensor
Capturing all messages to the gateway (dest == 0) for sensor 3 (ChildId 3) results in the following display:


In the past days I've presented a wireless packet sniffer for nRF24L01+ radios. It has very powerful features, thanks to the integration of Wireshark, but also has some limitations anyone who uses it should be aware of.
Currently the dissectors cannot be configured, so packets are always assumed to be in Enhanced Shockburst format, with 1 byte node address and 2 byte CRC value.
Always remember that the nRF24 sniffer uses a regular radio to capture that packets on air. Sometimes it detects CRC errors, while another node will receive the packet correctly, and vice versa.


  1. Good work!

    I had considered the theoretical possibility of using a shorter address, no CRC and fixed length payload to capture an ESB packet & CRC - or most of it, in the case of a nearlly full ESB packet. But the decoding seemed like a lot of work.

    Using wireshark seems to have worked well for you, including handling the nested levels of protocol and the alternatives at the same level (1.3v 1.4). You have surmounted all difficulties to create a useful tool, thank you.

    1. Thanks for your positive reponse man! Appreciate it!

  2. Thank you for the work you have done.

    There are 3 things that I bumped into though that I would like to share with you and those that decide to use the sniffer.

    1. Immediately after starting 'Nrf24Sniff.exe -P6' (the rest is default in my case) I got this error message 'The program can't start because MSVCR110.dll is missing...'. It basically complains that 'Visual C++ Redistributable Packages for Visual Studio 2012 SP 4' is not installed but that is not true. After doing a bit of searching I found that I needed to install or repair 'Microsoft Essentials'.
    Once this was done the program works as expected (don't ask me why, this must be pure MS genius).

    2. Once Wireshark was started like described I noticed that the Nrf24Sniff console window displayed a 'Wait for sniffer to restart' message. The author mentions that this will only take a few seconds but in my case I actually needed to reset the Arduino for it to go away. No exception.

    3. After creating the correct setup in Wireshark I expected data to show up but it did not. I wondered why and ran a complete test to see that the Ardiuno was ok, radio sufficiently powered etc. but that was all ok. I then re-read the article and found that the author says 'when monitoring MySensors 1.4beta traffic (at non-default 1Mb/s)'. I completely missed this and that caused the issue. The MySensors 1.4beta is set to 250b/s by default. Once I started the sniffer like 'Nrf24Sniff.exe -P6 -r2' everything works as expected.

    So, a big thank you to the author!!!

    1. Thanks for trying out. Too bad you ran into troubles (don't say I didn't warn you ;-).
      Some remarks:
      1. According to the DLL should be included in the 'Visual C++ Redistributable for Visual Studio 2012 Update 4' but you need to run all of the installers. I didn't try it yet, as I need to be on a machine without Vsisual Studio installed.
      2. I experience the same problem. Probably packets come in after resetting (captured during reset of the Arduino) which get returned before the configuration is processed. This chokes the nrf24sniff executable. I'll have a look at it.
      3. MySensors 1.4 was switched from default 1Mbps to 250Kbps, as this was believed to decrease CRC errors. I didn't see a difference and sticked with 1Mbps, which became the default in the tool... Now you can check for yourself what bitrate performes best!

  3. Very very cool work! What a grey write up too! Not only did u kick a$$ on the dissectors, research, etc., but wrote it Barney Style so anybody could reproduce your work. Thank you.
    I would add that friend Goodspeed and I have been chatting a bit on the Twittertubes about your great work built on top of his initial research. If you're on Twitter ping myself or Travis. We'd love to "meet" you to possibly discuss further.

    @infosec208 on Twitter.

  4. Glad you like it! The dissectors took far more time to develop than I expected, mainly to the limited development docs on Wireshark dissectors... I'm not very active on Twitter but I might someday... Cheers!

  5. Hi, I use Wireshark V1.12 and I do have an error when I start wireshark, it can't find nrf24.dll while it has been copied to the correct dir.

    1. I see some possible error causes:
      * I developed and tested using Wireshark 1.10.8. The dissectors might not be compatible with 1.12.x without recompilation. Maybe you can try with Wireshark 1.10.x?
      * Did you copy the correct version (32 or 64 bits) of the library, that matches your Wireshark architecture?
      If this all doesn't help, could you provide some more details, like 32/64bits, where you installed Wireshark and the plugins etc?
      Good luck!

    2. I did remove 1.12 64Bit version and installed 1.10.9 64 Bit version on W7 64 Bit. I did copy the correct 64 version and now it detects the dll and I can also see it help->About->Plugins in the list. It doesn't work yet but we did solve this issue.

    3. I checked your project and I was not able to capture a working setup on my side, I did look with the logic analyzer what your sending to the nordic chip and found that your only sending 2 bytes as address bytes and not the 4 you have to send. This is why your setup is not working on my side. I did have a quick look to see the error but I think it is inside the arduino code.

    4. I found that the getAddressWidth(void) call does not work correct !. It always returns 2 even if the SETUP_AW return 0x02 !!.

    5. I located the problem: The Nordic nRF24L01 expect the SPI data to be stable on the rising edge of the SPI clock. This is not happening, it is even changing on the rising edge and stable on the falling edge. The SPI functions are not correct for the nordic. I changed SPI mode_0 to mode_1 but this only solves the input sampling problem not the output shifting of the SPI on the arduino.

    6. The nRF24 driver I'm using is just a 'standard' driver derived from Maniacbug's implementation everybody seems to be using.
      If the SPI configuration really is wrong, this is a serious bug!
      Could also be a problem in the SPI implementation in the Arduino libraries. Which Arduino IDE version do you use?
      Did you make any progress on the issue?

    7. @Yveaux, I located the problem. Today I received the new saleae logic Pro series of logic analyzers and I did have a good look at what happens. 8MHz SPI speed mode 0. The MOSI output is delay a lot and by mounting a pullup resistor of 1K to 3V3 I made sure that the data is stable well before the clock signal. It still fails and its due to the fact that I am sending at maximum speed 1 Mbits/s but sending packets every 400us. The internal processing of the arduino nRF24 lib is so bad and slow that you have to wait a minimum of 1ms between two transmissions to make it work. I will try to make the lib faster.

  6. It would be interesting to see if you can add the wireshark code to the RTL-SDR version of the sniffer -

  7. Does anyone know of a BLE 4.0 sniffer for wireshark ?

  8. Awesome project!!!

    Do you think you can capture beacon frame packages from access points including RSSI???, in regular beacon frame snifing (tcpdump -i mon0 link[0]==0x80), you have this type of output:

    18:50:31.564045 2341290519us tsft 1.0 Mb/s 2412 MHz 11g -21dB signal antenna 1 Beacon (rherca) [1.0* 2.0* 5.5* 11.0* Mbit] ESS CH: 3, PRIVACY
    18:50:31.666393 2341392918us tsft 1.0 Mb/s 2412 MHz 11g -22dB signal antenna 1 Beacon (rherca) [1.0* 2.0* 5.5* 11.0* Mbit] ESS CH: 3, PRIVACY
    18:50:31.768774 2341495317us tsft 1.0 Mb/s 2412 MHz 11g -23dB signal antenna 1 Beacon (rherca) [1.0* 2.0* 5.5* 11.0* Mbit] ESS CH: 3, PRIVACY
    18:50:31.871452 2341597716us tsft 1.0 Mb/s 2412 MHz 11g -22dB signal antenna 1 Beacon (rherca) [1.0* 2.0* 5.5* 11.0* Mbit] ESS CH: 3, PRIVACY
    18:50:31.973664 2341700140us tsft 1.0 Mb/s 2412 MHz 11g -24dB signal antenna 1 Beacon (rherca) [1.0* 2.0* 5.5* 11.0* Mbit] ESS CH: 3, PRIVACY
    18:50:32.076181 2341802514us tsft 1.0 Mb/s 2412 MHz 11g -22dB signal antenna 1 Beacon (rherca) [1.0* 2.0* 5.5* 11.0* Mbit] ESS CH: 3, PRIVACY

    It would be great to have a similar output, when using your device to capture beacon frame packages....

    I really apreciate your feedback

    Best Regards

    1. Hi Rodrigo!

      This project is only able to capture packets transfered between nRF24L01+ nodes. It can be modified to capture traffic from devices using identical frame format (preamble etc.) as the nRF24L01+.
      Have a detailed look at the datasheet of the nRF and the beacon you'd like to capture from to see if the frames are identical.

      Good luck!

    2. Yveaux,

      thanks four your clarification, your quick reply saved me plenty of time. I'll follow your recomendations, if i have luck i'll let you know

  9. Check this! maybe you can update the sniffer

    1. Yo Damme!

      The blog from Travis inspired me to develop this sniffer, as you can read in post #1.
      Or are you looking for a version integrated into a wall charger? ;-)

  10. Hi, -- using V1.4 of Mysensors
    I have been using your sniffer/disector to try and see how MySensor deals with AKCs. I have one node and one GW in the network an all are within arms reach. The node sends out two messages at a 10 second interval. The first message is sent with the ACK flag and the second one is sent without.

    I see both messages appear on Wireshark, but neither message has ReqAck set, instead the IsACK flag is set on the message where I have the ACK request flag on the msg.send() call.

    Also I don't see any 0 length ACK packages in the air. I had expected to see some sort of radio response from the GW for a message with an ACK request set.

    What am I not getting here? Is MYsensor1.4 goofed up and it sends a ACK_response instead of an ACK request or is the disector wrong indicating response instead of request?


  11. I should add to the above, that neither packet had ACK set in the control field of nrf24 -- ???????

    1. Hi Gary,

      Please read the limitations of the sniffer on page 1 -- probably this causes some confusion!


    2. Hi Yveaux

      I did read the limitations, and I understand what you are saying regarding the turn around time. I understand that most likely I will not capture the ack package out of the air itself because of that.

      I'm however still puzzled as to the meaning of the MySensor protocol level element IsAck v.s. ReqAck. On a package originating at a node with the ACK flag set I would expect to see a reqAck being set not an IsAck.

      Also puzzled why all packages I capture have the nrf24 level noAck bit cleared ( therefore requesting a hw-level ack) even though I send some packages without the ACK flag. Sadly I can't find much of any documentation on the MySensor site......


    3. Hey Gary,

      Could be the issue I fixed in the MySensors development branch a while ago (see lines 107/118).

      Questions regarding the MySensors protocol itself should better be discussed on the MySensors forum.


  12. Hi Yveaux,
    You do a good job for debugging the nrf24L01+ networks. I followed your instructions Part1 and Part2, and get the captured packetes from the Wireshark. But all the captured packets showed CRC Error. I can not find the reason. Can you help me.

    1. I had a quick look at your capture and what caught my eye is that the node address of the captured packet is different all the time.
      What type of traffic are you trying to capture? This sniffer will only work for Nordic Semiconductor nRF24L01+ chips, not nRF24L01 or any other types of 2.4Ghz radios.

  13. Hello Yveaux, at first to say that the project is amazing. I'm using it to be able to connect a remote controller of my drone, to my robot car. I guess by knowing the address name and the channel it may work, but maybe it can't just be because of the kind of chip. So I want to sniff the address of my remote controller, to code a communication with pipes, usin the addresses. I'm not sure now if it can just be. So today I've downloaded the git repo and uploaded the sketch to Arduino. I cold compile and upload it, but first changed channel (which I captured with sniffer), turned on the remote controller and it stucks on Listening ... (I changed the macro BINARY_OUTPUT) the sketch recognizes well the device and seems to be working well, but I just read in a comment that this work you did only works with the same chip. I'm confused if changing this sketch I would be able to sniff the address, or would be better if you can help me to connect it, because maybe you know how to establish the I/O pipes in some other way without addresses. Thanks in advance.

    1. I got the channels with scanner, not with sniffer. I turned on and off the RController and I could see how 5 channels became busy.

  14. This comment has been removed by the author.