diff --git a/main/include/wifi_connection.h b/main/include/wifi_connection.h index ae6f053..040e949 100644 --- a/main/include/wifi_connection.h +++ b/main/include/wifi_connection.h @@ -5,7 +5,32 @@ #include "esp_wifi.h" +// Simpler interpretation of WiFi signal strength. +typedef enum { + WIFI_STRENGTH_VERY_BAD, + WIFI_STRENGTH_BAD, + WIFI_STRENGTH_GOOD, + WIFI_STRENGTH_VERY_GOOD, +} wifi_strength_t; + +// Thresholds for aforementioned signal strength definitions. +#define WIFI_THRESH_BAD -80 +#define WIFI_THRESH_GOOD -70 +#define WIFI_THRESH_VERY_GOOD -67 + +// Firt time initialisation of the WiFi stack. void wifi_init(); + +// Connect to a traditional username/password WiFi network. bool wifi_connect(const char* aSsid, const char* aPassword, wifi_auth_mode_t aAuthmode, uint8_t aRetryMax); + +// Connect to a WPA2 enterprise WiFi network. bool wifi_connect_ent(const char* aSsid, const char *aIdent, const char *aAnonIdent, const char* aPassword, uint8_t aRetryMax); -void wifi_scan(); + +// Scan for WiFi networks. +// Updates the APs pointer if non-null. +// Returns the number of APs found. +size_t wifi_scan(wifi_ap_record_t **aps); + +// Get the strength value for a given RSSI. +wifi_strength_t wifi_rssi_to_strength(int8_t rssi); diff --git a/main/main.c b/main/main.c index 7e1b9cb..69c586e 100644 --- a/main/main.c +++ b/main/main.c @@ -306,10 +306,6 @@ void display_fatal_error(pax_buf_t* pax_buffer, ILI9341* ili9341, const char* li ili9341_write(ili9341, pax_buffer->buf); } -// void wifi_connect_to_stored() { -// wifi_connect_ent("MCH2022-legacy", "thefunny", "q", "nope, you are not", 3); -// } - void wifi_connect_to_stored() { // Open NVS. nvs_handle_t handle; @@ -893,6 +889,9 @@ void app_main(void) { char buffer[300]; snprintf(buffer, sizeof(buffer), "SSID is %s\nPassword is %s", ssid, password); graphics_task(pax_buffer, ili9341, NULL, buffer); + } else if (menu_action == ACTION_WIFI_SCAN) { + // Scan for WiFi networks. + wifi_scan(NULL); } else { break; } diff --git a/main/wifi_connection.c b/main/wifi_connection.c index 6cb917f..d607762 100644 --- a/main/wifi_connection.c +++ b/main/wifi_connection.c @@ -16,35 +16,47 @@ static const char *TAG = "wifi_connection"; #define WIFI_CONNECTED_BIT BIT0 #define WIFI_FAIL_BIT BIT1 +#define WIFI_STARTED_BIT BIT2 -static EventGroupHandle_t s_wifi_event_group; +static EventGroupHandle_t wifiEventGroup; -static uint8_t gRetryCounter = 0; -static uint8_t gRetryMax = 3; +static uint8_t retryCount = 0; +static uint8_t maxRetries = 3; +static bool isScanning = false; static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { - esp_wifi_connect(); - } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { - if (gRetryCounter < 3) { + xEventGroupSetBits(wifiEventGroup, WIFI_STARTED_BIT); + if (!isScanning) { + // Connect only if we're not scanning the WiFi. esp_wifi_connect(); - gRetryCounter++; - ESP_LOGI(TAG, "retry to connect to the AP"); + } + ESP_LOGI(TAG, "WiFi station start."); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_STOP) { + xEventGroupClearBits(wifiEventGroup, WIFI_STARTED_BIT); + ESP_LOGI(TAG, "WiFi station stop."); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + if (retryCount < 3) { + esp_wifi_connect(); + retryCount++; + ESP_LOGI(TAG, "Retrying connection"); } else { - ESP_LOGI(TAG,"connect to the AP fail"); - xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); + ESP_LOGI(TAG, "Connection failed"); + xEventGroupSetBits(wifiEventGroup, WIFI_FAIL_BIT); } } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; - ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); - gRetryCounter = 0; - xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); + ESP_LOGI(TAG, "Got ip:" IPSTR, IP2STR(&event->ip_info.ip)); + retryCount = 0; + xEventGroupSetBits(wifiEventGroup, WIFI_CONNECTED_BIT); } } void wifi_init() { - s_wifi_event_group = xEventGroupCreate(); + // Create an event group for WiFi things. + wifiEventGroup = xEventGroupCreate(); + // Initialise WiFi stack. ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); @@ -53,17 +65,22 @@ void wifi_init() { wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + // Register event handlers for WiFi. esp_event_handler_instance_t instance_any_id; esp_event_handler_instance_t instance_got_ip; ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id)); ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &instance_got_ip)); + // Turn off WiFi hardware. ESP_ERROR_CHECK(esp_wifi_stop()); } bool wifi_connect(const char* aSsid, const char* aPassword, wifi_auth_mode_t aAuthmode, uint8_t aRetryMax) { - gRetryCounter = 0; - gRetryMax = aRetryMax; + // Set the retry counts. + retryCount = 0; + maxRetries = aRetryMax; + + // Create a config. wifi_config_t wifi_config = {0}; strcpy((char*) wifi_config.sta.ssid, aSsid); strcpy((char*) wifi_config.sta.password, aPassword); @@ -81,7 +98,7 @@ bool wifi_connect(const char* aSsid, const char* aPassword, wifi_auth_mode_t aAu /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ - EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY); + EventBits_t bits = xEventGroupWaitBits(wifiEventGroup, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY); /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually happened. */ if (bits & WIFI_CONNECTED_BIT) { ESP_LOGI(TAG, "Connected to WiFi"); @@ -97,8 +114,8 @@ bool wifi_connect(const char* aSsid, const char* aPassword, wifi_auth_mode_t aAu } bool wifi_connect_ent(const char* aSsid, const char *aIdent, const char *aAnonIdent, const char* aPassword, uint8_t aRetryMax) { - gRetryCounter = 0; - gRetryMax = aRetryMax; + retryCount = 0; + maxRetries = aRetryMax; wifi_config_t wifi_config = {0}; if (strlen(aSsid) > 32) { ESP_LOGE(TAG, "SSID is too long (%zu > 32)!", strlen(aSsid)); @@ -126,7 +143,7 @@ bool wifi_connect_ent(const char* aSsid, const char *aIdent, const char *aAnonId /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ - EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY); + EventBits_t bits = xEventGroupWaitBits(wifiEventGroup, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY); /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually happened. */ if (bits & WIFI_CONNECTED_BIT) { ESP_LOGI(TAG, "Connected to WiFi"); @@ -141,6 +158,116 @@ bool wifi_connect_ent(const char* aSsid, const char *aIdent, const char *aAnonId return false; } -void wifi_scan() { +// Shows a nice info message describing an AP record. +static inline void wifi_desc_record(wifi_ap_record_t *record) { + // Make a string representation of BSSID. + char *bssid_str = malloc(3*6); + if (!bssid_str) return; + snprintf(bssid_str, 3*6, "%02X:%02X:%02X:%02X:%02X:%02X", + record->bssid[0], record->bssid[1], record->bssid[2], + record->bssid[3], record->bssid[4], record->bssid[5] + ); + // Make a string representation of 11b/g/n modes. + char *phy_str = malloc(9); + if (!phy_str) { + free(bssid_str); + return; + } + *phy_str = 0; + if (record->phy_11b | record->phy_11g | record->phy_11n) { + strcpy(phy_str, " 1"); + } + if (record->phy_11b) { + strcat(phy_str, "/b"); + } + if (record->phy_11g) { + strcat(phy_str, "/g"); + } + if (record->phy_11n) { + strcat(phy_str, "/n"); + } + phy_str[2] = '1'; + + ESP_LOGI(TAG, "AP %s %s rssi=%hhd%s", bssid_str, record->ssid, record->rssi, phy_str); + free(bssid_str); + free(phy_str); +} + +// Scan for WiFi access points. +size_t wifi_scan(wifi_ap_record_t **aps_out) { + isScanning = true; + // Scan for any non-hidden APs on all channels. + wifi_scan_config_t cfg = { + .ssid = NULL, + .bssid = NULL, + .channel = 0, + .scan_type = WIFI_SCAN_TYPE_ACTIVE, + .scan_time = { .active={ 0, 0 } }, + }; + + // Start the scan now. + ESP_LOGI(TAG, "Starting scan..."); + esp_err_t res = esp_wifi_scan_start(&cfg, true); + // Whether to call esp_wifi_stop() on finish. + bool stopWhenDone = false; + if (res == ESP_ERR_WIFI_NOT_STARTED) { + // If it complains that the wifi wasn't started, then do so. + ESP_LOGI(TAG, "Starting WiFi for scan"); + + // Set to station but don't connect. + res = esp_wifi_set_mode(WIFI_MODE_STA); + if (res) goto ohno; + + // Start WiFi. + res = esp_wifi_start(); + if (res) goto ohno; + stopWhenDone = true; + + // Await the STA started bit. + xEventGroupWaitBits(wifiEventGroup, WIFI_STARTED_BIT, pdFALSE, pdFALSE, pdMS_TO_TICKS(2000)); + + // Try again. + res = esp_wifi_scan_start(&cfg, true); + } + if (res) { + ohno: + ESP_LOGE(TAG, "Error in WiFi scan: %s", esp_err_to_name(res)); + isScanning = false; + return 0; + } + + // Allocate memory for AP list. + uint16_t num_ap = 0; + esp_wifi_scan_get_ap_num(&num_ap); + wifi_ap_record_t *aps = malloc(sizeof(wifi_ap_record_t) * num_ap); + + // Collect APs and report findings. + esp_wifi_scan_get_ap_records(&num_ap, aps); + for (uint16_t i = 0; i < num_ap; i++) { + wifi_desc_record(&aps[i]); + } + + // Clean up. + if (aps_out) { + // Output pointer is non-null, return the APs list. + *aps_out = aps; + } else { + // Output pointer is null, free the APs list. + free(aps); + } + if (stopWhenDone) { + // Stop WiFi because it was started only for this scan. + esp_wifi_stop(); + } + isScanning = false; + return num_ap; +} + +// Get the strength value for a given RSSI. +wifi_strength_t wifi_rssi_to_strength(int8_t rssi) { + if (rssi > WIFI_THRESH_VERY_GOOD) return WIFI_STRENGTH_VERY_GOOD; + else if (rssi > WIFI_THRESH_GOOD) return WIFI_STRENGTH_GOOD; + else if (rssi > WIFI_THRESH_BAD) return WIFI_STRENGTH_BAD; + else return WIFI_STRENGTH_VERY_BAD; }