main/task_wifi.c

Thu, 20 Apr 2023 14:01:29 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Thu, 20 Apr 2023 14:01:29 +0200
changeset 35
9827c5a08c63
parent 24
74609f70411e
permissions
-rw-r--r--

Version 0.4.2, some code cleanup.

/**
 * @file task_wifi.c
 * @brief WiFi task. Connects to a known Access Point.
 */


#include "config.h"


static const char *TAG = "task_wifi";

#define ESP_WIFI_SSID           CONFIG_ESP_WIFI_SSID
#define ESP_WIFI_PASS           CONFIG_ESP_WIFI_PASSWORD

SemaphoreHandle_t       	xSemaphoreWiFi = NULL;		///< Semaphore WiFi task.
EventGroupHandle_t		xEventGroupWifi;		///< Events WiFi task.
esp_event_handler_instance_t	instance_any_id;		///< WiFi event handler.
esp_event_handler_instance_t	instance_got_ip;		///< IP event handler.

WIFI_State			*wifi_state = NULL;		///< Public state for other tasks.
esp_netif_t			*sta_netif = NULL;		///< Station interface.


const int TASK_WIFI_REQUEST_STA_DISCONNECT = BIT1;		///< When set, means a client requested to disconnect from currently connected AP.
const int TASK_WIFI_REQUEST_STA_CONNECT = BIT2;			///< When set, means a client requested to connect to an access point.

const int TASK_WIFI_HAS_IP = BIT3;				///< Indicate that we have an IP address
const int TASK_WIFI_STA_FAILED = BIT5;				///< Indicate that we could not get a connection to AP as station.
const int TASK_WIFI_STA_DISCONNECTED = BIT6;			///< Indicate that we are disconnected from an ap station.
const int TASK_WIFI_STA_CONNECTED = BIT7;			///< Indicate that we are connected to AP as station, flip of BIT6.


static void init_wifi(void);
void wifi_connect(void);


/****************************************************************************/


bool ready_WiFi(void)
{
    if ((xEventGroupGetBits(xEventGroupWifi) & (TASK_WIFI_STA_CONNECTED | TASK_WIFI_HAS_IP)) == (TASK_WIFI_STA_CONNECTED | TASK_WIFI_HAS_IP))
        return true;
    return false;
}



void request_WiFi(bool connect)
{
    if (connect)
    	xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_REQUEST_STA_CONNECT);
    else
	xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_REQUEST_STA_DISCONNECT);
}


static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
    switch (event_id) {

	case WIFI_EVENT_STA_START:
		ESP_LOGI(TAG, "Event wifi START");
        	// Set the hostname for the dhcp client.
#ifdef CONFIG_CODE_PRODUCTION
		ESP_ERROR_CHECK(esp_netif_set_hostname(sta_netif, "balkon"));
#endif
#ifdef CONFIG_CODE_TESTING
		ESP_ERROR_CHECK(esp_netif_set_hostname(sta_netif, "wemos"));
#endif
		break;

	case WIFI_EVENT_STA_CONNECTED: {
                wifi_ap_record_t ap_info;
		esp_wifi_sta_get_ap_info(&ap_info);
		ESP_LOGI(TAG, "Event STA connected rssi=%d", ap_info.rssi);
                if (xSemaphoreTake(xSemaphoreWiFi, 25) == pdTRUE) {
                    wifi_state->STA_connected = true;
                    wifi_state->STA_rssi = ap_info.rssi;
                    xSemaphoreGive(xSemaphoreWiFi);
                } else {
		    ESP_LOGE(TAG, "wifi_event_handler() lock error WIFI_EVENT_STA_CONNECTED");
		}
                xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_STA_CONNECTED);
                xEventGroupClearBits(xEventGroupWifi, TASK_WIFI_STA_DISCONNECTED);
	    	break;
	    }

	case WIFI_EVENT_STA_DISCONNECTED: {
		wifi_event_sta_disconnected_t* disconnected = (wifi_event_sta_disconnected_t*) event_data;
                ESP_LOGI(TAG, "Event STA disconnected, reason: %d", disconnected->reason);

		/*
		 * If it's not a normal request to disconnect, make sure the mqtt
		 * connection will be removed.
		 */
		if (disconnected->reason != 8)
		    request_mqtt(false);

		/*
		 * Error conditions.
		 */
		if (disconnected->reason == 2) {
		    ESP_LOGW(TAG, "Auth Expire: try to recover");
		    wifi_connect();
		} else if (disconnected->reason == 39) {
		    ESP_LOGW(TAG, "Timeout: try to recover");
		    ESP_ERROR_CHECK(esp_wifi_connect());
		}
                if (xSemaphoreTake(xSemaphoreWiFi, 25) == pdTRUE) {
                    wifi_state->STA_connected = false;
                    wifi_state->STA_online = false;
                    xSemaphoreGive(xSemaphoreWiFi);
                } else {
		    ESP_LOGE(TAG, "wifi_event_handler() lock error WIFI_EVENT_STA_DISCONNECTED");
		}
                xEventGroupClearBits(xEventGroupWifi, TASK_WIFI_STA_CONNECTED);
                xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_STA_DISCONNECTED);
		break;
	    }

	default:
	    ESP_LOGW(TAG, "Unknown WiFi event %d", (int)event_id);
	    break;
    }
}



