RSSI Interference

RSSI Interference

Before diving right in on this topic, we should ask what is RSSI and why does interference matter. First off, RSSI stands for Received Signal Strength Indicator and it’s a measurement of the radio frequency power level present at the receiver (1 and 2). A radio frequency transmitter typically has a fixed power output level. As a receiver moves away or around the transmitter, the received power level varies. RSSI is a way to measure that variation. Typically the variation follows a steady change when the receiver and transmitter have an unblocked line of sight of each other. However, objects or materials in the air can cause interference, as well as other RF transmitters that are in range. Since RF signals are only detectable with equipment, an operator of RF equipment would be unaware of the signal strength in the absence of some type of RSSI. So RSSI is a valuable tool to know the operation of the RF equipment, its limits, and factors that might further influence it.

The ESP32-Cam project provides up to 3 possible RSSI values for 802.11 transmitters within its range of detection. The ESP32-Cam also provides a total number of detected 802.11 transmitters in range. For those 3 APs with the strongest signal strength, the ESP32-Cam provides the network name, RSSI value, and whether encryption is enabled. Here is an example, names have been changed to protect the innocent.

Networks	SSID-1	RSSID-1	Enc-1	SSID-2	RSSID-2	Enc-2	SSID-3	RSSID-3	Enc-3
5	MyWifiNet	-83	*	OrbNetwork	-85	*	OrbNetwork	-86	*
5	Pickers	-89	 	KwikPin	-91	*	Vinno	-93	*
10	JailBreak	-84	*	OrbNetwork	-84	*	MyWifiNet	-87	*
4	KwikPin	-89	*	HaloNet	-89	*	KwikPin	-91	*
7	HaloNet	-82	*	Pickers	-82	 	MixNMatch	-89	*
14	MyWifiNet	-85	*	OrbNetwork	-86	*	HaloNet	-86	*
2	Sluther	-92	*	MixNMatch	-95	*		0	 
10	MyWifiNet	-87	*	KwikPin	-87	*	HaloNet	-87	*
10	MyWifiNet	-87	*	OrbNetwork	-88	*	OrbNetwork	-89	*

From the example above, these networks had more than 1 AP in range of the ESP32-Cam, “KwikPin” and “OrbNetwork”. As the ESP32-Cam location changed, the networks and RSSI values also changed. This can be used to create a heat map. But the date has some limitations. Since any network that has more than 1 AP can be read, it fails to provide details about which unique AP it is reading. The third line in the example lists “OrbNetwork”. We have no way of knowing which AP this is from the first line readings. Another significant limitation of the ESP32-Cam is it only provides higher level 802.11 RSSI values. There are other devices that use that same radio frequency that it can not detect.

Another module that can be used to provide RSSI values from lower level RF devices is the NRF24L0. This module is capable of operating as a transceiver, which seems to be its primary use case. However the focus here will be using it to provide detected RSSI values. This code base was used for examples demonstrated below (3).

The image above are the same sample of RSSI readings, but presented in the time (left) and frequency (right) domains. The code used to take these readings is here.

#include <SPI.h>

// Increased define CHANNELS from 64 to 256 in attempt to increase resolution

// Poor Man's Wireless 2.4GHz Scanner
//
// uses an nRF24L01p connected to an Arduino
// 
// Cables are:
//     SS       -> 10
//     MOSI     -> 11
//     MISO     -> 12
//     SCK      -> 13
// 
// and CE       ->  9
//
// created March 2011 by Rolf Henkel
//

#define CE  9

unsigned long myTime;

// Array to hold Channel data (32 = 1 sec, 64 = 2 sec, 128 = 4 sec, 256 = 8 sec, etc)
#define CHANNELS  256
int channel[CHANNELS];

// greyscale mapping 
int  line;
char grey[] = "0123456789";
// char grey[] = " .:-=+*aRW";

// nRF24L01P registers we need
#define _NRF24_CONFIG      0x00
#define _NRF24_EN_AA       0x01
#define _NRF24_RF_CH       0x05
#define _NRF24_RF_SETUP    0x06
#define _NRF24_RPD         0x09

// get the value of a nRF24L01p register
byte getRegister(byte r)
{
  byte c;
  
  PORTB &=~_BV(2);
  c = SPI.transfer(r&0x1F);
  c = SPI.transfer(0);  
  PORTB |= _BV(2);

  return(c);
}

// set the value of a nRF24L01p register
void setRegister(byte r, byte v)
{
  PORTB &=~_BV(2);
  SPI.transfer((r&0x1F)|0x20);
  SPI.transfer(v);
  PORTB |= _BV(2);
}
  
// power up the nRF24L01p chip
void powerUp(void)
{
  setRegister(_NRF24_CONFIG,getRegister(_NRF24_CONFIG)|0x02);
  delayMicroseconds(130);
}

// switch nRF24L01p off
void powerDown(void)
{
  setRegister(_NRF24_CONFIG,getRegister(_NRF24_CONFIG)&~0x02);
}

// enable RX 
void enable(void)
{
    PORTB |= _BV(1);
}

// disable RX
void disable(void)
{
    PORTB &=~_BV(1);
}

