WiFiMon Hardware Probes (WHP) are used to gather performance measurements in a WiFi network from dedicated small form factor devices which are installed in fixed points. WiFiMon tested its operation and recommends the use of Raspberry Pi’s v3 Model B+ or v4. WiFiMon Hardware Probe will work in the following configuration:
There are two options for the WHP installation:
The following steps apply for both installation options. WiFiMon users who will use the prepared WHP image (installation option 1) should simply edit the crontab and wireless.py files as discussed in the following. WiFiMon users who will not use the prepared WIFiMon WHP image (installation option 2) should follow the steps 2 up to 4.
Follow the instructions at the official Raspberry Pi site. Skip the "Download the image" step and use the WiFiMon Raspberry Pi operating system image instead (download size is approx. 8 GB).
WiFiMon Raspberry Pi image given above is a custom version of Raspberry Pi OS (Buster) with desktop, with the default Raspberry Pi credentials (user: pi, password: raspberry).
We advise the user to always secure Raspberry Pi by changing the default password.
Follow the simple steps below:
You should see a red light on the Raspberry Pi and raspberries on the monitor. The WiFiMon Hardware Probe will boot up into a graphical desktop.
Secure the Raspberry Pi by changing the default password. Optionally, you may enable SSH to access the command line of a Raspberry Pi remotely or setup remote desktop. Next, you have to connect to the wireless network you want to measure.
First, the following programs should be downloaded:
sudo apt-get update sudo apt-get install -y xvfb firefox-esr |
The WiFiMon Hardware Probe (WHP) performs performance tests towards the WiFiMon Test Server (WTS) in an automated manner. It uses crontab to schedule the tests. To do that, open the terminal (as user "pi") and enter the command: crontab -e. You will have to pick the text editor. Then scroll to the bottom of the file and add the following code block (which you will modify as explained below):
00,10,20,30,40,50 * * * * Xvfb :100 & 02,12,22,32,42,52 * * * * export DISPLAY=:100 && firefox-esr --new-tab URL_TO_nettest.html >/dev/null 2>&1 04,14,24,34,44,54 * * * * export DISPLAY=:100 && firefox-esr --new-tab URL_TO_speedworker.html >/dev/null 2>&1 06,16,26,36,46,56 * * * * export DISPLAY=:100 && firefox-esr --new-tab URL_TO_boomerang.html >/dev/null 2>&1 08,18,28,38,48,58 * * * * /home/pi/wireless.py >> ~/cron.log 2>&1 |
You have to modify the following parts of the crontab in lines 2-4:
You should put the URL or IP address of the WTS in which the NetTest, speedtest and boomerang JS scripts are injected. Details about the configuration of the WiFiMon testtools are included in the WiFiMon Test Server (WTS) installation documentation. Following the assumptions/notations of the WTS guide, examples of the URLs for NetTest, speedtest and boomerang respectively are (i) https://WTS_FQDN/wifimon/measurements/nettest.html, (ii) https://WTS_FQDN/wifimon/measurements/speedworker.html and (iii) https://WTS_FQDN/wifimon/measurements/boomerang.html.
Line 5 of the crontab is related to the streaming of wireless network interface metrics to the WiFiMon Analysis Server (WAS). Optionally, the intervals of the WHP measurements could be altered by appropriately configuring the crontab so that measurement are more or less frequent. The configuration of the crontab config given above sets up 10-minute intervals between the measurements of each test tool in a way in which there are no overlapping measurements.
In /home/pi, you will find the Python script wireless.py. The contents of the script are the following:
#!/usr/bin/python3 import subprocess import datetime import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) import json def return_command_output(command): proc = subprocess.Popen(command, stdout = subprocess.PIPE, shell = True) (out, err) = proc.communicate() output = out.rstrip('\n'.encode('utf8')) return output def get_mac(iface): command = "cat /sys/class/net/" + str(iface) + "/address" mac = return_command_output(command).decode('utf8') mac = mac.replace(":", "-") return mac def find_wlan_iface_name(): command = "printf '%s\n' /sys/class/net/*/wireless | awk -F'/' '{print $5 }'" wlan_iface_name = return_command_output(command) return wlan_iface_name.decode('utf8') def parse_iwconfig(iface): bit_rate = return_command_output("sudo iwconfig " + iface + " | grep Bit | awk '{print $2}' | sed 's/Rate=//'").decode('utf8') tx_power = return_command_output("sudo iwconfig " + iface + " | grep Bit | awk '{print $4}' | sed 's/Tx-Power=//'").decode('utf8') link_quality = return_command_output("sudo iwconfig " + iface + " | grep Link | awk '{print $2}' | sed 's/Quality=//'").decode('utf8') link_quality = link_quality.split("/")[0] signal_level = return_command_output("sudo iwconfig " + iface + " | grep Link | awk '{print $4}' | sed 's/level=//'").decode('utf8') accesspoint = return_command_output("sudo iwconfig " + iface + " | grep Mode | awk '{print $6}' | sed 's/Point: //'").decode('utf8') accesspoint = accesspoint.replace(":", "-") essid = return_command_output("sudo iwconfig " + iface + " | grep ESSID | awk '{print $4}' | sed 's/ESSID://'").decode('utf8') essid = essid.replace("\"", "") return bit_rate, tx_power, link_quality, signal_level, accesspoint, essid def parse_iwlist(iface, accesspoint): information = {} command = "sudo iwlist " + iface + " scan | grep -E \"Cell|Quality|ESSID\"" aps = return_command_output(command).decode("utf8") aps = aps.split("\n") cell_indices = list() for index in range(0, len(aps)): line_no_whitespace = ' '.join(aps[index].split()) parts = line_no_whitespace.split() if parts[0] == "Cell": cell_indices.append(index) for index in cell_indices: line0 = ' '.join(aps[index].split()) ap_mac = line0.split()[-1] ap_mac = ap_mac.replace(":", "-") information[ap_mac] = {} line1 = ' '.join(aps[index + 1].split()) parts = line1.split() information[ap_mac]["drillTest"] = float(parts[2].split("=")[1]) line2 = ' '.join(aps[index + 2].split()) parts = line2.split(":") information[ap_mac][str(parts[1].replace('"', ''))] = information[ap_mac]["drillTest"] return information def convert_info_to_json(accesspoint, essid, mac, bit_rate, tx_power, link_quality, signal_level, probe_no, information, location_name, test_device_location_description, nat_network): overall_dictionary = {} overall_dictionary["macAddress"] = "\"" + str(mac) + "\"" overall_dictionary["accesspoint"] = "\"" + str(accesspoint) + "\"" overall_dictionary["essid"] = "\"" + str(essid) + "\"" bit_rate = int(float(bit_rate)) overall_dictionary["bitRate"] = str(bit_rate) tx_power = int(float(tx_power)) overall_dictionary["txPower"] = str(tx_power) link_quality = int(float(link_quality)) overall_dictionary["linkQuality"] = str(link_quality) signal_level = int(float(signal_level)) overall_dictionary["signalLevel"] = str(signal_level) overall_dictionary["probeNo"] = str(probe_no) information = json.dumps(information) overall_dictionary["monitor"] = information overall_dictionary["locationName"] = "\"" + str(location_name) + "\"" overall_dictionary["testDeviceLocationDescription"] = "\"" + str(test_device_location_description) + "\"" overall_dictionary["nat"] = "\"" + str(nat_network) + "\"" json_data = json.dumps(overall_dictionary) return json_data def stream_data(data): headers = {'content-type':"application/json"} try: session = requests.Session() session.verify = False session.post(url='https://WAS_FQDN:443/wifimon/probes/', data=data, headers=headers, timeout=30) except: pass def set_location_information(): location_name = "" test_device_location_description = "" nat_network = "" return location_name, test_device_location_description, nat_network def wireless_info(): location_name, test_device_location_description, nat_network = set_location_information() iface_name = find_wlan_iface_name() mac = get_mac(iface_name) bit_rate, tx_power, link_quality, signal_level, accesspoint, essid = parse_iwconfig(iface_name) information = parse_iwlist(iface_name, accesspoint) probe_no = "" json_data = convert_info_to_json(accesspoint, essid, mac, bit_rate, tx_power, link_quality, signal_level, probe_no, information, location_name, test_device_location_description, nat_network) stream_data(json_data) if __name__ == "__main__": wireless_info() |
The following values should be set:
That's all! At this point you may (optionally) unplug the keyboard, the mouse and the monitor and let the WHP measure the performance of your wireless network!
We suggest that you take additional efforts to safeguard the security of your probes:
set +o history wpa_passphrase YOUR_ESSID YOUR_PASSWORD set -o history |
A "psk=....." line will be generated. Add this line in /etc/wpa_supplicant/wpa_supplicant.conf under your ESSID and delete the plaintext password.
delgroup pi sudo rm /etc/sudoers.d/010_pi-nopasswd |