WiFi Spectrum Analyzer

Hi everyone,
I finished the (mostly) permanent version of my 2.4ghz spectrum analyzer, and soldered it up. I included 3 modes for scanning (fast, slow, and ghost - like the long exposure on a camera), plus a function to display the voltage on an analog pin, and graph it (for when the oscilloscope's not cooperating). These modes are toggled through by hitting the big push button (EDIT: which is hiding under the green radio module). More Information about the build is here: http://arduino.cc/forum/index.php/topic,52655.0.html , and here is a video of the first version working, the functionality is about the same: Wireless Spectrum analyzer and multimeter - YouTube . I was planning on making another version that was much smaller, but I'm in the middle of another project.

I have already used it to help setup my wireless network, (channel, location, things that cause interference), and it is always interesting to see what uses the 2.4ghz spectrum. So far, the things that I've noticed on the spectrum while walking around with the analyzer are:
-microwave ovens (huge disturbance in the middle of the spectrum)
-Wifi
-Cordless phones
-Bluetooth
-Wireless keyboard
-Wireless speakers
The fast mode is ok for seeing EMI, but for digital signals, the slow mode is best. The ghost mode also gives a general idea of spectrum use over a period of time.

Main sketch: CodeTidy.com is for sale | HugeDomains
LCD Header: CodeTidy.com is for sale | HugeDomains
Rssi library header (rssi.h): CodeTidy.com is for sale | HugeDomains
Rssi Library (rssi.cpp) : CodeTidy.com is for sale | HugeDomains

Thanks! I hope someone finds my code or design useful.

Very interesting project, well done!!

Q: what wifi shield did you use? (I admit, did not read all links yet)

Some remarks to improve the sketch

There is a possible lock in the rssi.cpp: if RADIO_Read() does not return the right bit set (could happen theoretically) there is an endless loop.

while(1){
    // Force conversion
    RADIO_Write(REG_CARRIER_DETECT,0x00);
    RADIO_Write(REG_CARRIER_DETECT,0x80);
    // RSSI_ADC_CONVERSION (50)
    delayMicroseconds(50);
    // Read RSSI
    value=RADIO_Read(REG_RSSI);  <<<<<<<<<<
    // Exit if valid
    if (value&0x20) break;  <<<<<<<<<<<<<
  }

adding a small timeout test fixes this (or a max # of iterations => for loop)

#define RADIO_TIMEOUT 10000
unsigned long start = millis();
while(1)
{
    // Force conversion
    RADIO_Write(REG_CARRIER_DETECT,0x00);
    RADIO_Write(REG_CARRIER_DETECT,0x80);
    // RSSI_ADC_CONVERSION (50)
    delayMicroseconds(50);
    // Read RSSI
    value=RADIO_Read(REG_RSSI);
    // Exit if valid
    if (value&0x20) break;
    
    // TIMEOUT
    if (millis() - start > RADIO_TIMEOUT) break;
  }

Is it possible to merge these two loops?

    for (unsigned char n=0; n<MAXCHANNEL; n++){
      // Read the signal on current channel
      unsigned char s = radio.RADIO_RSSI(n);
      s = map(s, 0, 31, 0,Lcd_visible); 
      signal[n]=s;
    }
    for (unsigned char n = LCD_VISIBLE_X_RES; n > 0; n--){
      drawLine(n,Lcd_TOP,PIXEL_OFF);
      drawLine(n,signal[abs(LCD_VISIBLE_X_RES-n)], PIXEL_ON);
    }

to something like:

    for (unsigned char n=0; n<MAXCHANNEL; n++)
    {
      // Read the signal on current channel
      unsigned char s = radio.RADIO_RSSI(n);
      s = map(s, 0, 31, 0,Lcd_visible); 

      // redraw if it was changed 
      if (signal[n] != s) 
      {
         signal[n]=s;
         _draw(signal[n]);   
      }

Conditionally drawing only the changed lines would speedup the graphical part of the app.

Furthermore if I understand correctly the routine for drawing the graphics first clear a vertical line completely and then draws a new line representing the new signal strength. That means that part of the pixels are addressed twice, you could try something like this to improve on that. (less time drawing means more time scanning :wink:

    for (unsigned char n = LCD_VISIBLE_X_RES; n > 0; n--)
    {
      byte y = 0;
      for ( ; y <= signal[n]; y++) setPixel(n, y, PIXEL_ON);
      for ( ; y <= LCD_TOP; y++) setPixel(n,y, PIXEL_OFF);
    }

Thanks for your feedback! While I haven't yet had any freezes, I have now implemented your timeout idea, just in case.
As for the wireless, I'm using the CYWM6935 board, which (annoyingly) has 2mm pitch headers, and that is why you see it hanging over the part of the board to which it is attached.

I kept the measuring and drawing arrays separate for the sake of accuracy. My reasoning is that if the radio waits to draw the value of the current channel on the lcd (which can be pretty slow to respond), before moving to the next channel, then the times at which each channel's measurement was taken are further spaced apart, and the full portrait of the spectrum is less accurate.

However, I did add a line to the drawing loops to check if the measurement has changed, and I also added that fix in the drawing algorithm (which is a really good idea) and it seems to be running slightly faster, so thanks for that.

Thanks again!

My pleasure to optimize;)

I kept the measuring and drawing arrays separate for the sake of accuracy.

You have thought about it and have good arguments for your choice.

I saw this code and thought that should be a function:

    gotoXY(75,5);
    LcdCharacter('F');
    gotoXY(69,5);
    LcdCharacter('a');
    .... etc
void  lcdString(byte x, byte y, char * s)
{
  for (char *p = *s; *p != '\0'; p++, x=x-6)
  {
    gotoXY(x, y);
    lcdCharacter(*p);
  }
}

then just call - lcdString(75,5, "Fast Scan"); - just less typing and less errorprone :wink:

Thanks again for sharing your nice project.

hey sorry to go a little off topic here... but i've seen a similar project, but the guy picked up a spike around the same frequency that his CPU used (when he got it close to his cpu). are you able to observe the same phenomenon?

However, I did add a line to the drawing loops to check if the measurement has changed, and I also added that fix in the drawing algorithm (which is a really good idea) and it seems to be running slightly faster, so thanks for that.

The drawing part can be improved even more. You added the code to only redraw the line if the value was changed. That is optimization on line level. Now think about optimizing the drawing of lines on pixel level. What you really want is to redraw only the pixels that need to be changed. Sounds much harder than it is.

There are three scenario's:

Suppose the current value is 15;

  1. new value is 11 => only need to turn off 4 pixel;
  2. new value is 21 => only need to turn on 6 pixels
  3. new value is 15 => no pixels to change

The nice part of this is that this pixel level algorithm includes the optimization at line level (scenario 3).

You need an additional array holding the newSignal[], same size as signal[] and by comparing the two you know what to do. The amount of code is roughly the same as the

As I don't have your latest code I just "prototype" it, so you can get the idea:

    // READ NEW SIGNAL STRENGTHS
    for (unsigned char n=0; n<MAXCHANNEL; n++)
    {
      // Read the signal on current channel
      unsigned char s = radio.RADIO_RSSI(n);
      newSignal[n] = map (s, 0, 31, 0, Lcd_visible); 
    }

    // ADJUST DISPLAY
    for (unsigned char n = LCD_VISIBLE_X_RES; n > 0; n--)   // should be MAXCHANNEL ???
    {
      if (newSignal[n] > signal[n])     // stronger signal => more pixels on
      {
        for (byte y = signal[n]; y <= newSignal[n]; y++) setPixel(n, y, PIXEL_ON);
      } 
      else if (newSignal[n] < signal[n])   // weaker signal => less pixels
      {
        for (byte y = newSignal[n]; y <= signal[n]; y++) setPixel(n, y, PIXEL_OFF);
      } 
      // else if (newSignal[n] == signal[n]) {} // do nothing 

      // REMEMBER SIGNALS
      signal[n] = newSignal[n];
    }

The value of each signal entry is mapped upon 0..40,
In the previous algorithm every pixel is addressed once, ==> 40 * MAXCHANNEL == 40 * 84 = 3360 setpixel calls.
Assumption: strength per channel changes 10% on average ==> 10% of the pixels need to be redrawn, 3360 * 10% = 336 setPixel calls on average
Note 1: Only the first time more pixels need to be set.
Note 2: In worst case all pixels need to be redrawn.

I am very interested if this theoretical speedup is matched in practice.... Can you post the results?

Succes,
Rob

update -- redid the math

hey sorry to go a little off topic here... but i've seen a similar project, but the guy picked up a spike around the same frequency that his CPU used (when he got it close to his cpu). are you able to observe the same phenomenon?

Do you mean the CPU of his PC? as that can be very well in the 2.4 GHz range..

yet another optimization

    int aread = analogRead(5);
    float readval = (float)aread / 1023.0;
    readval = readval * 5.0;
    readval = readval * 100.0;

=>

    float readval = 0,488759 * analogRead(5);  // readval = 5 * 100 * analogRead(5) /1023.0;

you can remove this float math altogether by using a long.

   long readval = (int) (500L * analogRead(5) / 1023L);

as this seems the only float math in the sketch it would reduce the footprint quite a bit making room for new functionality :wink:

Hi folks,

I was looking to build a 2.4GHz analyser and I stumbled upon this thread. Thankyou for your hard work and I would like to share a couple of changes I have made which allowed me to increase the fast scan scanning speed by a wopping 54.5 times!

First change: Switch the way the data is fed to the LCD screen from ShiftOut to SPI. Simply move the wires from pin 4 to pin 11, and pin 3 to pin 13. Then change shiftOut(PIN_SDIN, PIN_SCLK, MSBFIRST, data); in LcdWrite to SPI.transfer(data)
This resulted in a 15x speed increase, taking the frame rate from 0.36fps to 5.75fps

Second change: The LcdWrite function uses bitWrite() to assert the chip select line instead of digitalWrite(), so I changed RADIO_Read() and RADIO_Write() to use bitWrite(). Pin 8 on the arduino relates to PORTB, bit 0, so I changed the functions to become

// Read data from radio module
unsigned char rssi::RADIO_Read(unsigned char address){
  bitWrite(PORTB, 0, 0) ; 
  unsigned char value;
  SPI.transfer(address);
  value = SPI.transfer(0x00);
  bitWrite(PORTB, 0, 1) ; 
  return value;
}

// Write data to radio module
void rssi::RADIO_Write(unsigned char address, unsigned char value){
  // Enable module
  bitWrite(PORTB, 0, 0) ; 
  // Send data
  SPI.transfer(0x80|address);
  SPI.transfer(value);
  // Disable module
  bitWrite(PORTB, 0, 1) ; 
}

This resulted in a much smaller speed increase, of just over quarter of a frame a second but any increase is a good increase!

Finally I implemented the by-pixel line update method put forward by robtillaart and this resulted in a further increase from 6.06fps to 19.61fps! A wopping 54.47x speed increase.

You might be wondering why I need such a fast refresh rate, but I am using it to monitor RC aircraft transmitters and the FrSky system I am using hops frequency so fast that if the frame rate is too slow then the odds of scanning a given frequency at the time the TX is transmitting on it is very slim.

I hope that my mods will prove usefull to someone else!

Thank you so much for the hard work that you guys put in on this.

Chris

50+ times faster, that's a BIG improvement altogether,

Chris, can you post your final code (attached as a zip or so) for people who want this thread also useful?

Thanks in advance,
Rob

If you get a chance, please do post your code, those improvements look great! This really is the best part of open source development, having others build off of and (significantly) improve a project.

Just a question- did you implement the drawing changes in the oscilloscope function too? How does that affect the speed?

Also, this project was featured on hackaday this summer, which was very exciting.

Happy Friday!

Looks great! You asked for ideas for further development. Well, you have 2.4GHz signal generator, so with the addition of microwave bridge, you could build a reflectometer. A reflectometer measures the magnitude of the reflection coefficient of the microwave circuit under test, and is useful for measuring input characteristics of things like antennas, amplifiers, transmission- line discontinuities, etc.

We cannot download the code from your link.
Can any body share me the code, please?

Could i have your complete modified code?

This sounds like a great project! Perhaps just the thing I am looking for. Could you please send the complete code to me also? Thanks!

Hey, i would like to ask if you can tell me how exactly could i connect my arduino with CYWM6935.
I mean pinout connection.
How can i check if it works correctly in order to run the specrum analyzer?

Thanks in advance