static void got_ip_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
    switch (event_id) {

	case IP_EVENT_STA_GOT_IP:
		//ESP_LOGE(TAG, "got_ip_event_handler()");
		xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_HAS_IP);
		ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
		if (xSemaphoreTake(xSemaphoreWiFi, 25) == pdTRUE) {
			wifi_state->STA_online = true;
			snprintf(wifi_state->STA_ip, 16, IPSTR, IP2STR(&event->ip_info.ip));
			snprintf(wifi_state->STA_nm, 16, IPSTR, IP2STR(&event->ip_info.netmask));
			snprintf(wifi_state->STA_gw, 16, IPSTR, IP2STR(&event->ip_info.gw));
			xSemaphoreGive(xSemaphoreWiFi);
		} else {
			ESP_LOGE(TAG, "got_ip_event_handler() lock error IP_EVENT_STA_GOT_IP");
		}
		break;

	case IP_EVENT_STA_LOST_IP:
		ESP_LOGW(TAG, "Lost IP address");
		xEventGroupClearBits(xEventGroupWifi, TASK_WIFI_HAS_IP);
		if (xSemaphoreTake(xSemaphoreWiFi, 25) == pdTRUE) {
			wifi_state->STA_ip[0] = '\0';
			wifi_state->STA_nm[0] = '\0';
			wifi_state->STA_gw[0] = '\0';
			wifi_state->STA_online = false;
			xSemaphoreGive(xSemaphoreWiFi);
		} else {
			ESP_LOGE(TAG, "got_ip_event_handler() lock error IP_EVENT_STA_LOST_IP");
		}
		break;

	default:
		ESP_LOGW(TAG, "Unknown IP event %d", (int)event_id);
		break;
    }
}


static void init_wifi(void)
{
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    xSemaphoreWiFi = xSemaphoreCreateMutex();

    /* initialize the tcp stack */
    ESP_ERROR_CHECK(esp_netif_init());
    sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif);

    wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&wifi_init_config));

    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, &instance_any_id) );
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,  ESP_EVENT_ANY_ID, &got_ip_event_handler, NULL, &instance_got_ip) );

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_start());

    xEventGroupClearBits(xEventGroupWifi, TASK_WIFI_STA_CONNECTED);
    xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_STA_DISCONNECTED);
}


