Get WiFi RSSI on Mac

Hey!

I’m trying to make a WiFi signal strength mapper so that when I click I can store the RSSI on the current network. However, I can’t find any methods in processing to get the RSSI. I thought it would be something similar to this but I just haven’t been able to find any library that has any similar functions.

Do you have any suggestions regarding this?

EDIT: I noticed the topic was moved to Electronics, but I’m wondering if this can be done in Processing. The link was just an example of how I’ve been able to do the same in Arduino.

Thanks!

1 Like

LOL
well, that might have been “someones” idea to use the USB connected arduino to do the JOB
and send the data to processing for the SHOW, why not?


i have no idea about external tools for processing to do that,
but my idea is just to talk to the operating system to give you that data.

for example on a Raspberry PI / Raspbian ( linux debian ) buster
in a terminal can use

sudo iwlist wlan0 scan | egrep "Cell|ESSID|Signal" 

( for your MAC i have no idea )


in processing i play


ArrayList ssids;

void setup() {
  ssids = scan_wifi();
  println(ssids);
}

void draw() {}

// from https://stackoverflow.com/questions/15869578/java-wifi-api
//_____________________________________________scan wifi on RPI / Raspbian buster
import java.io.InputStreamReader;

public static ArrayList scan_wifi() {
  ArrayList<String> networkList = new ArrayList<String>();
  try {
    //String command = "netsh wlan show networks mode=Bssid"; // ? windows wifi 
    // RASPBERRY PI Terminal try: sudo iwlist wlan0 scan | egrep "Cell|ESSID|Signal"
    String command = "sudo iwlist wlan0 scan";

    Process p = Runtime.getRuntime().exec(command);
    try   {  p.waitFor();  } 
    catch (InterruptedException ex) {  ex.printStackTrace(); }
    BufferedReader reader = new BufferedReader( new InputStreamReader(p.getInputStream()) );
    String line;

    while ((line = reader.readLine()) != null) {
      System.out.println(line);                   // see RAW
      if      ( line.contains("ESSID:") )         networkList.add(splitTokens(line, ":")[1]);
      else if ( line.contains("Signal level=") )  networkList.add(splitTokens(line, "=")[2]);
    }
  } 
  catch (IOException e) { }
  return networkList;
}


i know not a good array usage,
but i see like [ signal1,ssid1,signal2,ssid2,signal3,ssid3]
just to test the idea.

1 Like

Thanks for the response!

That might be an interesting idea; just have an esp8266 read the rssi and send it through serial to a processing sketch. However, I want to know the RSSI as read by the computer. For some reason (maybe difference in antenna?) the rssi I get from the esp is quite different from that of the computer.

This is very interesting! I have never done this but I did find the command to ask for the rssi in the osx terminal:

/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I | grep CtlRSSI | sed -e 's/^.*://g' | xargs -I SIGNAL

And I also managed to execute the command from processing (I got the code from here):

void setup() {
  String[] params = {"/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport", "-I", "|", "grep", "CtlRSSI", "|", "sed", "-e", "'s/^.*://g'", "|", "xargs", "-I", "SIGNAL"};
  Process p = exec(params);
  try {
    int result = p.waitFor();
    println("the process returned " + result);
  } 
  catch (InterruptedException e) {
    println("failed");
  }
}

But I haven’t been able to read the output from this command for use in my sketch. Any ideas on that?

2 Likes

For a more complete example, see @jeffthompson’s sketch here:

https://forum.processing.org/one/topic/running-bash-script-from-processing#25080000001056314.html

To make it work you will want to add

import java.io.InputStreamReader;

and change workingDir to:

File workingDir = new File(sketchPath());

Now you can see your results with the BufferedReader. But the command seems broken – why?

It may be because you cannot use pipes | with a Java Process. The recommended workaround is instead to put your command in a shell script and execute the script, capturing the output.

WiFiCheck.sh:

#!/usr/bin/env bash
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I | grep CtlRSSI | sed -e 's/^.*://g' | xargs -I SIGNAL
2 Likes

Thanks a lot for the link, it’s the best explanation on how this works that I’ve seen so far!

I did find this code as well (that does pretty much the same thing as jeffthomson’s). I used it to make my sketch:

import java.io.*;

