From 388522a886c950c2b8952672ac69a47241dd25eb Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 25 Jun 2020 15:44:04 +1000 Subject: [PATCH 1/7] ci: Wait up to 60 seconds for client to connect to DUT in provisioning test --- examples/provisioning/softap_prov/softap_prov_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/provisioning/softap_prov/softap_prov_test.py b/examples/provisioning/softap_prov/softap_prov_test.py index f129f3500..522eb20fc 100644 --- a/examples/provisioning/softap_prov/softap_prov_test.py +++ b/examples/provisioning/softap_prov/softap_prov_test.py @@ -55,7 +55,7 @@ def test_examples_provisioning_softap(env, extra_data): ctrl = wifi_tools.wpa_cli(iface, reset_on_exit=True) print("Connecting to DUT SoftAP...") ip = ctrl.connect(ssid, password) - got_ip = dut1.expect(re.compile(r"softAP assign IP to station,IP is: (\d+.\d+.\d+.\d+)"), timeout=30)[0] + got_ip = dut1.expect(re.compile(r"softAP assign IP to station,IP is: (\d+.\d+.\d+.\d+)"), timeout=60)[0] if ip != got_ip: raise RuntimeError("SoftAP connected to another host! " + ip + "!=" + got_ip) print("Connected to DUT SoftAP") From 6787718e1bd08a322cb141083d826dc575664694 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 25 Jun 2020 15:43:42 +1000 Subject: [PATCH 2/7] ci provisioning: Use strings instead of "magic numbers" for connection state --- examples/provisioning/ble_prov/ble_prov_test.py | 4 ++-- examples/provisioning/manager/wifi_prov_mgr_test.py | 6 +++--- examples/provisioning/softap_prov/softap_prov_test.py | 4 ++-- tools/esp_prov/esp_prov.py | 4 ++-- tools/esp_prov/prov/wifi_prov.py | 8 +++++++- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/examples/provisioning/ble_prov/ble_prov_test.py b/examples/provisioning/ble_prov/ble_prov_test.py index c61001545..9acea55fe 100644 --- a/examples/provisioning/ble_prov/ble_prov_test.py +++ b/examples/provisioning/ble_prov/ble_prov_test.py @@ -87,9 +87,9 @@ def test_examples_provisioning_ble(env, extra_data): time.sleep(5) print("Wi-Fi connection state") ret = esp_prov.get_wifi_config(transport, security) - if (ret == 1): + if (ret == "connecting"): continue - elif (ret == 0): + elif (ret == "connected"): print("Provisioning was successful") success = True break diff --git a/examples/provisioning/manager/wifi_prov_mgr_test.py b/examples/provisioning/manager/wifi_prov_mgr_test.py index c8727c8ad..b97780e5d 100644 --- a/examples/provisioning/manager/wifi_prov_mgr_test.py +++ b/examples/provisioning/manager/wifi_prov_mgr_test.py @@ -92,12 +92,12 @@ def test_examples_wifi_prov_mgr(env, extra_data): time.sleep(5) print("Wi-Fi connection state") ret = esp_prov.get_wifi_config(transport, security) - if (ret == 1): + if (ret == "connecting"): continue - elif (ret == 0): + elif (ret == "connected"): print("Provisioning was successful") success = True - elif (ret == 3 and retry < 3): + elif (ret == "failed" and retry < 3): retry = retry + 1 print("Connection failed.. retry again...: ", ret) continue diff --git a/examples/provisioning/softap_prov/softap_prov_test.py b/examples/provisioning/softap_prov/softap_prov_test.py index 522eb20fc..dbd51a322 100644 --- a/examples/provisioning/softap_prov/softap_prov_test.py +++ b/examples/provisioning/softap_prov/softap_prov_test.py @@ -101,9 +101,9 @@ def test_examples_provisioning_softap(env, extra_data): time.sleep(5) print("Wi-Fi connection state") ret = esp_prov.get_wifi_config(transport, security) - if (ret == 1): + if (ret == "connecting"): continue - elif (ret == 0): + elif (ret == "connected"): print("Provisioning was successful") success = True break diff --git a/tools/esp_prov/esp_prov.py b/tools/esp_prov/esp_prov.py index 3ce67d700..9a16252e5 100644 --- a/tools/esp_prov/esp_prov.py +++ b/tools/esp_prov/esp_prov.py @@ -453,9 +453,9 @@ if __name__ == '__main__': time.sleep(5) print("\n==== Wi-Fi connection state ====") ret = get_wifi_config(obj_transport, obj_security) - if (ret == 1): + if (ret == "connecting"): continue - elif (ret == 0): + elif (ret == "connected"): print("==== Provisioning was successful ====") else: print("---- Provisioning failed ----") diff --git a/tools/esp_prov/prov/wifi_prov.py b/tools/esp_prov/prov/wifi_prov.py index 9140291f9..11e4c0bde 100644 --- a/tools/esp_prov/prov/wifi_prov.py +++ b/tools/esp_prov/prov/wifi_prov.py @@ -45,19 +45,25 @@ def config_get_status_response(security_ctx, response_data): cmd_resp1.ParseFromString(decrypted_message) print_verbose(security_ctx, "Response type " + str(cmd_resp1.msg)) print_verbose(security_ctx, "Response status " + str(cmd_resp1.resp_get_status.status)) + if cmd_resp1.resp_get_status.sta_state == 0: print("++++ WiFi state: " + "connected ++++") + return "connected" elif cmd_resp1.resp_get_status.sta_state == 1: print("++++ WiFi state: " + "connecting... ++++") + return "connecting" elif cmd_resp1.resp_get_status.sta_state == 2: print("++++ WiFi state: " + "disconnected ++++") + return "disconnected" elif cmd_resp1.resp_get_status.sta_state == 3: print("++++ WiFi state: " + "connection failed ++++") if cmd_resp1.resp_get_status.fail_reason == 0: print("++++ Failure reason: " + "Incorrect Password ++++") elif cmd_resp1.resp_get_status.fail_reason == 1: print("++++ Failure reason: " + "Incorrect SSID ++++") - return cmd_resp1.resp_get_status.sta_state + return "failed" + return "unknown" + def config_set_config_request(security_ctx, ssid, passphrase): From 822b6986aab84f9f7a4f7457d96bbd84865915c4 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 25 Jun 2020 15:52:25 +1000 Subject: [PATCH 3/7] esp_prov: Refactor to use new 'wait_wifi_connected' function Means all provisioning examples will have the same retry behaviour. --- .../provisioning/ble_prov/ble_prov_test.py | 15 +------- .../manager/wifi_prov_mgr_test.py | 20 +--------- .../softap_prov/softap_prov_test.py | 15 +------- tools/esp_prov/esp_prov.py | 38 +++++++++++++------ tools/esp_prov/prov/wifi_prov.py | 1 - 5 files changed, 30 insertions(+), 59 deletions(-) diff --git a/examples/provisioning/ble_prov/ble_prov_test.py b/examples/provisioning/ble_prov/ble_prov_test.py index 9acea55fe..ccbf5c5f9 100644 --- a/examples/provisioning/ble_prov/ble_prov_test.py +++ b/examples/provisioning/ble_prov/ble_prov_test.py @@ -17,7 +17,6 @@ from __future__ import print_function import re import os -import time import ttfw_idf import esp_prov @@ -82,19 +81,7 @@ def test_examples_provisioning_ble(env, extra_data): if not esp_prov.apply_wifi_config(transport, security): raise RuntimeError("Failed to send apply config") - success = False - while True: - time.sleep(5) - print("Wi-Fi connection state") - ret = esp_prov.get_wifi_config(transport, security) - if (ret == "connecting"): - continue - elif (ret == "connected"): - print("Provisioning was successful") - success = True - break - - if not success: + if not esp_prov.wait_wifi_connected(transport, security): raise RuntimeError("Provisioning failed") diff --git a/examples/provisioning/manager/wifi_prov_mgr_test.py b/examples/provisioning/manager/wifi_prov_mgr_test.py index b97780e5d..4289ab704 100644 --- a/examples/provisioning/manager/wifi_prov_mgr_test.py +++ b/examples/provisioning/manager/wifi_prov_mgr_test.py @@ -17,7 +17,6 @@ from __future__ import print_function import re import os -import time import ttfw_idf import esp_prov @@ -86,24 +85,7 @@ def test_examples_wifi_prov_mgr(env, extra_data): if not esp_prov.apply_wifi_config(transport, security): raise RuntimeError("Failed to send apply config") - success = False - retry = 0 - while True: - time.sleep(5) - print("Wi-Fi connection state") - ret = esp_prov.get_wifi_config(transport, security) - if (ret == "connecting"): - continue - elif (ret == "connected"): - print("Provisioning was successful") - success = True - elif (ret == "failed" and retry < 3): - retry = retry + 1 - print("Connection failed.. retry again...: ", ret) - continue - break - - if not success: + if not esp_prov.wait_wifi_connected(transport, security): raise RuntimeError("Provisioning failed") # Check if BTDM memory is released after provisioning finishes diff --git a/examples/provisioning/softap_prov/softap_prov_test.py b/examples/provisioning/softap_prov/softap_prov_test.py index dbd51a322..9bfbd8613 100644 --- a/examples/provisioning/softap_prov/softap_prov_test.py +++ b/examples/provisioning/softap_prov/softap_prov_test.py @@ -17,7 +17,6 @@ from __future__ import print_function import re import os -import time import ttfw_idf import esp_prov @@ -96,19 +95,7 @@ def test_examples_provisioning_softap(env, extra_data): if not esp_prov.apply_wifi_config(transport, security): raise RuntimeError("Failed to send apply config") - success = False - while True: - time.sleep(5) - print("Wi-Fi connection state") - ret = esp_prov.get_wifi_config(transport, security) - if (ret == "connecting"): - continue - elif (ret == "connected"): - print("Provisioning was successful") - success = True - break - - if not success: + if not esp_prov.wait_wifi_connected(transport, security): raise RuntimeError("Provisioning failed") diff --git a/tools/esp_prov/esp_prov.py b/tools/esp_prov/esp_prov.py index 9a16252e5..463a13983 100644 --- a/tools/esp_prov/esp_prov.py +++ b/tools/esp_prov/esp_prov.py @@ -266,6 +266,32 @@ def get_wifi_config(tp, sec): return None +def wait_wifi_connected(tp, sec): + """ + Wait for provisioning to report Wi-Fi is connected + + Returns True if Wi-Fi connection succeeded, False if connection consistently failed + """ + TIME_PER_POLL = 5 + retry = 3 + + while True: + time.sleep(TIME_PER_POLL) + print("\n==== Wi-Fi connection state ====") + ret = get_wifi_config(tp, sec) + if ret == "connecting": + continue + elif ret == "connected": + print("==== Provisioning was successful ====") + return True + elif retry > 0: + retry -= 1 + print("Waiting to poll status again (status %s, %d tries left)..." % (ret, retry)) + else: + print("---- Provisioning failed ----") + return False + + def desc_format(*args): desc = '' for arg in args: @@ -449,14 +475,4 @@ if __name__ == '__main__': exit(7) print("==== Apply config sent successfully ====") - while True: - time.sleep(5) - print("\n==== Wi-Fi connection state ====") - ret = get_wifi_config(obj_transport, obj_security) - if (ret == "connecting"): - continue - elif (ret == "connected"): - print("==== Provisioning was successful ====") - else: - print("---- Provisioning failed ----") - break + wait_wifi_connected(obj_transport, obj_security) diff --git a/tools/esp_prov/prov/wifi_prov.py b/tools/esp_prov/prov/wifi_prov.py index 11e4c0bde..8bd6cd147 100644 --- a/tools/esp_prov/prov/wifi_prov.py +++ b/tools/esp_prov/prov/wifi_prov.py @@ -65,7 +65,6 @@ def config_get_status_response(security_ctx, response_data): return "unknown" - def config_set_config_request(security_ctx, ssid, passphrase): # Form protobuf request packet for SetConfig command cmd = proto.wifi_config_pb2.WiFiConfigPayload() From 200fa4abebf1c060ac28d2a6de04d33694cd5bfd Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 25 Jun 2020 18:08:58 +1000 Subject: [PATCH 4/7] legacy provisioning: Always try to reconnect when disconnected Some APs may fail authentication temporarily if they have too many associated stations, for example. --- examples/provisioning/ble_prov/main/app_prov.c | 2 +- examples/provisioning/console_prov/main/app_prov.c | 2 +- examples/provisioning/custom_config/main/app_prov.c | 2 +- examples/provisioning/softap_prov/main/app_prov.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/provisioning/ble_prov/main/app_prov.c b/examples/provisioning/ble_prov/main/app_prov.c index 19ea1b311..7ce62346f 100644 --- a/examples/provisioning/ble_prov/main/app_prov.c +++ b/examples/provisioning/ble_prov/main/app_prov.c @@ -248,8 +248,8 @@ static void app_prov_event_handler(void* handler_arg, esp_event_base_t event_bas /* If none of the expected reasons, * retry connecting to host SSID */ g_prov->wifi_state = WIFI_PROV_STA_CONNECTING; - esp_wifi_connect(); } + esp_wifi_connect(); } } diff --git a/examples/provisioning/console_prov/main/app_prov.c b/examples/provisioning/console_prov/main/app_prov.c index b260db5f4..c02e48280 100644 --- a/examples/provisioning/console_prov/main/app_prov.c +++ b/examples/provisioning/console_prov/main/app_prov.c @@ -186,8 +186,8 @@ static void app_prov_event_handler(void* handler_arg, esp_event_base_t event_bas /* If none of the expected reasons, * retry connecting to host SSID */ g_prov->wifi_state = WIFI_PROV_STA_CONNECTING; - esp_wifi_connect(); } + esp_wifi_connect(); } } diff --git a/examples/provisioning/custom_config/main/app_prov.c b/examples/provisioning/custom_config/main/app_prov.c index d9859cdb4..16e893a99 100644 --- a/examples/provisioning/custom_config/main/app_prov.c +++ b/examples/provisioning/custom_config/main/app_prov.c @@ -211,8 +211,8 @@ static void app_prov_event_handler(void* handler_arg, esp_event_base_t event_bas /* If none of the expected reasons, * retry connecting to host SSID */ g_prov->wifi_state = WIFI_PROV_STA_CONNECTING; - esp_wifi_connect(); } + esp_wifi_connect(); } } diff --git a/examples/provisioning/softap_prov/main/app_prov.c b/examples/provisioning/softap_prov/main/app_prov.c index 73c8bdfc6..11e798854 100644 --- a/examples/provisioning/softap_prov/main/app_prov.c +++ b/examples/provisioning/softap_prov/main/app_prov.c @@ -197,8 +197,8 @@ static void app_prov_event_handler(void* handler_arg, esp_event_base_t event_bas /* If none of the expected reasons, * retry connecting to host SSID */ g_prov->wifi_state = WIFI_PROV_STA_CONNECTING; - esp_wifi_connect(); } + esp_wifi_connect(); } } From 7358470ae92c2c0a15637d0e443a672a9ec07a00 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 25 Jun 2020 18:18:04 +1000 Subject: [PATCH 5/7] ci wifi_tools: Log the wpa_supplicant interface state when trying to connect Trigger reconnection if wpa_supplicant seems to have dropped the connection. --- tools/ci/python_packages/wifi_tools.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/ci/python_packages/wifi_tools.py b/tools/ci/python_packages/wifi_tools.py index 13376e52e..bc3a7aab8 100644 --- a/tools/ci/python_packages/wifi_tools.py +++ b/tools/ci/python_packages/wifi_tools.py @@ -51,6 +51,7 @@ class wpa_cli: iface_path = service.GetInterface(self.iface_name) self.iface_obj = bus.get_object("fi.w1.wpa_supplicant1", iface_path) self.iface_ifc = dbus.Interface(self.iface_obj, "fi.w1.wpa_supplicant1.Interface") + self.iface_props = dbus.Interface(self.iface_obj, 'org.freedesktop.DBus.Properties') if self.iface_ifc is None: raise RuntimeError('supplicant : Failed to fetch interface') @@ -61,6 +62,14 @@ class wpa_cli: else: self.connected = True + def _get_iface_property(self, name): + """ Read the property with 'name' from the wi-fi interface object + + Note: The result is a dbus wrapped type, so should usually convert it to the corresponding native + Python type + """ + return self.iface_props.Get("fi.w1.wpa_supplicant1.Interface", name) + def connect(self, ssid, password): if self.connected is True: self.iface_ifc.Disconnect() @@ -76,7 +85,14 @@ class wpa_cli: retry = 10 while retry > 0: time.sleep(5) + + state = str(self._get_iface_property("State")) + print("wpa iface state %s (scanning %s)" % (state, bool(self._get_iface_property("Scanning")))) + if state in ["disconnected", "inactive"]: + self.iface_ifc.Reconnect() + ip = get_wiface_IPv4(self.iface_name) + print("wpa iface %s IP %s" % (self.iface_name, ip)) if ip is not None: self.connected = True return ip From e0fc7b1c48c282f32aaca4a9d61adb4c7da06ddb Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 26 Jun 2020 14:21:11 +1000 Subject: [PATCH 6/7] esp_prov: Extend the timeout for HTTP connection to SoftAP --- tools/esp_prov/transport/transport_http.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/esp_prov/transport/transport_http.py b/tools/esp_prov/transport/transport_http.py index 3c7aed013..c68612882 100644 --- a/tools/esp_prov/transport/transport_http.py +++ b/tools/esp_prov/transport/transport_http.py @@ -31,10 +31,10 @@ class Transport_HTTP(Transport): raise RuntimeError("Unable to resolve hostname :" + hostname) if certfile is None: - self.conn = http.client.HTTPConnection(hostname, timeout=30) + self.conn = http.client.HTTPConnection(hostname, timeout=45) else: ssl_ctx = ssl.create_default_context(cafile=certfile) - self.conn = http.client.HTTPSConnection(hostname, context=ssl_ctx, timeout=30) + self.conn = http.client.HTTPSConnection(hostname, context=ssl_ctx, timeout=45) try: print("Connecting to " + hostname) self.conn.connect() From d7f251aef3497495387712aa204562d9c83dce42 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 26 Jun 2020 18:16:20 +1000 Subject: [PATCH 7/7] ci provisioning: Add some experiments to check the runner's STA state Not clear why sometimes it associates without negotiating a new DHCP lease. --- .../softap_prov/softap_prov_test.py | 79 ++++++++++--------- tools/ci/python_packages/wifi_tools.py | 7 +- 2 files changed, 46 insertions(+), 40 deletions(-) diff --git a/examples/provisioning/softap_prov/softap_prov_test.py b/examples/provisioning/softap_prov/softap_prov_test.py index 9bfbd8613..1689d6f95 100644 --- a/examples/provisioning/softap_prov/softap_prov_test.py +++ b/examples/provisioning/softap_prov/softap_prov_test.py @@ -51,52 +51,55 @@ def test_examples_provisioning_softap(env, extra_data): print("SoftAP SSID : " + ssid) print("SoftAP Password : " + password) - ctrl = wifi_tools.wpa_cli(iface, reset_on_exit=True) - print("Connecting to DUT SoftAP...") - ip = ctrl.connect(ssid, password) - got_ip = dut1.expect(re.compile(r"softAP assign IP to station,IP is: (\d+.\d+.\d+.\d+)"), timeout=60)[0] - if ip != got_ip: - raise RuntimeError("SoftAP connected to another host! " + ip + "!=" + got_ip) - print("Connected to DUT SoftAP") + try: + ctrl = wifi_tools.wpa_cli(iface, reset_on_exit=True) + print("Connecting to DUT SoftAP...") + ip = ctrl.connect(ssid, password) + got_ip = dut1.expect(re.compile(r"softAP assign IP to station,IP is: (\d+.\d+.\d+.\d+)"), timeout=60)[0] + if ip != got_ip: + raise RuntimeError("SoftAP connected to another host! " + ip + "!=" + got_ip) + print("Connected to DUT SoftAP") - print("Starting Provisioning") - verbose = False - protover = "V0.1" - secver = 1 - pop = "abcd1234" - provmode = "softap" - ap_ssid = "myssid" - ap_password = "mypassword" - softap_endpoint = ip.split('.')[0] + "." + ip.split('.')[1] + "." + ip.split('.')[2] + ".1:80" + print("Starting Provisioning") + verbose = False + protover = "V0.1" + secver = 1 + pop = "abcd1234" + provmode = "softap" + ap_ssid = "myssid" + ap_password = "mypassword" + softap_endpoint = ip.split('.')[0] + "." + ip.split('.')[1] + "." + ip.split('.')[2] + ".1:80" - print("Getting security") - security = esp_prov.get_security(secver, pop, verbose) - if security is None: - raise RuntimeError("Failed to get security") + print("Getting security") + security = esp_prov.get_security(secver, pop, verbose) + if security is None: + raise RuntimeError("Failed to get security") - print("Getting transport") - transport = esp_prov.get_transport(provmode, softap_endpoint) - if transport is None: - raise RuntimeError("Failed to get transport") + print("Getting transport") + transport = esp_prov.get_transport(provmode, softap_endpoint) + if transport is None: + raise RuntimeError("Failed to get transport") - print("Verifying protocol version") - if not esp_prov.version_match(transport, protover): - raise RuntimeError("Mismatch in protocol version") + print("Verifying protocol version") + if not esp_prov.version_match(transport, protover): + raise RuntimeError("Mismatch in protocol version") - print("Starting Session") - if not esp_prov.establish_session(transport, security): - raise RuntimeError("Failed to start session") + print("Starting Session") + if not esp_prov.establish_session(transport, security): + raise RuntimeError("Failed to start session") - print("Sending Wifi credential to DUT") - if not esp_prov.send_wifi_config(transport, security, ap_ssid, ap_password): - raise RuntimeError("Failed to send Wi-Fi config") + print("Sending Wifi credential to DUT") + if not esp_prov.send_wifi_config(transport, security, ap_ssid, ap_password): + raise RuntimeError("Failed to send Wi-Fi config") - print("Applying config") - if not esp_prov.apply_wifi_config(transport, security): - raise RuntimeError("Failed to send apply config") + print("Applying config") + if not esp_prov.apply_wifi_config(transport, security): + raise RuntimeError("Failed to send apply config") - if not esp_prov.wait_wifi_connected(transport, security): - raise RuntimeError("Provisioning failed") + if not esp_prov.wait_wifi_connected(transport, security): + raise RuntimeError("Provisioning failed") + finally: + ctrl.reset() if __name__ == '__main__': diff --git a/tools/ci/python_packages/wifi_tools.py b/tools/ci/python_packages/wifi_tools.py index bc3a7aab8..b926efb55 100644 --- a/tools/ci/python_packages/wifi_tools.py +++ b/tools/ci/python_packages/wifi_tools.py @@ -55,8 +55,9 @@ class wpa_cli: if self.iface_ifc is None: raise RuntimeError('supplicant : Failed to fetch interface') - self.old_network = self.iface_obj.Get("fi.w1.wpa_supplicant1.Interface", "CurrentNetwork", - dbus_interface='org.freedesktop.DBus.Properties') + self.old_network = self._get_iface_property("CurrentNetwork") + print("Old network is %s" % self.old_network) + if self.old_network == '/': self.old_network = None else: @@ -78,6 +79,8 @@ class wpa_cli: if self.new_network is not None: self.iface_ifc.RemoveNetwork(self.new_network) + print("Pre-connect state is %s, IP is %s" % (self._get_iface_property("State"), get_wiface_IPv4(self.iface_name))) + self.new_network = self.iface_ifc.AddNetwork({"ssid": ssid, "psk": password}) self.iface_ifc.SelectNetwork(self.new_network)