{"id":5013,"date":"2025-10-28T08:00:19","date_gmt":"2025-10-28T15:00:19","guid":{"rendered":"https:\/\/www.cloudacm.com\/?p=5013"},"modified":"2025-10-24T07:19:28","modified_gmt":"2025-10-24T14:19:28","slug":"node-red-sonoff-rf-bridge-wifi-scanner","status":"publish","type":"post","link":"https:\/\/www.cloudacm.com\/?p=5013","title":{"rendered":"Node Red Sonoff RF Bridge Wifi Scanner"},"content":{"rendered":"<p>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.<\/p>\n<p>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.<\/p>\n<p><iframe loading=\"lazy\" title=\"Unveiling the Future of Smart Living with SONOFF\" width=\"640\" height=\"360\" src=\"https:\/\/www.youtube.com\/embed\/xZ2t-d7N-6o?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n<p>This post demonstrates how to modify and program the RF Bridge module, <a href=\"https:\/\/nerdiy.de\/en\/flashing-sonoff-433mhz-rf-bridge-with-tasmota-firmware-2\/\">https:\/\/nerdiy.de\/en\/flashing-sonoff-433mhz-rf-bridge-with-tasmota-firmware-2\/<\/a>. 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.<\/p>\n<p>The following code was used.<\/p>\n<pre>\/*\r\nThis sketch demonstrates how to scan WiFi networks.\r\nThe API is almost the same as with the WiFi Shield library,\r\nthe most obvious difference being the different file you need to include:\r\n\r\nSee, https:\/\/arduino-esp8266.readthedocs.io\/en\/latest\/esp8266wifi\/scan-class.html\r\n\r\nSpecifics on flashing the Sonoff Bridge Modules\r\nSee, https:\/\/www.youtube.com\/watch?v=XixXbg2T4Ns\r\n*\/\r\n\r\n\/\/ Libraries and Declarations\r\n#include &lt;Arduino.h&gt;\r\n#include &lt;ESP8266WiFi.h&gt;\r\n#include &lt;PubSubClient.h&gt;\r\nWiFiClient espClient;\r\nPubSubClient MQTTclient(espClient);\r\n\r\n\/\/ Variables\r\nString ssid;\r\nint32_t rssi;\r\nuint8_t encryptionType;\r\nuint8_t *bssid;\r\nint32_t channel;\r\nbool hidden;\r\nint scanResult;\r\n\r\nString StringScanCount;\r\nString StringScanRSSI;\r\nString StringScanSSID;\r\nString channelString;\r\nString MacAddressString;\r\nString EncyrptionString;\r\n\r\n\/\/ MQTT Broker\r\nconst char* mqtt_server = \"qwerty\"; \/\/ Put your MQTT Broker here\r\n\r\n\/\/ Your WiFi credentials\r\nconst char* myssid = \"qwerty\"; \/\/ Put your SSID here\r\nconst char* mypassword = \"qwerty\"; \/\/ Put your PASSWORD here\r\n\r\n\/\/ Type of WiFi encryption\r\nString encryptionTypeStr(uint8_t authmode) {\r\nswitch (authmode) {\r\ncase ENC_TYPE_NONE:\r\nreturn \"Open\";\r\ncase ENC_TYPE_WEP:\r\nreturn \"WEP\";\r\ncase ENC_TYPE_TKIP:\r\nreturn \"WPA+PSK\";\r\ncase ENC_TYPE_CCMP:\r\nreturn \"WPA2+PSK\";\r\ncase ENC_TYPE_AUTO:\r\nreturn \"WPA+WPA2+PSK\";\r\ndefault:\r\nreturn \"Uknown\";;\r\n}\r\n}\r\n\r\n\/\/ MQTT Functions\r\nvoid callback(char* topic, byte* message, unsigned int length) {\r\nString messageTemp;\r\nfor (int i = 0; i &lt; length; i++) {\r\nmessageTemp += (char)message[i];\r\n}\r\nif (String(topic) == \"Sonoff-RF-Bridge\/Reboot\") {\r\nif(messageTemp == \"Reboot\"){\r\nESP.restart();\r\n}\r\n}\r\n}\r\n\r\nvoid reconnect() {\r\n\/\/ Loop until we're reconnected\r\nwhile (!MQTTclient.connected()) {\r\n\/\/ Attempt to connect\r\nif (MQTTclient.connect(\"Sonoff-RF-Bridge\")) {\r\n\/\/ Subscribe\r\n\/\/ Do you not subscribe to my methods?\r\n\/\/ Sonoff-RF-Bridge\/# for everything, or Sonoff-RF-Bridge\/Uptime for just the Uptime\r\nMQTTclient.subscribe(\"Sonoff-RF-Bridge\/#\");\r\n} else {\r\n\/\/ Wait 5 seconds before retrying\r\ndelay(5000);\r\n}\r\n}\r\n}\r\n\r\nvoid UpdateStats() {\r\n\r\ndelay(250);\r\n\r\nMQTTclient.publish(\"Sonoff-RF-Bridge\/Firmware\", \"Sonoff-RF-Bridge_ver1\"); \r\nString StringUptime = String(millis());\r\nMQTTclient.publish(\"Sonoff-RF-Bridge\/Uptime\", StringUptime.c_str());\r\nString StringHWAddress = String(WiFi.macAddress());\r\nMQTTclient.publish(\"Sonoff-RF-Bridge\/HWAddress\", StringHWAddress.c_str()); \r\nString StringWifiSignal = String(WiFi.RSSI());\r\nMQTTclient.publish(\"Sonoff-RF-Bridge\/WifiSignal\",StringWifiSignal.c_str()); \r\n\r\nString StringFreeHeapSize = String(ESP.getFreeHeap());\r\nMQTTclient.publish(\"Sonoff-RF-Bridge\/FreeHeapSize\",StringFreeHeapSize.c_str()); \r\nString StringHeapFragmentation = String(ESP.getHeapFragmentation());\r\nMQTTclient.publish(\"Sonoff-RF-Bridge\/HeapFragmentation\",StringHeapFragmentation.c_str()); \r\nString StringMaxFreeBlockSize = String(ESP.getMaxFreeBlockSize());\r\nMQTTclient.publish(\"Sonoff-RF-Bridge\/MaxFreeBlockSize\",StringMaxFreeBlockSize.c_str()); \r\nString StringSketchSize = String(ESP.getSketchSize());\r\nMQTTclient.publish(\"Sonoff-RF-Bridge\/SketchSize\",StringSketchSize.c_str()); \r\nString StringFreeSketchSpace = String(ESP.getFreeSketchSpace());\r\nMQTTclient.publish(\"Sonoff-RF-Bridge\/FreeSketchSpace\",StringFreeSketchSpace.c_str()); \r\nString StringCpuFreqMHz = String(ESP.getCpuFreqMHz());\r\nMQTTclient.publish(\"Sonoff-RF-Bridge\/CpuFreqMHz\",StringCpuFreqMHz.c_str());\r\nString StringChipId = String(ESP.getChipId());\r\nMQTTclient.publish(\"Sonoff-RF-Bridge\/ChipId\",StringChipId.c_str());\r\nString StringscanResult = String(scanResult);\r\nMQTTclient.publish(\"Sonoff-RF-Bridge\/Networks-Found\",StringscanResult.c_str());\r\nString StringSpacer = String(\" \");\r\n\r\n\/\/ Print unsorted scan results\r\nfor (int8_t i = 0; i &lt; scanResult; i++) {\r\nWiFi.getNetworkInfo(i, ssid, encryptionType, rssi, bssid, channel, hidden);\r\n\r\nStringScanCount = String(i + 1);\r\nStringScanRSSI = String(rssi);\r\nif(ssid != NULL) {\r\nStringScanSSID = String(ssid);\r\n}\r\nelse {\r\nStringScanSSID = String(\"*-Hidden-*\");\r\n}\r\n\r\nchannelString = String(channel);\r\nEncyrptionString = String((String) encryptionTypeStr(WiFi.encryptionType(i)));\r\n\/\/ uint8_t* mac=WiFi.BSSID(i);\r\nuint8_t* mac = bssid;\r\nchar APmac[13];\r\nAPmac[0]=0;\r\nfor (int macadd=0; macadd&lt;6;macadd++) {\r\nchar buffer[3];\r\nsprintf(buffer, \"%02x\", mac[macadd]);\r\nstrncpy((char *)(APmac+(macadd*2)), buffer, 3);\r\n}\r\nMacAddressString = String(WiFi.BSSIDstr(i));\r\nString StringScanAPFinal = String(StringScanCount + StringSpacer + StringScanRSSI + StringSpacer + MacAddressString + StringSpacer + channelString + StringSpacer + StringScanSSID + StringSpacer + EncyrptionString);\r\nMQTTclient.publish(\"Sonoff-RF-Bridge\/AP-Found\",StringScanAPFinal.c_str());\r\n\r\n\/\/ Just wait it out\r\ndelay(250);\r\n}\r\n}\r\n\r\nvoid WifiConnect() {\r\nWiFi.begin(myssid, mypassword);\r\nwhile (WiFi.status() != WL_CONNECTED)\r\n{\r\n\/\/ Just wait it out\r\ndelay(250);\r\n}\r\nMQTTclient.setServer(mqtt_server, 1883);\r\nMQTTclient.setCallback(callback);\r\n}\r\n\r\nvoid WifiDisConnect() {\r\n\/\/ Disconnect from an AP if it was previously connected\r\nWiFi.disconnect();\r\ndelay(100);\r\n}\r\n\r\nvoid CheckForNetworks() {\r\n\/\/ Set WiFi to station mode\r\nWiFi.mode(WIFI_STA);\r\n\r\nWifiDisConnect();\r\nscanResult = WiFi.scanNetworks(\/*async=*\/false, \/*hidden=*\/true);\r\nif (scanResult == 0) {\r\n}\r\nelse {\r\n}\r\n\r\n\/\/ Wait a bit before scanning again\r\ndelay(100);\r\nWifiConnect();\r\nif (!MQTTclient.connected()) {\r\nreconnect();\r\n} \r\nMQTTclient.loop();\r\nUpdateStats();\r\nWifiDisConnect();\r\n}\r\n\r\nvoid setup() {\r\n}\r\n\r\nvoid loop() {\r\nCheckForNetworks();\r\ndelay(30000);\r\n}<\/pre>\n<p>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.<\/p>\n<p><a href=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/10\/Screenshot-from-2025-10-14-05-25-00.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5018\" src=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/10\/Screenshot-from-2025-10-14-05-25-00.png\" alt=\"\" width=\"302\" height=\"468\" srcset=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/10\/Screenshot-from-2025-10-14-05-25-00.png 302w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/10\/Screenshot-from-2025-10-14-05-25-00-194x300.png 194w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/10\/Screenshot-from-2025-10-14-05-25-00-174x270.png 174w\" sizes=\"auto, (max-width: 302px) 100vw, 302px\" \/><\/a><\/p>\n<p>Here are the Dashboard nodes used in Node Red for this example.<\/p>\n<p>Text Node: Title of widget<br \/>\nLevel Node: Number of APs Found with color bar<br \/>\nText Node: Uptime<br \/>\nText Node: Running Firmware Version<br \/>\nText Node: Wifi Signal Strength<br \/>\nText Node: Device Mac Address<br \/>\nText Node: Date<br \/>\nText Node: Time<br \/>\nTemplate Node: Message Stacker Processor<\/p>\n<p><a href=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/10\/Screenshot-from-2025-10-24-06-48-31.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-5019 size-full\" src=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/10\/Screenshot-from-2025-10-24-06-48-31.png\" alt=\"\" width=\"1099\" height=\"323\" srcset=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/10\/Screenshot-from-2025-10-24-06-48-31.png 1099w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/10\/Screenshot-from-2025-10-24-06-48-31-300x88.png 300w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/10\/Screenshot-from-2025-10-24-06-48-31-1024x301.png 1024w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/10\/Screenshot-from-2025-10-24-06-48-31-768x226.png 768w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/10\/Screenshot-from-2025-10-24-06-48-31-604x178.png 604w\" sizes=\"auto, (max-width: 1099px) 100vw, 1099px\" \/><\/a><\/p>\n<p>The Function node used is the heart of how the data stream from the broker is processed. Here is the code used.<\/p>\n<pre>\/\/ Time threshold to identify a new burst (in milliseconds)\r\nconst BURST_THRESHOLD = 5000; \/\/ 5 seconds\r\n\r\n\/\/ Get the last message timestamp and messages from flow context\r\nvar lastMessageTimeDevID = flow.get('lastMessageTime') || 0;\r\nvar currentTimeDevID = new Date().getTime();\r\nvar messagesDevID = flow.get('messages') || '';\r\n\r\n\/\/ If time since last message exceeds threshold, consider it a new burst\r\nif (currentTimeDevID - lastMessageTimeDevID &gt; BURST_THRESHOLD) {\r\nmessagesDevID = ''; \/\/ Clear the messages buffer for new burst\r\n}\r\n\r\n\/\/ Add new message to buffer\r\nmessagesDevID += msg.payload + \"\\n\";\r\n\r\n\/\/ Update flow context\r\nflow.set('lastMessageTime', currentTimeDevID);\r\nflow.set('messages', messagesDevID);\r\n\r\n\/\/ Set message payload\r\nmsg.payload = messagesDevID;\r\n\r\nreturn msg;<\/pre>\n<p>This is fed to the Dashboard Template Node that uses the following code.<\/p>\n<pre>&lt;style&gt;\r\n#myText {\r\nheight: 200px;\r\noverflow-y: scroll;\r\nbackground-color: #242424;\r\ncolor: #d29200;\r\nfont-size: 12px;\r\nfont-family: monospace;\r\nwhite-space: pre-wrap;\r\n}\r\n&lt;\/style&gt;\r\n\r\n&lt;div id=\"container\"&gt;\r\n&lt;div id=\"myText\"&gt;{{msg.payload}}&lt;\/div&gt;\r\n&lt;\/div&gt;\r\n\r\n&lt;script&gt;\r\nvar container = document.getElementById(\"container\");\r\ncontainer.scrollTop = container.scrollHeight;\r\n\r\n\/\/ Whenever new data is added to the container, scroll to the bottom\r\ncontainer.addEventListener(\"DOMNodeInserted\", function () {\r\ncontainer.scrollTop = container.scrollHeight;\r\n});\r\n&lt;\/script&gt;<\/pre>\n<p>This makes a simple at a glance dashboard to view all of the WiFi access points that broadcast their SSIDs.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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&#8230;<\/p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https:\/\/www.cloudacm.com\/?p=5013\"> Read More<span class=\"screen-reader-text\">  Read More<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-5013","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts\/5013","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=5013"}],"version-history":[{"count":8,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts\/5013\/revisions"}],"predecessor-version":[{"id":5015,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts\/5013\/revisions\/5015"}],"wp:attachment":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5013"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5013"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5013"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}