Convert WiFi RSSI to Signal Bars (Formula and Code Snippet)
I went way down the rabbit hole trying to convert RSSI (in dBm) to a useful signal strength meter, like you’d see on your cell phone.
I read many articles, but no one really had an algorithm. So….here is an algorithm written in Javascript.
The Goods
//This assumes you have a way to obtain rssi... in my application,
// this is the rssi for the web server's connection (IoT device).
function rssiToBars( rssi , maxbars ){
var ret=rssi+80; //Useful range is -80 (bad) to -30 (good).
//+50 converts this to a useful range of 0 (bad) to 50 (good)
ret=(ret< 0)?0:rssi; //Clamping
ret=(ret>50)?50:rssi;
ret=parseInt(ret*maxbars/50); //taking a percent (rssi/50) of maxbars
// If you are using a language with integer data types, make sure you multiply
// first, then divide.
return(ret);
}
function rssiMeter( bars , maxbars ){
return("<PRE>[" + "#".reapeat(bars) + "-".repeat(maxbars-bars) + "]</PRE>");
//This is a simple example, but you could stack a couple of small PNG or
//render a table where the cells are bars.
}
const maxbars=8
var rssi=-55; //some magical way to get the rssi
var bars=rssiToBars(rssi,maxbars);
var meter=rssiMeter( bars , maxbars);
// rssi == -55
// bars == 4
// meter == <PRE>[####---]</PRE>
How It Works
Received Signal Strength Indicator (RSSI) is the relative signal strength of a transmitter, from the receiver’s perspective.
RSSI is measured in dBm, or Decibel-milliWatts. Basically, this is milliWatts as perceived by the receiver, but represented on a logarithmic scale.
Because power drops inversely with the square of the distance, a logarithmic scale more or less represents this as a linear scale, rather than exponential.
For example, if you have signal strength of -30 (very good) and you walk 50 feet away with no obstacles between you and the router, you might drop to -40 dBm. If you walk another 50 feet, you would expect the signal to drop to -50 dBm. This a super-simple example, and there are a lot of other factors involved, but you get the basic idea.
The algorithm works like this:
- Convert rssi to a useful scale. We do this by adding 80. If we start with a range of -30 (good) to -80 (bad) and add 30, we get 0 (good) to -50 (bad). Further if we add 50, we get a positive integer that is now reversed: -50+50==0 (bad) and 0+50==50 (good).
- Next, we clamp the values to prevent unexpected results, ensuring we now have an integer in the range of 0..50.
- Dividing this number by 50 yields a percentage (0..1)
- Multiplying the percentage by the max number of bars, gives you the number of bars. For example, .5 * 8 (medium signal strength * 8 bars) == 4.
- If using a typed language such as c++, be sure to multiply first, THEN divide: (rssi+50)*maxbars/50. If not, the CPU will perform int math, and bars will be zero most of the time.
Rendering can be done however you want. One slick trick is to use CSS and a small table, where each cell is a bar, and CSS dictates the cell color. Your code sets the class of each cell to “bar” or “nobar”.
<STYLE>
.bar , .nobar {
height: 10px;
width: 10px;
border: 1px solid gray;
border-collapse:collaps;
font-size: 1px;
}
.bar{
background-color: #0000FF //Blue
}
.nobar{
background-color: #000022 //Dark Blue
}
</STYLE>
<SCRIPT>
function rssiMeter( bars , maxbars ){
var meter="<TABLE STYLE='border-collapse:collapse;'><TR>";
for(var i=0 ; i<maxbars ; i++){
var class=(i<bars)?"bar":"nobar";
meter+="<TD CLASS="+class+"> ";
//NO I DO NOT CLOSE MY TD TAGS. SUE ME
}
meter+="</TR></TABLE>";
return(meter);
}
// you can attach it to a DIV like this:
const maxbars=8; // define constants at the top of your script
const divname="meter";
function updateMeter(){
var rssi = somenumber;
var bars = rssiToBars(rssi , maxbars); //maxbars is a global const
var meter= rssiMeter( bars , maxbars);
document.getElementById(divname).innerHTML=meter; //divname is a global const
}
// be sure there is <DIV ID=meter> </DIV>
// somewhere in your document
//Now, whenever you want to update your meter:
updateMeter();
</SCRIPT>
Enjoy!