// setup RX-Mode of nRF24L01p
void setRX(void)
{
  setRegister(_NRF24_CONFIG,getRegister(_NRF24_CONFIG)|0x01);
  enable();
  // this is slightly shorter than
  // the recommended delay of 130 usec
  // - but it works for me and speeds things up a little...
  delayMicroseconds(100);
}

// scanning all channels in the 2.4GHz band
void scanChannels(void)
{
  disable();
  for( int j=0 ; j<200  ; j++)
  {
    for( int i=0 ; i<CHANNELS ; i++)
    {
      // select a new channel
      setRegister(_NRF24_RF_CH,(128*i)/CHANNELS);
      
      // switch on RX
      setRX();
      
      // wait enough for RX-things to settle
      delayMicroseconds(40);
      
      // this is actually the point where the RPD-flag
      // is set, when CE goes low
      disable();
      
      // read out RPD flag; set to 1 if 
      // received power > -64dBm
      if( getRegister(_NRF24_RPD)>0 )   channel[i]++;
    }
  }
}

// outputs channel data as a simple grey map
void outputChannels(void)
{
  int norm = 0;
  
  // find the maximal count in channel array
  for( int i=0 ; i<CHANNELS ; i++)
    if( channel[i]>norm ) norm = channel[i];
    
  // now output the data
  for( int i=0 ; i<CHANNELS ; i++)
  {
    int pos;
    
    // calculate grey value position
    if( norm!=0 ) pos = (channel[i]*10)/norm;
    else          pos = 0;
    
    // boost low values
    if( pos==0 && channel[i]>0 ) pos++;
    
    // clamp large values
    if( pos>9 ) pos = 9;
   
    // print it out
    Serial.print(grey[pos]);
    Serial.print(',');
    channel[i] = 0;
  }
  
  // indicate overall power
  Serial.print(norm);
  Serial.print(',');
  myTime = millis();
  Serial.print(myTime);
  Serial.println(',');
}


void setup()
{
  Serial.begin(57600);
  
  // Setup SPI
  SPI.begin();
  // Clock Speed, Bit Order, and Data Mode
  SPI.beginTransaction(SPISettings(20000000, MSBFIRST, SPI_MODE0));
  
  // Activate Chip Enable
  pinMode(CE,OUTPUT);
  disable();
  
  // now start receiver
  powerUp();
  
  // switch off Shockburst
  setRegister(_NRF24_EN_AA,0x0);
  
  // make sure RF-section is set properly 
  // - just write default value... 
  setRegister(_NRF24_RF_SETUP,0x0F); 
  
  // reset line counter
  line = 0;
}

void loop() 
{ 
  // do the scan
  scanChannels();
  
  // output the result
  outputChannels();
}

The code generates text that is output on an Arduino Uno serial transmit pin. Putty was used to log the serial stream and save it as a csv file. The resulting file was opened as a spreadsheet and the last 2 columns were used to chart the time domain RSSI values. The frequency domain values could be visualized in the spreadsheet as well using cell value conditions, but this wasn’t efficient. Python was used instead, but the resulting image required some post process normalization, here is the code.

from numpy import genfromtxt
numpy_array = genfromtxt('C:/Share/Python/RSSI Readings_New_256-Resolution.csv', delimiter=',')

import cv2
cv2.imwrite('C:/Share/Python/RSSI Readings_New_256-Resolution.png', numpy_array)

Python libraries used in the example will need to be installed. These steps worked for the examples above. The last video was relevant for the Linux hosts.


There are commercial products that can provide visibility, but the costs for these devices put them out of reach for casual users. Here are list of resources for products used for professional work.

In addition to the cost, these devices aren’t designed to be placed on a remote controlled device or carried aloft using other means due to their size, weight, and power requirements. The RSSI value readings offered by the NRF24L0 provides a wealth of information for its low cost and small size. Be aware though that the manufacturer has indicated that this device should not be used for new designs (4 and 5). Nordic semiconductors is currently offering nRF9 and nRF5 series devices for new designs. Of these new device offerings, Adafruit offers a guide for the nRF51822 (6).

For operators of radio controlled equipment, RSSI readings are often a built in feature. FlySky offers a reasonably priced TX and their RX options offer RSSI values back to the TX. These readings provide the operator with information that can prevent a loss of signal condition. However, the RSSI values the RX provides is also limited in detail. It only provides its RSSI value from the bound TX. The operator has no way of knowing about other devices transmitting in the area. Although FlySky has limited RSSI data, it does offer other features from its iBus support, which will be covered in the next post.

Footnotes

(1) https://en.wikipedia.org/wiki/Received_signal_strength_indication
(2) https://randomnerdtutorials.com/nrf24l01-2-4ghz-rf-transceiver-module-with-arduino/
(3) https://forum.arduino.cc/t/poor-mans-2-4-ghz-scanner/54846
(4) https://www.nordicsemi.com/Products/nRF24-series
(5) https://www.nordicsemi.com/-/media/Publications/WQ-Product-guide/Wireless-Q—Q1—Product-Guide.pdf
(6) https://learn.adafruit.com/introducing-the-adafruit-bluefruit-le-sniffer?view=all

Comments are closed.