Node Red Sonoff RF Bridge Wifi Scanner
This post will cover how to use the Sonoff RF Bridge hardware as a wifi scanner and display its results in Node Red. Here is a high level view of what will be covered. The hardware is an IoT device intended as a gateway between 433 Mhz sensors and a wifi network. That purpose will be abandoned. Instead the device will be modified to allow it to be programmed using a serial to usb converter. With that, firmware will be created and loaded on the device. This firmware will define its new use. It will scan for wireless networks that broadcast within its range. The results will be stored in its memory. The device will connect to its wifi network and publish the results to a mqtt broker. Afterward it will disconnect and repeat this cycle until its powered off. Node Red will be used as a front end to display the scan results. It will subscribe to the mqtt broker. As data arrives from the broker, it will format the results and display them on the dashboard.
The hardware used is manufactured by Sonoff, a smart home automation development company headquartered in Shenzhen China. They are distributed through ITEAD Intelligent Systems, another entity in Shenzhen, along with other devices such as Nextion displays and Airspy SDR modules.
This post demonstrates how to modify and program the RF Bridge module, https://nerdiy.de/en/flashing-sonoff-433mhz-rf-bridge-with-tasmota-firmware-2/. The USB to serial connection and setting the device in flash mode will be used in this post. This will allow the device to be directly flashed with the Arduino IDE.
The following code was used.
/*
This sketch demonstrates how to scan WiFi networks.
The API is almost the same as with the WiFi Shield library,
the most obvious difference being the different file you need to include:
See, https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/scan-class.html
Specifics on flashing the Sonoff Bridge Modules
See, https://www.youtube.com/watch?v=XixXbg2T4Ns
*/
// Libraries and Declarations
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
WiFiClient espClient;
PubSubClient MQTTclient(espClient);
// Variables
String ssid;
int32_t rssi;
uint8_t encryptionType;
uint8_t *bssid;
int32_t channel;
bool hidden;
int scanResult;
String StringScanCount;
String StringScanRSSI;
String StringScanSSID;
String channelString;
String MacAddressString;
String EncyrptionString;
// MQTT Broker
const char* mqtt_server = "qwerty"; // Put your MQTT Broker here
// Your WiFi credentials
const char* myssid = "qwerty"; // Put your SSID here
const char* mypassword = "qwerty"; // Put your PASSWORD here
// Type of WiFi encryption
String encryptionTypeStr(uint8_t authmode) {
switch (authmode) {
case ENC_TYPE_NONE:
return "Open";
case ENC_TYPE_WEP:
return "WEP";
case ENC_TYPE_TKIP:
return "WPA+PSK";
case ENC_TYPE_CCMP:
return "WPA2+PSK";
case ENC_TYPE_AUTO:
return "WPA+WPA2+PSK";
default:
return "Uknown";;
}
}
// MQTT Functions
void callback(char* topic, byte* message, unsigned int length) {
String messageTemp;
for (int i = 0; i < length; i++) {
messageTemp += (char)message[i];
}
if (String(topic) == "Sonoff-RF-Bridge/Reboot") {
if(messageTemp == "Reboot"){
ESP.restart();
}
}
}
void reconnect() {
// Loop until we're reconnected
while (!MQTTclient.connected()) {
// Attempt to connect
if (MQTTclient.connect("Sonoff-RF-Bridge")) {
// Subscribe
// Do you not subscribe to my methods?
// Sonoff-RF-Bridge/# for everything, or Sonoff-RF-Bridge/Uptime for just the Uptime
MQTTclient.subscribe("Sonoff-RF-Bridge/#");
} else {
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void UpdateStats() {
delay(250);
MQTTclient.publish("Sonoff-RF-Bridge/Firmware", "Sonoff-RF-Bridge_ver1");
String StringUptime = String(millis());
MQTTclient.publish("Sonoff-RF-Bridge/Uptime", StringUptime.c_str());
String StringHWAddress = String(WiFi.macAddress());
MQTTclient.publish("Sonoff-RF-Bridge/HWAddress", StringHWAddress.c_str());
String StringWifiSignal = String(WiFi.RSSI());
MQTTclient.publish("Sonoff-RF-Bridge/WifiSignal",StringWifiSignal.c_str());
String StringFreeHeapSize = String(ESP.getFreeHeap());
MQTTclient.publish("Sonoff-RF-Bridge/FreeHeapSize",StringFreeHeapSize.c_str());
String StringHeapFragmentation = String(ESP.getHeapFragmentation());
MQTTclient.publish("Sonoff-RF-Bridge/HeapFragmentation",StringHeapFragmentation.c_str());
String StringMaxFreeBlockSize = String(ESP.getMaxFreeBlockSize());
MQTTclient.publish("Sonoff-RF-Bridge/MaxFreeBlockSize",StringMaxFreeBlockSize.c_str());
String StringSketchSize = String(ESP.getSketchSize());
MQTTclient.publish("Sonoff-RF-Bridge/SketchSize",StringSketchSize.c_str());
String StringFreeSketchSpace = String(ESP.getFreeSketchSpace());
MQTTclient.publish("Sonoff-RF-Bridge/FreeSketchSpace",StringFreeSketchSpace.c_str());
String StringCpuFreqMHz = String(ESP.getCpuFreqMHz());
MQTTclient.publish("Sonoff-RF-Bridge/CpuFreqMHz",StringCpuFreqMHz.c_str());
String StringChipId = String(ESP.getChipId());
MQTTclient.publish("Sonoff-RF-Bridge/ChipId",StringChipId.c_str());
String StringscanResult = String(scanResult);
MQTTclient.publish("Sonoff-RF-Bridge/Networks-Found",StringscanResult.c_str());
String StringSpacer = String(" ");
// Print unsorted scan results
for (int8_t i = 0; i < scanResult; i++) {
WiFi.getNetworkInfo(i, ssid, encryptionType, rssi, bssid, channel, hidden);
StringScanCount = String(i + 1);
StringScanRSSI = String(rssi);
if(ssid != NULL) {
StringScanSSID = String(ssid);
}
else {
StringScanSSID = String("*-Hidden-*");
}
channelString = String(channel);
EncyrptionString = String((String) encryptionTypeStr(WiFi.encryptionType(i)));
// uint8_t* mac=WiFi.BSSID(i);
uint8_t* mac = bssid;
char APmac[13];
APmac[0]=0;
for (int macadd=0; macadd<6;macadd++) {
char buffer[3];
sprintf(buffer, "%02x", mac[macadd]);
strncpy((char *)(APmac+(macadd*2)), buffer, 3);
}
MacAddressString = String(WiFi.BSSIDstr(i));
String StringScanAPFinal = String(StringScanCount + StringSpacer + StringScanRSSI + StringSpacer + MacAddressString + StringSpacer + channelString + StringSpacer + StringScanSSID + StringSpacer + EncyrptionString);
MQTTclient.publish("Sonoff-RF-Bridge/AP-Found",StringScanAPFinal.c_str());
// Just wait it out
delay(250);
}
}
void WifiConnect() {
WiFi.begin(myssid, mypassword);
while (WiFi.status() != WL_CONNECTED)
{
// Just wait it out
delay(250);
}
MQTTclient.setServer(mqtt_server, 1883);
MQTTclient.setCallback(callback);
}
void WifiDisConnect() {
// Disconnect from an AP if it was previously connected
WiFi.disconnect();
delay(100);
}
void CheckForNetworks() {
// Set WiFi to station mode
WiFi.mode(WIFI_STA);
WifiDisConnect();
scanResult = WiFi.scanNetworks(/*async=*/false, /*hidden=*/true);
if (scanResult == 0) {
}
else {
}
// Wait a bit before scanning again
delay(100);
WifiConnect();
if (!MQTTclient.connected()) {
reconnect();
}
MQTTclient.loop();
UpdateStats();
WifiDisConnect();
}
void setup() {
}
void loop() {
CheckForNetworks();
delay(30000);
}
With a usable version installed on the device, it should start scan and publish its results. These results can now be processed with Node Red along with other information about the device.
Here are the Dashboard nodes used in Node Red for this example.
Text Node: Title of widget
Level Node: Number of APs Found with color bar
Text Node: Uptime
Text Node: Running Firmware Version
Text Node: Wifi Signal Strength
Text Node: Device Mac Address
Text Node: Date
Text Node: Time
Template Node: Message Stacker Processor
The Function node used is the heart of how the data stream from the broker is processed. Here is the code used.
// Time threshold to identify a new burst (in milliseconds)
const BURST_THRESHOLD = 5000; // 5 seconds
// Get the last message timestamp and messages from flow context
var lastMessageTimeDevID = flow.get('lastMessageTime') || 0;
var currentTimeDevID = new Date().getTime();
var messagesDevID = flow.get('messages') || '';
// If time since last message exceeds threshold, consider it a new burst
if (currentTimeDevID - lastMessageTimeDevID > BURST_THRESHOLD) {
messagesDevID = ''; // Clear the messages buffer for new burst
}
// Add new message to buffer
messagesDevID += msg.payload + "\n";
// Update flow context
flow.set('lastMessageTime', currentTimeDevID);
flow.set('messages', messagesDevID);
// Set message payload
msg.payload = messagesDevID;
return msg;
This is fed to the Dashboard Template Node that uses the following code.
<style>
#myText {
height: 200px;
overflow-y: scroll;
background-color: #242424;
color: #d29200;
font-size: 12px;
font-family: monospace;
white-space: pre-wrap;
}
</style>
<div id="container">
<div id="myText">{{msg.payload}}</div>
</div>
<script>
var container = document.getElementById("container");
container.scrollTop = container.scrollHeight;
// Whenever new data is added to the container, scroll to the bottom
container.addEventListener("DOMNodeInserted", function () {
container.scrollTop = container.scrollHeight;
});
</script>
This makes a simple at a glance dashboard to view all of the WiFi access points that broadcast their SSIDs.

