diff --git a/src/net/nl_wifi_sm.c b/src/net/nl_wifi_sm.c index dd5d38f..930831f 100644 --- a/src/net/nl_wifi_sm.c +++ b/src/net/nl_wifi_sm.c @@ -46,6 +46,8 @@ static void fail_test(nl_wifi_sm_t *sm, nl_wifi_error_t err, uint32_t now, nl_wi sm->backoff = NL_WIFI_BACKOFF_MIN_MS; sm->retry_armed = false; a->connect = true; /* old credentials */ + sm->awaiting = true; + sm->connect_at = now; } else { sm->state = NL_WIFI_ST_AP_ONBOARDING; } @@ -60,6 +62,8 @@ nl_wifi_actions_t nl_wifi_sm_start(nl_wifi_sm_t *sm, bool have_creds, uint32_t n if (have_creds) { begin_connecting(sm, now); a.connect = true; + sm->awaiting = true; + sm->connect_at = now; } else { sm->state = NL_WIFI_ST_AP_ONBOARDING; sm->ap_up = true; @@ -77,6 +81,7 @@ nl_wifi_actions_t nl_wifi_sm_event(nl_wifi_sm_t *sm, nl_wifi_event_t ev, nl_wifi switch (sm->state) { case NL_WIFI_ST_STA_CONNECTING: case NL_WIFI_ST_APSTA_FALLBACK: + sm->awaiting = false; if (ev == NL_WIFI_EV_CONNECTED) { sm->state = NL_WIFI_ST_STA_CONNECTED; sm->retry_armed = false; @@ -131,10 +136,20 @@ nl_wifi_actions_t nl_wifi_sm_tick(nl_wifi_sm_t *sm, uint32_t now) a.start_ap = true; } } + if ((sm->state == NL_WIFI_ST_STA_CONNECTING || sm->state == NL_WIFI_ST_APSTA_FALLBACK) && sm->awaiting && !sm->retry_armed && + due(now, sm->connect_at + NL_WIFI_CONNECT_TIMEOUT_MS)) { + sm->awaiting = false; + uint32_t timeout_at = sm->connect_at + NL_WIFI_CONNECT_TIMEOUT_MS; + sm->retry_armed = true; + sm->retry_at = timeout_at + sm->backoff; + sm->backoff = sm->backoff * 2u > NL_WIFI_BACKOFF_MAX_MS ? NL_WIFI_BACKOFF_MAX_MS : sm->backoff * 2u; + } if ((sm->state == NL_WIFI_ST_STA_CONNECTING || sm->state == NL_WIFI_ST_APSTA_FALLBACK) && sm->retry_armed && due(now, sm->retry_at)) { sm->retry_armed = false; a.connect = true; + sm->awaiting = true; + sm->connect_at = now; } if (sm->state == NL_WIFI_ST_ONBOARD_TEST && due(now, sm->deadline)) fail_test(sm, NL_WIFI_ERR_TIMEOUT, now, &a); diff --git a/src/net/nl_wifi_sm.h b/src/net/nl_wifi_sm.h index ff6fe65..478d16e 100644 --- a/src/net/nl_wifi_sm.h +++ b/src/net/nl_wifi_sm.h @@ -10,6 +10,7 @@ #define NL_WIFI_AP_CLOSE_FALLBACK_MS 10000u #define NL_WIFI_BACKOFF_MIN_MS 1000u #define NL_WIFI_BACKOFF_MAX_MS 60000u +#define NL_WIFI_CONNECT_TIMEOUT_MS 30000u /* no event after a connect -> treat as failed */ typedef enum { NL_WIFI_ST_AP_ONBOARDING = 0, @@ -41,11 +42,13 @@ typedef struct { bool pulsing; bool retry_armed; bool ap_stop_armed; + bool awaiting; /* waiting for response to a connect */ uint32_t since; /* start of the current connecting phase */ uint32_t retry_at; uint32_t backoff; uint32_t deadline; /* onboarding test */ uint32_t ap_stop_at; + uint32_t connect_at; /* when the pending connect was issued */ } nl_wifi_sm_t; nl_wifi_actions_t nl_wifi_sm_start(nl_wifi_sm_t *sm, bool have_creds, uint32_t now); diff --git a/test/test_wifi_sm.c b/test/test_wifi_sm.c index 24ba7d8..b306afd 100644 --- a/test/test_wifi_sm.c +++ b/test/test_wifi_sm.c @@ -155,6 +155,25 @@ static void test_error_names(void) TEST_ASSERT_EQUAL_STRING("not_found", nl_wifi_error_name(NL_WIFI_ERR_NOT_FOUND)); } +static void test_connect_without_any_event_retries(void) +{ + nl_wifi_sm_start(&sm, true, 0); + TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 29999).connect); + TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 30000).connect); /* retry scheduled at 31000 */ + TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 31000).connect); + /* next timeout: 31000 + 30000 + 2000 backoff = 63000 */ + TEST_ASSERT_FALSE(nl_wifi_sm_tick(&sm, 62999).connect); + TEST_ASSERT_TRUE(nl_wifi_sm_tick(&sm, 63000).connect); + /* fallback after 300000 ms, and attempts continue */ + a = nl_wifi_sm_tick(&sm, 300000); + TEST_ASSERT_TRUE(a.start_ap); + TEST_ASSERT_EQUAL(NL_WIFI_ST_APSTA_FALLBACK, sm.state); + TEST_ASSERT_EQUAL_STRING("apsta", nl_wifi_sm_mode(&sm)); + /* another attempt fires later */ + a = nl_wifi_sm_tick(&sm, 360000); + TEST_ASSERT_TRUE(a.connect); +} + int main(void) { UNITY_BEGIN(); @@ -168,5 +187,6 @@ int main(void) RUN_TEST(test_onboarding_timeout); RUN_TEST(test_onboarding_while_connected_falls_back_to_old_credentials); RUN_TEST(test_error_names); + RUN_TEST(test_connect_without_any_event_retries); return UNITY_END(); }