Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add WifiScan and WifiTest commands #16141

Merged

Conversation

ascillato
Copy link
Contributor

@ascillato ascillato commented Aug 3, 2022

Description:

This PR adds 2 new commands called WifiScan and WifiTest which are aimed for phone apps to help in the initial configuration of Tasmota and to make more user-friendly apps. WifiScan will scan for the Wi-Fi Networks that a Tasmota device can reach and will return the ordered list. WifiTest can be used to test if the credentials given by the user are correct and that the device can connect to a network. Both support HTTP call and polling.

On Android you can make the app to search for Wi-Fi Networks, but on iPhone you can't. On iPhone, you have to minimize the app and go to configuration. What Apple has is to connect to a known AP with known SSId and Password. That can be preconfigured in Tasmota, but if you have all your devices with the same SSId and Password, that could make conflicts in case of having several devices. https://developer.apple.com/forums/thread/39633 https://developer.apple.com/library/archive/qa/qa1942/_index.html Another approach on iPhone is to use the api NEHotspotConfiguration, which makes a pop-up asking if the user wants to connect to a predefined network without exiting the app, but that is still outside the app.

So, one solution is to have everything inside the app by offloading this limitation to the ESP8266 device. In short, to make Tasmota search for available networks and report back to the App. With this approach, the user will see on the app exactly which networks Tasmota can reach and how much signal they have. The procedure would be that the App sends the commands to Tasmota by HTTP and then polls for its status and responses (this is because HTTP commands have immediate responses).

Besides that, as these commands can be used also from console and from MQTT, they are useful to check which other networks are being saw by your installed devices and which possible interferences they have. Also, these commands are the base for an upcoming PR with a new command to mass configure all your Tasmota preflashed devices at once (in short, to configure only one device and to instruct that one to connect and configure the rest of installed devices in range).

WifiScan

WifiScan 1 will start a network scan. The results can be seen in the console and by MQTT. In the case of HTTP, you can ask Tasmota for the result by command Wifiscan. The results will remain in RAM for 1 minute after the scan has finished.

To start an scan, it must be used WifiScan 1:
http://192.168.4.1/cm?cmnd=WifiScan%201

Tasmota should answer:
{"WifiScan":"Scanning"}

(a second later or so) it can be polled for the result by command WifiScan:
http://192.168.4.1/cm?cmnd=WifiScan

Tasmota should answer:

{"WiFiScan":{"NET1":{"SSId":"WirelessNet", "BSSId":"D6:35:1D:81:DE:60", "Channel":"1", "Signal":"-31", "RSSI":"100", "Encryption":"WPA2/PSK"}, "NET2":{"SSId":"House of Choy", "BSSId":"FC:2B:B2:CA:68:42", "Channel":"6", "Signal":"-31", "RSSI":"100", "Encryption":"WPA2/PSK"}, "NET3":{"SSId":"TELUS0671", "BSSId":"FC:2B:B2:AD:1E:62", "Channel":"11", "Signal":"-52", "RSSI":"96", "Encryption":"WPA2/PSK"}}}

that is a JSON ordered by RSSI

If a scan is being performed and Tasmota is instructed to initiate a new scan (WifiScan 1), it will answer {"WifiScan":"Busy"}.
If it is asked the result (WifiScan) before Tasmota has finished, it will answer {"WifiScan":"Scanning"}.
If it is asked the result (WifiScan) but an scan was not initiated or was initiated more than a minute ago, it will answer {"WifiScan":"Not Started"}.
A scan can be triggered at any time and in any mode (AP, STA or AP+STA).

WifiTest

WifiTest mySSID+myPASSWORD can be used to test if that SSId and Password are correct so as to connect to a Network. In the present PR, this command is available only when Tasmota is in AP mode. In an upcoming PR, this can be expanded to STA mode. This command uses the dual mode of ESP devices which let them do both things, be in AP mode connected to a Phone AND also connect and test the router.