void wifi_connect(void)
{
    ESP_LOGI(TAG, "wifi_connect() start");
    wifi_config_t	wifi_Config = {		///< Current STA configuration.
	.sta = {
	    .ssid = ESP_WIFI_SSID,
	    .password = ESP_WIFI_PASS,
	    .threshold.authmode = WIFI_AUTH_WPA2_PSK,
	},
    };
    // .threshold.authmode = WIFI_AUTH_WPA2_PSK,

    ESP_ERROR_CHECK(esp_wifi_disconnect());
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_Config) );

    xEventGroupClearBits(xEventGroupWifi, TASK_WIFI_STA_FAILED);
    esp_err_t wifierror = esp_wifi_connect();

    if (wifierror != ESP_OK) {
	ESP_LOGE(TAG, "esp_wifi_connect() rc=%04x %s", (int)wifierror, esp_err_to_name(wifierror));
	xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_STA_FAILED);
    } else {
	ESP_LOGI(TAG, "Connected Ok");
    }
    ESP_LOGI(TAG, "wifi_connect() done");
}


void task_wifi( void * pvParameters )
{
    ESP_LOGI(TAG, "Starting WiFi task");
    esp_log_level_set("wifi", ESP_LOG_ERROR);

    /*
     * memory allocation of objects used by the task 
     */
    wifi_state = malloc(sizeof(WIFI_State));
    memset(wifi_state, 0x00, sizeof(WIFI_State));

    /*
     * event group for the wifi driver
     */
    xEventGroupWifi = xEventGroupCreate();

    /*
     * init wifi as station
     */
    init_wifi();
    EventBits_t uxBits;
    int8_t tx_level;
    ESP_ERROR_CHECK(esp_wifi_get_max_tx_power(&tx_level));
    ESP_LOGI(TAG, "Startup completed, maximum transmit power %d dBm", tx_level / 4);

    for(;;) {

	/*
	 * Actions that can trigger: request a connection or a disconnection
	 */
	uxBits = xEventGroupWaitBits(xEventGroupWifi, TASK_WIFI_REQUEST_STA_CONNECT | TASK_WIFI_REQUEST_STA_DISCONNECT,	pdFALSE, pdFALSE, portMAX_DELAY );

	if (uxBits & TASK_WIFI_REQUEST_STA_DISCONNECT) {
	    /*
	     * user requested a disconnect, this will in effect disconnect the wifi
	     */
	    ESP_LOGI(TAG, "Request STA disconnect");
	    ESP_ERROR_CHECK(esp_wifi_disconnect());
	    xEventGroupWaitBits(xEventGroupWifi, TASK_WIFI_STA_DISCONNECTED, pdFALSE, pdTRUE, portMAX_DELAY );

	    /*
	     * Finally: release the request bit
	     */
	    xEventGroupClearBits(xEventGroupWifi, TASK_WIFI_REQUEST_STA_DISCONNECT);

	} else if (uxBits & TASK_WIFI_REQUEST_STA_CONNECT) {

	    ESP_LOGI(TAG, "Request STA connect `%s' `%s'", ESP_WIFI_SSID, ESP_WIFI_PASS);
	    wifi_connect();

	    /*
	     * 3 scenarios here: connection is successful and TASK_WIFI_STA_CONNECTED will be posted
	     * or it's a failure and we get a TASK_WIFI_STA_FAILED with a reason code.
	     * Or, option 3, the 5 seconds timeout is reached. This happens when the AP is not in range.
	     * Note that the reason code is not exploited. For all intent and purposes a failure is a failure.
	     */
	    uxBits = xEventGroupWaitBits(xEventGroupWifi, TASK_WIFI_STA_CONNECTED | TASK_WIFI_STA_FAILED, pdFALSE, pdFALSE, 5000 / portTICK_PERIOD_MS);

	    if (uxBits & (TASK_WIFI_STA_CONNECTED | TASK_WIFI_STA_FAILED)) {
		/*
		 * only save the config if the connection was successful!
		 */
		if (uxBits & TASK_WIFI_STA_FAILED) {
		    ESP_LOGI(TAG, "No AP found");
		    vTaskDelay(3000 / portTICK_PERIOD_MS);
		    ESP_LOGW(TAG, "Connection failed");
		    /* failed attempt to connect regardles of the reason */
            	}
	    }

	    /*
	     * Finally: release the request bit
	     */
            xEventGroupClearBits(xEventGroupWifi, TASK_WIFI_REQUEST_STA_CONNECT);
	}

    } /* for(;;) */
}

mercurial