void setup() {
  String[] params = {"/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport", "-I", "|", "grep", "CtlRSSI", "|", "sed", "-e", "'s/^.*://g'", "|", "xargs", "-I", "SIGNAL"};
  Process p = exec(params);
  InputStream input = p.getInputStream(); 

  try {
    BufferedReader in = new BufferedReader(new InputStreamReader(input));
    String inputLine;
    int lineNumber = 0;
    while ((inputLine = in.readLine()) != null) {
      if (lineNumber==100) {
        String textRSSI = inputLine.substring(17,inputLine.length());
        int RSSI = int(textRSSI);
        println(RSSI);
      }
      lineNumber++;
    }
    in.close();
  } 
  catch(IOException ee) {
  }
}

Here I’m grabbing just line 100 of the InputStream since that’s where the RSSI is, but if you print all the lines, you get a good deal of network information from your computer:

while ((inputLine = in.readLine()) != null) {
        println(inputLine);
    }
1 Like

please can you go back to my example and read the 2 lines:

    // RASPBERRY PI Terminal try: sudo iwlist wlan0 scan | egrep "Cell|ESSID|Signal"
    String command = "sudo iwlist wlan0 scan";

so first i get the full list in processing, as NO pipe grep in JAVA/processing

BUT that line select and string find show value pre / post a delimiter ?:?=?
i can do in processing easy

      if      ( line.contains("ESSID:") )         networkList.add(splitTokens(line, ":")[1]);
      else if ( line.contains("Signal level=") )  networkList.add(splitTokens(line, "=")[2]);

so you might try my code with your basic MAC params

"/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport", "-I"

and then adjust the search words AND delimiters if different from linux


last step ( if you get ONLY ) what you want in the array ) would be to disable the

      System.out.println(line);                   // see RAW

line

2 Likes

Fair enough, and it works although – at least on my machine – it works around the fact that some of the code is clearly broken – the grep and sed fail to return values, while the airport command prints like 90+ lines of unnecessary usage text, etc. This doesn’t happen if you run airport -I from the command line. The wanted value is down there on line 100, but any system update with the tiniest change to airport would make that not true.

Here is an alternative – because pipes work grep/sed actually filter correctly, it will always return only the value from the last line containing CtlRSSI, and it will always work as long as some line contains CtlRSSI.

// https://discourse.processing.org/t/get-wifi-rssi-on-mac/13942/4
// https://forum.processing.org/one/topic/running-bash-script-from-processing#25080000001056314.html
import java.io.InputStreamReader;
int RSSI = 0;
void setup() {
  String[] params = { sketchPath() + "/WiFiCheck.sh"};
  Process p = exec(params);
  try {
    int result = p.waitFor();
    BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
    String returnedValues;
    while ( (returnedValues = stdInput.readLine ()) != null) {
      RSSI = int(returnedValues);
    }
  }
  catch (InterruptedException e) {
    println("failed");
  }
  catch (IOException e) {
    println("failed");
  }
  println(RSSI);
}

/**
WiFiCheck.sh
#!/usr/bin/env bash
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I | grep CtlRSSI | sed -e 's/^.*://g' | xargs -I SIGNAL
*/

If you want a full airport report, don’t filter the lines, and instead dump them into a HashMap or a StringDict. Then you can retrieve whatever keys you want later.

// https://discourse.processing.org/t/get-wifi-rssi-on-mac/13942/4
// https://forum.processing.org/one/topic/running-bash-script-from-processing#25080000001056314.html
import java.io.InputStreamReader;
int RSSI = 0;
HashMap<String,String> report = new HashMap<String,String>();
void setup() {
  String[] params = { sketchPath() + "/WiFiCheck.sh"};
  Process p = exec(params);
  try {
    int result = p.waitFor();
    BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
    String returnedValues;
    while ( (returnedValues = stdInput.readLine ()) != null) {
      String[] terms = returnedValues.split(":");
      report.put(terms[0],terms[1]);
    }
  }
  catch (InterruptedException e) {
    println("failed");
  }
  catch (IOException e) {
    println("failed");
  }
  println(report);
  println(report.get("agrCtlRSSI"));
}

/**
WiFiCheck.sh
#!/usr/bin/env bash
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I | xargs -I SIGNAL
*/

Now you can look up the value for any key.

{link auth= wpa2-psk, agrCtlRSSI= -60, channel= 4, op mode= station lastTxRate, maxRate= 54, 802.11 auth= open, lastAssocStatus= 0, agrCtlNoise= -99, agrExtRSSI= 0, BSSID= 0, state= running, MCS= -1, SSID= NETGEAR1, agrExtNoise= 0}

-60

2 Likes

Awesome! Thanks a lot to both of you!