Note: The separator of SSID and Password is a + symbol since is not allowed as SSId according to https://www.cisco.com/assets/sol/sb/WAP321_Emulators/WAP321_Emulator_v1.0.0.3/help/Wireless05.html

To start a WiFi Test, it must be sent:
http://192.168.4.1/cm?cmnd=WifiTest%20mySSID%2BmyPASSWORD
that is the command: WifiTest mySSID+myPASSWORD

Tasmota should answer:
{"WifiTest":"Testing"}

(a second later or so) it can be polled for the result by command WifiTest:
http://192.168.4.1/cm?cmnd=WifiTest

Tasmota should answer:
{"WifiTest":"Successful"}

Or

{"WifiTest":"Connect failed"}
{"WifiTest":"Connect failed as no IP address received"}
{"WifiTest":"Connect failed as AP cannot be reached"}
{"WifiTest":"Connect failed with AP timeout"}

If a test is being performed and Tasmota is instructed to initiate a new test, it will answer {"WifiTest":"Busy"}.
If it is asked the result (WifiTest) before Tasmota has finished, it will answer {"WifiTest":"Testing"}.
If it is asked the result (WifiTest) but a test was not initiated, it will answer {"WifiTest":"Not Started"}.

If the connection was successful Tasmota will save the SSID and PASSWORD, and if no other configuration is required to be sent to Tasmota, the APP should tell Tasmota to restart in order to connect to the new WiFi Network. If no command is received in 3 minutes, Tasmota will shutdown the AP mode restart in STA mode connecting to the recently configured network.

For restarting, the App should send:
http://192.168.4.1/cm?cmnd=restart%201

This extra step is useful in order to let the app KNOW that the connection was successful and be able to move to the next step.

The full syntax of this command is:

WifiTest0 SSID+PASSWORD will test and if successful will save the credentials on the SSID Slot number 1 of Tasmota and will restart.

WifiTest SSID+PASSWORD or WifiTest1 SSID+PASSWORD will test and if successful will save the credentials on the SSID Slot number 1 of Tasmota without a restart.

WifiTest2 SSID+PASSWORD will test and if successful will save the credentials on the SSID Slot number 2 of Tasmota and without a restart.

Resources Cost for Tasmota Version 12.0.2.4

  • RAM: +170 Bytes
  • Flash: +2.412 Bytes

Notes:

  • The wifi struct definition was moved to the header file tasmota_globals.h in order to be accessible from other functions outside support_wifi.ino file. This was needed due to Tasmota is using .ino files instead of .cpp + .h files and the linker is compiling the functions in the alphabetical order of the files that contain them. This sometimes led to the problem that not all functions are accessible from other parts of the project.
  • The declaration of variables related to Wifi Network Scanning were moved from webserver file to the header file tasmota_globals.h
  • Just in case if needed, to encode or decode an URL to have those %20 characters, you can use https://meyerweb.com/eric/tools/dencoder/ for testing.

Related issue (if applicable): NA

Checklist:

  • The pull request is done against the latest development branch
  • Only relevant files were touched
  • Only one feature/fix was added per PR and the code change compiles without warnings
  • The code change is tested and works with Tasmota core ESP8266 V.2.7.4.9
  • The code change is tested and works with Tasmota core ESP32 V.2.0.4.1
  • I accept the CLA.

NOTE: The code change must pass CI tests. Your PR cannot be merged unless tests pass

@barbudor
Copy link
Contributor

barbudor commented Aug 3, 2022

Hi @ascillato
Nice
But why using a + as a separator instead of a space (escaped as %20) ?

@barbudor
Copy link
Contributor

barbudor commented Aug 3, 2022

Oups, my bad, you can have spaces in the SSID (and maybe the password too)

@ascillato
Copy link
Contributor Author

ascillato commented Aug 3, 2022

Yes, exactly. The comma and the spaces are accepted for SSId, but the + is not, so that is why, it is useful as separator here. The supported characters for SSId according to the standard are explained at https://www.cisco.com/assets/sol/sb/WAP321_Emulators/WAP321_Emulator_v1.0.0.3/help/Wireless05.html

@arendst arendst merged commit ebf0ad5 into arendst:development Aug 6, 2022
@ascillato ascillato deleted the Add_WifiScan_and_WifiTest_cmnds branch August 6, 2022 18:51
@Jason2866
Copy link
Collaborator

Jason2866 commented Aug 7, 2022

@ascillato Hi, nice new commands! Tested and encountered a little bug. If a BSSID supports WPA2 and WPA3 there is wrong shown OPEN. The WPA2 only shows 3 and the WPA3 only shows 6

My test scan (ESP32)

CMD: wifiscan 1
14:15:33.229 MQT: stat/sonoff-952728/RESULT = {"WiFiScan":"Scanning"}
14:15:41.332 MQT: stat/sonoff-952728/RESULT = {"WiFiScan":{"NET1":{"SSId":"Jason_Home_WLAN", "BSSId":"00:A0:57:2A:BD:19", "Channel":"9", "Signal":"-50", "RSSI":"100", "Encryption":"OPEN"}}}
14:15:41.343 MQT: stat/sonoff-952728/RESULT = {"WiFiScan":{"NET2":{"SSId":"TestWPA3", "BSSId":"02:A0:57:2A:BD:19", "Channel":"9", "Signal":"-50", "RSSI":"100", "Encryption":"6"}}}
14:15:41.350 MQT: stat/sonoff-952728/RESULT = {"WiFiScan":{"NET3":{"SSId":"Jason_Home_WLAN", "BSSId":"88:C3:97:B1:1D:56", "Channel":"4", "Signal":"-56", "RSSI":"88", "Encryption":"3"}}}
14:15:41.359 MQT: stat/sonoff-952728/RESULT = {"WiFiScan":{"NET4":{"SSId":"Jason_Home_WLAN", "BSSId":"00:A0:F9:45:D5:F8", "Channel":"13", "Signal":"-71", "RSSI":"58", "Encryption":"3"}}}
14:15:41.369 MQT: stat/sonoff-952728/RESULT = {"WiFiScan":{"NET5":{"SSId":"Jason_Home_WLAN", "BSSId":"02:A0:F9:4F:F9:41", "Channel":"7", "Signal":"-86", "RSSI":"28", "Encryption":"3"}}}
14:15:41.379 MQT: stat/sonoff-952728/RESULT = {"WiFiScan":{"NET6":{"SSId":"Jason_Home_WLAN", "BSSId":"00:09:4F:70:72:BB", "Channel":"1", "Signal":"-88", "RSSI":"24", "Encryption":"3"}}}

@arendst
Copy link
Owner

arendst commented Aug 7, 2022

I think there is a difference in esp8266 and esp32 enumaration

esp8266:

/* Encryption modes */
enum wl_enc_type {  /* Values map to 802.11 encryption suites... */
        ENC_TYPE_WEP  = 5,
        ENC_TYPE_TKIP = 2,
        ENC_TYPE_CCMP = 4,
        /* ... except these two, 7 and 8 are reserved in 802.11-2007 */
        ENC_TYPE_NONE = 7,
        ENC_TYPE_AUTO = 8
};

with scan result:

14:56:19.032-030 CMD: wifiscan 1
14:56:19.033-030 SRC: Serial
14:56:19.036-030 CMD: Grp 0, Cmd 'WIFISCAN', Idx 1, Len 1, Pld 1, Data '1'
14:56:19.040-026 MQT: stat/wemos4/RESULT = {"WiFiScan":"Scanning"}
14:56:19.754-029 WIF: Network scan started...
14:56:21.751-029 WIF: Network scan finished...
14:56:22.755-026 MQT: stat/wemos4/RESULT = {"WiFiScan":{"NET1":{"SSId":"indebuurt_IoT","BSSId":"18:E8:29:CA:17:C1","Channel":"11","Signal":"-27","RSSI":"100","Encryption":"WPA2/PSK"}}}
14:56:22.765-027 MQT: stat/wemos4/RESULT = {"WiFiScan":{"NET2":{"SSId":"indebuurt_mt2","BSSId":"DC:2C:6E:C8:47:B4","Channel":"5","Signal":"-46","RSSI":"100","Encryption":"WPA/WPA2/PSK"}}}

Esp32:

typedef enum {
    WIFI_AUTH_OPEN = 0,         /**< authenticate mode : open */
    WIFI_AUTH_WEP,              /**< authenticate mode : WEP */
    WIFI_AUTH_WPA_PSK,          /**< authenticate mode : WPA_PSK */
    WIFI_AUTH_WPA2_PSK,         /**< authenticate mode : WPA2_PSK */
    WIFI_AUTH_WPA_WPA2_PSK,     /**< authenticate mode : WPA_WPA2_PSK */
    WIFI_AUTH_WPA2_ENTERPRISE,  /**< authenticate mode : WPA2_ENTERPRISE */
    WIFI_AUTH_WPA3_PSK,         /**< authenticate mode : WPA3_PSK */
    WIFI_AUTH_WPA2_WPA3_PSK,    /**< authenticate mode : WPA2_WPA3_PSK */
    WIFI_AUTH_WAPI_PSK,         /**< authenticate mode : WAPI_PSK */
    WIFI_AUTH_MAX
} wifi_auth_mode_t;

with scan result:

14:57:54.349-123/13 CMD: wifiscan 1
14:57:54.353-123/13 SRC: Serial
14:57:54.357-123/13 CMD: Grp 0, Cmd 'WIFISCAN', Idx 1, Len 1, Pld 1, Data '1'
14:57:54.373-120/12 MQT: stat/esp32d/RESULT = {"WiFiScan":"Scanning"}
14:57:54.811-123/13 WIF: Network scan started...
14:58:01.804-123/13 WIF: Network scan finished...
14:58:02.806-119/12 MQT: stat/esp32d/RESULT = {"WiFiScan":{"NET1":{"SSId":"indebuurt_IoT","BSSId":"18:E8:29:CA:17:C1","Channel":"11","Signal":"-26","RSSI":"100","Encryption":"3"}}}
14:58:02.840-119/11 MQT: stat/esp32d/RESULT = {"WiFiScan":{"NET2":{"SSId":"indebuurt_mt2","BSSId":"DC:2C:6E:C8:47:B4","Channel":"5","Signal":"-43","RSSI":"100","Encryption":"WPA2/PSK"}}}

@arendst
Copy link
Owner

arendst commented Aug 7, 2022

Let me write a common WiFiEncryptionType() function solving this. Hold on.

I'll also move some globals to the correct location. @ascillato pls do nothing now.

arendst added a commit that referenced this pull request Aug 7, 2022
Fix wifiscan encryption types (#16141)
@arendst
Copy link
Owner

arendst commented Aug 7, 2022

@Jason2866 give latest commit a try.

@Jason2866
Copy link
Collaborator

Jason2866 commented Aug 7, 2022

@arendst LGTM

16:02:41.774 CMD: wifiscan 1
16:02:41.782 MQT: stat/sonoff-952728/RESULT = {"WiFiScan":"Scanning"}
16:02:50.541 MQT: stat/sonoff-952728/RESULT = {"WiFiScan":{"NET1":{"SSId":"TestWPA3","BSSId":"02:A0:57:2A:BD:19","Channel":"9","Signal":"-49","RSSI":"100","Encryption":"WPA3/PSK"}}}
16:02:50.548 MQT: stat/sonoff-952728/RESULT = {"WiFiScan":{"NET2":{"SSId":"Jason_Home_WLAN","BSSId":"00:A0:57:2A:BD:19","Channel":"9","Signal":"-50","RSSI":"100","Encryption":"WPA2/WPA3/PSK"}}}
16:02:50.554 MQT: stat/sonoff-952728/RESULT = {"WiFiScan":{"NET3":{"SSId":"Jason_Home_WLAN","BSSId":"88:C3:97:B1:1D:56","Channel":"4","Signal":"-66","RSSI":"68","Encryption":"WPA2/PSK"}}}
16:02:50.561 MQT: stat/sonoff-952728/RESULT = {"WiFiScan":{"NET4":{"SSId":"Jason_Home_WLAN","BSSId":"00:A0:F9:45:D5:F8","Channel":"13","Signal":"-74","RSSI":"52","Encryption":"WPA2/PSK"}}}
16:02:50.570 MQT: stat/sonoff-952728/RESULT = {"WiFiScan":{"NET5":{"SSId":"Jason_Home_WLAN","BSSId":"02:A0:F9:4F:F9:41","Channel":"7","Signal":"-88","RSSI":"24","Encryption":"WPA2/PSK"}}}
16:02:50.576 MQT: stat/sonoff-952728/RESULT = {"WiFiScan":{"NET6":{"SSId":"Jason_Home_WLAN","BSSId":"00:09:4F:70:72:BB","Channel":"1","Signal":"-93","RSSI":"14","Encryption":"WPA2/PSK"}}}

esp8266 (WPA3 unknown to SDK, so this result is as expected)

wifiscan 1
16:10:24.045 MQT: stat/sonoff-79FDC5/RESULT = {"WiFiScan":"Scanning"}
16:10:27.795 MQT: stat/sonoff-79FDC5/RESULT = {"WiFiScan":{"NET1":{"SSId":"Jason_Home_WLAN","BSSId":"00:A0:57:2A:BD:19","Channel":"9","Signal":"-46","RSSI":"100","Encryption":"WPA2/PSK"}}}
16:10:27.800 MQT: stat/sonoff-79FDC5/RESULT = {"WiFiScan":{"NET2":{"SSId":"TestWPA3","BSSId":"02:A0:57:2A:BD:19","Channel":"9","Signal":"-47","RSSI":"100","Encryption":"WPA2/PSK"}}}
16:10:27.805 MQT: stat/sonoff-79FDC5/RESULT = {"WiFiScan":{"NET3":{"SSId":"Jason_Home_WLAN","BSSId":"88:C3:97:B1:1D:56","Channel":"4","Signal":"-52","RSSI":"96","Encryption":"WPA2/PSK"}}}
16:10:27.811 MQT: stat/sonoff-79FDC5/RESULT = {"WiFiScan":{"NET4":{"SSId":"Jason_Home_WLAN","BSSId":"00:A0:F9:45:D5:F8","Channel":"13","Signal":"-78","RSSI":"44","Encryption":"WPA2/PSK"}}}
16:10:27.817 MQT: stat/sonoff-79FDC5/RESULT = {"WiFiScan":{"NET5":{"SSId":"Jason_Home_WLAN","BSSId":"02:A0:F9:4F:F9:41","Channel":"7","Signal":"-83","RSSI":"34","Encryption":"WPA2/PSK"}}}
16:10:27.823 MQT: stat/sonoff-79FDC5/RESULT = {"WiFiScan":{"NET6":{"SSId":"Jason_Home_WLAN","BSSId":"00:09:4F:70:72:BB","Channel":"1","Signal":"-85","RSSI":"30","Encryption":"WPA2/PSK"}}}

@ascillato
Copy link
Contributor Author

Hi, thanks 👍

@tomlaigna
Copy link

Sometimes, WiFiTest0 times out and the device goes to the network anyway. Seems to be a race condition, maybe? The device may go to the specified wifi network before it finishes the http response?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants