main/task_wifi.c

Thu, 04 Jul 2019 15:57:43 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Thu, 04 Jul 2019 15:57:43 +0200
changeset 61
c7b8a9931b59
parent 56
756d1a63d129
child 62
2e90ada37476
permissions
-rw-r--r--

Updated esp-ide. Changed some logging levels. Disabled support for termios.h

/**
 * @file task_wifi.c
 * @brief WiFi task. Connects to a known Access Point. If we know more then
 *        one AP, try to connect all of them until it succeeds (Not yet written).
 */


#include "config.h"


static const char *TAG = "task_wifi";


SemaphoreHandle_t       	xSemaphoreWiFi = NULL;		///< Semaphore WiFi task.
EventGroupHandle_t		xEventGroupWifi;		///< Events WiFi task.
uint16_t			ap_num = MAX_AP_NUM;		///< Scan counter.
wifi_ap_record_t		*accessp_records;		///< [MAX_AP_NUM] records array with scan results.
wifi_config_t			*task_wifi_ConfigSTA = NULL;	///< Current STA configuration.
WIFI_State			*wifi_state = NULL;		///< Public state for other tasks.

wifi_scan_config_t scan_config = {				///< WiFi scanner configuration.
	.ssid = 0,
	.bssid = 0,
	.channel = 0,
	.show_hidden = false
};

uint8_t				_wifi_ssid[33];			///< Current SSID
bool				_wifi_ScanAPs = false;		///< Scanning
bool				_wifi_ScanDone = false;		///< Scan ready
uint16_t			_wifi_Scanned = 0;		///< Total scanned APs.

extern int			Main_Screen;			///< Current Screen number.
extern sButton			Buttons[MAXBUTTONS];		///< Buttons definitions.
extern uint32_t			TimeSpent;			///< Counter that is increased each second.
extern bool			System_TimeOk;
extern time_t			now;
extern char			strftime_buf[64];
extern struct tm		timeinfo;

const int TASK_WIFI_REQUEST_WIFI_SCAN = BIT0;			///< When set, means a client requested to scan wireless networks.
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_AP_STARTED = BIT4;				///< Indicate that the SoftAP is started
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.


/**
 * @brief Local function, save station configuration.
 * @return Esp error code.
 */
esp_err_t SaveStaConfig(void);

/**
 * @brief Local function, fetch last connected station configuration.
 * @return True if there was a last connection, false if there was not.
 */
bool FetchStaConfig(void);

/**
 * @brief Local function, WiFi event handler.
 * @param ctx Context
 * @param event The event
 * @return Esp error code.
 */
esp_err_t task_wifi_EventHandler(void *ctx, system_event_t *event);

/**
 * @brief local callback function. Is called when the timesync is valid
 *        from a timeserver. Sets the global boolean System_TimeOk value. 
 * @param tv is the received time. Not used.
 */
void time_sync_notification_cb(struct timeval *tv);

/**
 * @brief Array with AP security names
 */
const char *apsec[] = { "Open", "WEP", "WPA", "WPA2", "WPA WPA2", "Enterprise" };


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



esp_err_t SaveStaConfig(void)
{
    int		record;

    if (task_wifi_ConfigSTA && strlen((char *)task_wifi_ConfigSTA->sta.ssid)) {
	/*
	 * Store in /spiffs/stations.conf if it's a new station.
	 */
	record = read_station(task_wifi_ConfigSTA->sta.ssid);
	if (record == -1) {
	    add_station(task_wifi_ConfigSTA->sta.ssid, task_wifi_ConfigSTA->sta.password);
	}

	/*
	 * Update main configuration if needed.
	 */
	if (strcmp(config.lastSSID, (char *)task_wifi_ConfigSTA->sta.ssid)) {
	    sprintf(config.lastSSID, "%s", task_wifi_ConfigSTA->sta.ssid);
	    write_config();
	}
	ESP_LOGD(TAG, "SaveStaConfig %s, record %d", wifiStation.SSID, record);
    }

    return ESP_OK;
}



bool FetchStaConfig(void)
{
    if (task_wifi_ConfigSTA == NULL) {
	task_wifi_ConfigSTA = (wifi_config_t*)malloc(sizeof(wifi_config_t));
    }
    memset(task_wifi_ConfigSTA, 0x00, sizeof(wifi_config_t));

    /*
     * Search last connected AP as station.
     */
    if (strlen(config.lastSSID) && (read_station((uint8_t *)config.lastSSID) >= 0)) {

	/* ssid */
	size_t sz = sizeof(task_wifi_ConfigSTA->sta.ssid);
	memcpy(task_wifi_ConfigSTA->sta.ssid, wifiStation.SSID, sz);

	/* password */
	sz = sizeof(task_wifi_ConfigSTA->sta.password);
	memcpy(task_wifi_ConfigSTA->sta.password, wifiStation.Password, sz);

	ESP_LOGI(TAG, "FetchStaConfig: last connected to ssid: %s", task_wifi_ConfigSTA->sta.ssid);
	return task_wifi_ConfigSTA->sta.ssid[0] != '\0';
    }

    ESP_LOGI(TAG, "FetchStaConfig: no last connection found");
    return false;
}



esp_err_t task_wifi_EventHandler(void *ctx, system_event_t *event)
{
    switch(event->event_id) {
	case SYSTEM_EVENT_SCAN_DONE:
		// Get the results so the memory used is freed.
		ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_num, accessp_records));
		_wifi_Scanned = ap_num;
		_wifi_ScanDone = true;
		break;

	case SYSTEM_EVENT_STA_START:
		// Set the configured hostname for the dhcp client.
		ESP_ERROR_CHECK(tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, config.hostname));   
		esp_wifi_connect();
		break;

	//   SYSTEM_EVENT_STA_STOP		3
	case SYSTEM_EVENT_STA_CONNECTED: {
		const system_event_sta_connected_t *connected = &event->event_info.connected;
            	ESP_LOGI(TAG, "Event STA connected, ssid:%s, ssid_len:%d, bssid:" MACSTR ", channel:%d, authmode:%s",
                       connected->ssid, connected->ssid_len, MAC2STR(connected->bssid), connected->channel, apsec[connected->authmode]);
		wifi_ap_record_t ap_info;
		esp_wifi_sta_get_ap_info(&ap_info);
		if (xSemaphoreTake(xSemaphoreWiFi, 25) == pdTRUE) {
		    wifi_state->STA_connected = true;
		    wifi_state->STA_rssi = ap_info.rssi;
		    sprintf(wifi_state->STA_ssid, "%s", ap_info.ssid);
		    xSemaphoreGive(xSemaphoreWiFi);
		}
		xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_STA_CONNECTED);
		xEventGroupClearBits(xEventGroupWifi, TASK_WIFI_STA_DISCONNECTED);
		break;
	}
	case SYSTEM_EVENT_STA_DISCONNECTED: {
		const system_event_sta_disconnected_t *disconnected = &event->event_info.disconnected;
		wifi_ap_record_t    ap;

            	ESP_LOGW(TAG, "Event STA disconnected, ssid:%s, ssid_len:%d, bssid:" MACSTR ", reason:%d",
                       disconnected->ssid, disconnected->ssid_len, MAC2STR(disconnected->bssid), disconnected->reason);
		if (xSemaphoreTake(xSemaphoreWiFi, 10) == pdTRUE) {
		    wifi_state->STA_connected = false;
		    wifi_state->STA_online = false;
		    wifi_state->STA_rssi = 0;
		    xSemaphoreGive(xSemaphoreWiFi);
		}
		xEventGroupClearBits(xEventGroupWifi, TASK_WIFI_STA_CONNECTED);
		xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_STA_DISCONNECTED);
		sntp_stop();
		
		if (disconnected->reason == WIFI_REASON_NO_AP_FOUND && ! _wifi_ScanAPs) {
		    ESP_LOGI(TAG, "Request scan for another AP");
		    _wifi_ScanAPs = true;
                    _wifi_ScanDone = false;
                    ap_num = MAX_AP_NUM;
                    ESP_ERROR_CHECK(esp_wifi_scan_start(&scan_config, false));
		    xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_REQUEST_STA_CONNECT);	// Keep looping active.
		    break;
		}
		if (disconnected->reason == WIFI_REASON_NO_AP_FOUND && _wifi_ScanAPs && _wifi_ScanDone) {
		    ESP_LOGD(TAG, "Scan completed, look for another AP, found:%d", _wifi_Scanned);
		    _wifi_ScanAPs = false;
                    _wifi_ScanDone = false;
		    for (int i = 0; i < _wifi_Scanned; i++) {
			ap = accessp_records[i];
			if ((read_station(ap.ssid) != -1)) {
			    if (wifiStation.hide) {
				continue;               // Blacklisted.
                            }
			    /* We know this one */
			    wifi_config_t* config = task_wifi_ConfigSTA;
                            memset(config, 0x00, sizeof(wifi_config_t));
                            memcpy(config->sta.ssid, wifiStation.SSID, strlen(wifiStation.SSID));
                            memcpy(config->sta.password, wifiStation.Password, strlen(wifiStation.Password));
			    ESP_LOGD(TAG, "new AP %s %s", wifiStation.SSID, wifiStation.Password);
                            xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_REQUEST_STA_CONNECT);
			    break;
			}
                    }
		    break;
		}

		/*
		 * Reconnect previous AP.
		 */
		if (FetchStaConfig()) {
		    xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_REQUEST_STA_CONNECT);
		}
		break;
	}
	//   SYSTEM_EVENT_STA_AUTHMODE_CHANGE	6
	case SYSTEM_EVENT_STA_GOT_IP:
		xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_HAS_IP);
		tcpip_adapter_ip_info_t ip;
		memset(&ip, 0, sizeof(tcpip_adapter_ip_info_t));
		if (tcpip_adapter_get_ip_info(ESP_IF_WIFI_STA, &ip) == 0) {
		    if (xSemaphoreTake(xSemaphoreWiFi, 10) == pdTRUE) {
			wifi_state->STA_online = true;
		    	snprintf(wifi_state->STA_ip, 16, IPSTR, IP2STR(&ip.ip));
			snprintf(wifi_state->STA_nm, 16, IPSTR, IP2STR(&ip.netmask));
			snprintf(wifi_state->STA_gw, 16, IPSTR, IP2STR(&ip.gw));
		    	xSemaphoreGive(xSemaphoreWiFi);
		    }
		}
		/*
		 * There doesn't seem to be support for configuring NTP via DHCP so
		 * we need to hardcode the ntp servers. The preffered server can be
		 * set via the setup screen. It should be on your LAN, else leave it
		 * empty. And if you are on a different lan someday, there is no extra
		 * delay because the hostname will not be found. 
		 */
		sntp_stop();
		if (strlen(config.ntp_server))
		    sntp_setservername(0, config.ntp_server);
		sntp_setservername(1, (char *)"pool.ntp.org"); // Will get you servers nearby
                sntp_set_sync_mode(SNTP_SYNC_MODE_IMMED);
                sntp_set_time_sync_notification_cb(time_sync_notification_cb);
                sntp_init();
#if 0
		if (strlen(config.ntp_server))
                    ESP_LOGI(TAG, "NTP server %s", sntp_getservername(0));
		ESP_LOGI(TAG, "NTP server %s", sntp_getservername(1));
#endif
		break;

	case SYSTEM_EVENT_STA_LOST_IP:
		ESP_LOGW(TAG, "Lost IP address");
		xEventGroupClearBits(xEventGroupWifi, TASK_WIFI_HAS_IP);
		if (xSemaphoreTake(xSemaphoreWiFi, 10) == 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);
		}
		sntp_stop();
		break;

	//   SYSTEM_EVENT_STA_WPS_ER_SUCCESS	9
	//   SYSTEM_EVENT_STA_WPS_ER_FAILED	10
	//   SYSTEM_EVENT_STA_WPS_ER_TIMEOUT	11
	//   SYSTEM_EVENT_STA_WPS_ER_PIN	12

    	case SYSTEM_EVENT_AP_START:
    		xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_AP_STARTED);
		if (xSemaphoreTake(xSemaphoreWiFi, 25) == pdTRUE) {
		    wifi_state->AP_active = true;
		    wifi_state->AP_clients = 0;
		    xSemaphoreGive(xSemaphoreWiFi);
		}
		break;

	case SYSTEM_EVENT_AP_STOP:
		xEventGroupClearBits(xEventGroupWifi, TASK_WIFI_AP_STARTED);
		if (xSemaphoreTake(xSemaphoreWiFi, 25) == pdTRUE) {
		    wifi_state->AP_active = false;
		    wifi_state->AP_clients = 0;
		    xSemaphoreGive(xSemaphoreWiFi);
		}
		break;

    	case SYSTEM_EVENT_AP_STACONNECTED: {
		if (xSemaphoreTake(xSemaphoreWiFi, 25) == pdTRUE) {
		    wifi_state->AP_clients++;
		    xSemaphoreGive(xSemaphoreWiFi);
		}
		const system_event_ap_staconnected_t *staconnected = &event->event_info.sta_connected;
            	ESP_LOGI(TAG, "Event AP connected, mac:" MACSTR ", aid:%d, conns:%d",
                       MAC2STR(staconnected->mac), staconnected->aid, wifi_state->AP_clients);
		break;
	}
    	case SYSTEM_EVENT_AP_STADISCONNECTED: {
		if (xSemaphoreTake(xSemaphoreWiFi, 25) == pdTRUE) {
		    if (wifi_state->AP_clients > 0)
		    	wifi_state->AP_clients--;
		    else
		    	wifi_state->AP_clients = 0;
		    xSemaphoreGive(xSemaphoreWiFi);
		}
		const system_event_ap_stadisconnected_t *stadisconnected = &event->event_info.sta_disconnected;
            	ESP_LOGI(TAG, "Event AP disconnected, mac:" MACSTR ", aid:%d, conns:%d",
                       MAC2STR(stadisconnected->mac), stadisconnected->aid, wifi_state->AP_clients);
		break;
	}
	case SYSTEM_EVENT_AP_STAIPASSIGNED:
		break;

	//   SYSTEM_EVENT_AP_PROBEREQRECVED	18
	//   SYSTEM_EVENT_GOT_IP6		19
	//   SYSTEM_EVENT_ETH_START		20
	//   SYSTEM_EVENT_ETH_STOP		21
	//   SYSTEM_EVENT_ETH_CONNECTED		22
	//   SYSTEM_EVENT_ETH_DISCONNECTED	23
	//   SYSTEM_EVENT_ETH_GOT_IP		24

	default:
		printf("Unknown event %d\n", event->event_id);
        	break;
    }
    return ESP_OK;
}



void time_sync_notification_cb(struct timeval *tv)
{
    int rc = sntp_get_sync_status();

    if (rc == SNTP_SYNC_STATUS_COMPLETED) {
	time(&now);
        localtime_r(&now, &timeinfo);
	System_TimeOk = true;
        strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
        ESP_LOGI(TAG, "NTP time is set: %s", strftime_buf);
    } else {
    	ESP_LOGI(TAG, "NTP unknown time sync event rc=%d", rc);
    }
}



void task_wifi( void * pvParameters )
{
    esp_err_t		ret;

    ESP_LOGI(TAG, "Starting WiFi");

    /*
     * Initialize NVS
     */
    ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
	ESP_ERROR_CHECK(nvs_flash_erase());
	ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    /* event handler and event group for the wifi driver */
    xEventGroupWifi = xEventGroupCreate();
    /* initialize the tcp stack */
    tcpip_adapter_init();
    ESP_ERROR_CHECK(esp_event_loop_init(task_wifi_EventHandler, NULL));

    /*
     * memory allocation of objects used by the task 
     */
    accessp_records = (wifi_ap_record_t*)malloc(sizeof(wifi_ap_record_t) * MAX_AP_NUM);
    task_wifi_ConfigSTA = (wifi_config_t*)malloc(sizeof(wifi_config_t));
    memset(task_wifi_ConfigSTA, 0x00, sizeof(wifi_config_t));

    xSemaphoreWiFi = xSemaphoreCreateMutex();
    wifi_state = malloc(sizeof(WIFI_State));
    wifi_state->AP_clients = 0;
    wifi_state->AP_active = false;
    wifi_state->STA_connected = false;
    wifi_state->STA_online = false;
    wifi_state->STA_rssi = 0;

    /*
     * start the softAP access point
     * stop DHCP server
     */
    ESP_ERROR_CHECK(tcpip_adapter_dhcps_stop(TCPIP_ADAPTER_IF_AP));

    /*
     * Assign a static IP to the AP network interface
     */
    tcpip_adapter_ip_info_t info;
    memset(&info, 0x00, sizeof(info));
    IP4_ADDR(&info.ip, 192, 168, 1, 1);
    IP4_ADDR(&info.gw, 192, 168, 1, 1);
    IP4_ADDR(&info.netmask, 255, 255, 255, 0);
    ESP_ERROR_CHECK(tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_AP, &info));

    /* start dhcp server */
    ESP_ERROR_CHECK(tcpip_adapter_dhcps_start(TCPIP_ADAPTER_IF_AP));
    ESP_LOGI(TAG, "AP start dhcps ip: 192.168.1.1 nm: 255.255.255.0 gw: 192.168.1.1");

    /* start dhcp client */
    ESP_ERROR_CHECK(tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA));

    /*
     * init wifi as station + access point
     */
    wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&wifi_init_config));
    ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));
    ESP_ERROR_CHECK(esp_wifi_set_bandwidth(WIFI_IF_AP, config.ap_bandwidth));

    // configure the softAP and start it */
    wifi_config_t ap_config = {
	.ap = {
	    .ssid_len = 0,
	    .channel = config.ap_channel,
	    .authmode = WIFI_AUTH_WPA2_PSK,
	    .ssid_hidden = config.ap_ssid_hidden,
	    .max_connection = AP_MAX_CONNECTIONS,
	    .beacon_interval = 100,
	},
    };
    memcpy(ap_config.ap.ssid, config.ap_ssid , sizeof(config.ap_ssid));
    memcpy(ap_config.ap.password, config.ap_pwd, sizeof(config.ap_pwd));
    ret = esp_wifi_set_config(WIFI_IF_AP, &ap_config);
    if (ret != ESP_OK) {
	ESP_LOGE(TAG, "esp_wifi_set_config(WIFI_IF_AP, nnn) rc=%d", ret);
    }
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "AP start ssid:`%s' pwd:`%s' channel:%d, hidden:%s", 
		    ap_config.ap.ssid, ap_config.ap.password, ap_config.ap.channel, ap_config.ap.ssid_hidden ? "yes":"no");

    /*
     * try to get access to previously saved wifi
     */
    if (FetchStaConfig()) {
	xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_REQUEST_STA_CONNECT);
    }

    /* 
     * Wait for access point to start
     */
    xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_STA_DISCONNECTED);
    xEventGroupWaitBits(xEventGroupWifi, TASK_WIFI_AP_STARTED, pdFALSE, pdTRUE, portMAX_DELAY );
    EventBits_t uxBits;

    for(;;) {

	/* actions that can trigger: request a connection, a scan, or a disconnection */
	uxBits = xEventGroupWaitBits(xEventGroupWifi, 
		                     TASK_WIFI_REQUEST_STA_CONNECT | TASK_WIFI_REQUEST_WIFI_SCAN | 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 but also erase NVS memory
	     */
	    ESP_LOGI(TAG, "Request STA disconnect");
	    sntp_stop();
	    ESP_ERROR_CHECK(esp_wifi_disconnect());
	    xEventGroupWaitBits(xEventGroupWifi, TASK_WIFI_STA_DISCONNECTED, pdFALSE, pdTRUE, portMAX_DELAY );
	    
	    /*
	     * erase configuration
	     */
	    if (task_wifi_ConfigSTA) {
		memset(task_wifi_ConfigSTA, 0x00, sizeof(wifi_config_t));
	    }
	    config.lastSSID[0] = '\0';
	    write_config();

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

	} else if (uxBits & TASK_WIFI_REQUEST_STA_CONNECT) {

	    //someone requested a connection!
	    ESP_LOGI(TAG, "Request STA connect `%s'", task_wifi_ConfigSTA->sta.ssid);
	    /* set the new config and connect - reset the failed bit first as it is later tested */
	    xEventGroupClearBits(xEventGroupWifi, TASK_WIFI_STA_FAILED);
	    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, task_wifi_ConfigSTA));

            esp_err_t wifierror = esp_wifi_connect();
	    if (wifierror != ESP_OK) {
	    	ESP_LOGE(TAG, "esp_wifi_connect() rc=%04x", (int)wifierror);
		xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_STA_FAILED);
	    }

	    /* 
	     * 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.
	     */
//	    ESP_LOGI(TAG, "2 wait for %08x", TASK_WIFI_STA_CONNECTED | TASK_WIFI_STA_FAILED);
	    uxBits = xEventGroupWaitBits(xEventGroupWifi, TASK_WIFI_STA_CONNECTED | TASK_WIFI_STA_FAILED, pdFALSE, pdFALSE, 5000 / portTICK_PERIOD_MS);
//	    ESP_LOGI(TAG, "2 waitbits %08x", uxBits & (TASK_WIFI_STA_CONNECTED | TASK_WIFI_STA_FAILED));

	    if (uxBits & (TASK_WIFI_STA_CONNECTED | TASK_WIFI_STA_FAILED)) {
		/* 
		 * only save the config if the connection was successful! 
		 */
		if (uxBits & TASK_WIFI_STA_CONNECTED) {
		    /* save wifi config */
		    SaveStaConfig();
		} else {
		    ESP_LOGW(TAG, "Connection failed");
		    /* failed attempt to connect regardles of the reason */

		    /* otherwise: reset the config */
		    memset(task_wifi_ConfigSTA, 0x00, sizeof(wifi_config_t));
		}
		xEventGroupClearBits(xEventGroupWifi, TASK_WIFI_REQUEST_STA_CONNECT);
	    } else {
		/* hit 10 seconds timeout */
		ESP_LOGW(TAG, "Connection timeout");
		xEventGroupClearBits(xEventGroupWifi, TASK_WIFI_REQUEST_STA_CONNECT);
                vTaskDelay(100 / portTICK_PERIOD_MS);
		xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_REQUEST_STA_CONNECT);
	    }

	} else if (uxBits & TASK_WIFI_REQUEST_WIFI_SCAN) {

	    /* safe guard against overflow */
	    ap_num = MAX_AP_NUM;
	    ESP_ERROR_CHECK(esp_wifi_scan_start(&scan_config, false));

	    /* finally: release the scan request bit */
	    xEventGroupClearBits(xEventGroupWifi, TASK_WIFI_REQUEST_WIFI_SCAN);
	}

    } /* for(;;) */
    vTaskDelay( (TickType_t)10);
}



/**
 * @brief Show an AP station as a button. The buttons are already defined.
 * @param idx The index position on the display, 1 to 7.
 * @param ap The AP information from the WiFi scan.
 * @param show How to display. 0 is blank, 1 is unknown, 2 is known, 3 is a connected AP.
 */
void Show_AP(uint8_t idx, wifi_ap_record_t ap, int show)
{
    char	tmp[33];

    if ((idx > 7) || (idx < 1))
	return;

    if (show == 0) {
	_bg = TFT_BLACK;
    } else {
    	_bg = (color_t){ 63, 63, 64 };
    }

    TFT_fillRect(Buttons[idx].x, Buttons[idx].y, Buttons[idx].w, Buttons[idx].h, _bg);
    if (show == 0)
	return;

    TFT_drawRect(Buttons[idx].x, Buttons[idx].y, Buttons[idx].w, Buttons[idx].h, TFT_NAVY);
    if (show == 3) {
	_fg = TFT_WHITE;
    } else if (show == 1) {
    	_fg = TFT_YELLOW;
    } else {
	_fg = TFT_CYAN;
    }

    TFT_setFont(DEJAVU18_FONT, NULL);
    sprintf(tmp, "%s", ap.ssid);
    TFT_print(tmp, Buttons[idx].x + 5, Buttons[idx].y + 6);
    sprintf(tmp, "%d", ap.rssi);
    TFT_setFont(DEF_SMALL_FONT, NULL);
    TFT_print(tmp, Buttons[idx].x + Buttons[idx].w - (TFT_getStringWidth(tmp) + 5), Buttons[idx].y + 4);
    sprintf(tmp, "%s", apsec[ap.authmode]);
    TFT_print(tmp, Buttons[idx].x + Buttons[idx].w - (TFT_getStringWidth(tmp) + 5), Buttons[idx].y + 15);
}



bool WiFi_Init(void)
{
    char	pwd[65], pmpt[96];

    switch (Main_Screen) {
	case MAIN_TOOLS_SETUP_WIFI:
			TopMessage((char *)"WiFi");
			TFT_setFont(DEJAVU24_FONT, NULL);
			_fg = TFT_WHITE;
			TFT_print((char *)"Momentje ..", CENTER, CENTER);
			_wifi_ScanAPs = true;
			_wifi_ScanDone = false;
			Buttons_Add(260, 200, 60, 40, (char *)"Ok", 0);
			Buttons[0].dark = true;
			Buttons_Show();
			// Now add the buttons we draw manually.
			Buttons_Add(  0, 30, 250, 30, (char *)"", 1);
			Buttons_Add(  0, 60, 250, 30, (char *)"", 2);
			Buttons_Add(  0, 90, 250, 30, (char *)"", 3);
			Buttons_Add(  0,120, 250, 30, (char *)"", 4);
			Buttons_Add(  0,150, 250, 30, (char *)"", 5);
			Buttons_Add(  0,180, 250, 30, (char *)"", 6);
			Buttons_Add(  0,210, 250, 30, (char *)"", 7);
			break;

	case MAIN_TOOLS_SETUP_WIFI_CUR:
			TopMessage((char *)"WiFi verbinding");
			// Get extra information.
			wifi_ap_record_t ap_info;
			esp_wifi_sta_get_ap_info(&ap_info);

			wifi_config_t *wconfig = task_wifi_ConfigSTA /*task_wifi_GetWifiStaConfig( ) */;
			if (wconfig) {

			    tcpip_adapter_ip_info_t ip_info;
			    ESP_ERROR_CHECK(tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info));
			    char ip[IP4ADDR_STRLEN_MAX];
			    char gw[IP4ADDR_STRLEN_MAX];
			    char netmask[IP4ADDR_STRLEN_MAX];
			    strcpy(ip, ip4addr_ntoa(&ip_info.ip));
			    strcpy(netmask, ip4addr_ntoa(&ip_info.netmask));
			    strcpy(gw, ip4addr_ntoa(&ip_info.gw));
			    TFT_setFont(DEFAULT_FONT, NULL);
			    _fg = TFT_WHITE;
			    TFT_print((char *)"SSID", 155 - TFT_getStringWidth((char *)"SSID"), 40);
			    TFT_print((char *)"Kanaal", 155 - TFT_getStringWidth((char *)"Kanaal"), 60);
			    TFT_print((char *)"Rssi", 155 - TFT_getStringWidth((char *)"Rssi"), 80);
			    TFT_print((char *)"Mode", 155 - TFT_getStringWidth((char *)"Mode"), 100);
			    TFT_print((char *)"IP adres", 155 - TFT_getStringWidth((char *)"IP adres"), 120);
			    TFT_print((char *)"Netmask", 155 - TFT_getStringWidth((char *)"Netmask"), 140);
			    TFT_print((char *)"Gateway", 155 - TFT_getStringWidth((char *)"Gateway"), 160);
			    _fg = TFT_YELLOW;
			    TFT_print((char*)wconfig->sta.ssid, 165, 40);
			    sprintf(pmpt, "%d", ap_info.primary);
			    TFT_print(pmpt, 165, 60);
			    sprintf(pmpt, "%d", ap_info.rssi);
			    TFT_print(pmpt, 165, 80);
			    sprintf(pmpt, "%s%s%s", ap_info.phy_11b ? "b":"", ap_info.phy_11g ? "g":"", ap_info.phy_11n ? "n":"");
			    TFT_print(pmpt, 165, 100);
			    TFT_print((char*)ip, 165, 120);
			    TFT_print((char*)netmask, 165, 140);
			    TFT_print((char*)gw, 165, 160);
			}
			Buttons_Add(130, 200, 60, 40, (char *)"Ok", 0);
			Buttons[0].dark = true;
			Buttons_Show();
			break;

	case MAIN_TOOLS_SETUP_WIFI_CON:
			TopMessage((char *)"WiFi verbinden");
			TFT_setFont(DEJAVU18_FONT, NULL);
			_fg = TFT_WHITE;
			TFT_print((char *)"SSID", 155 - TFT_getStringWidth((char *)"SSID"), 70);
			_fg = TFT_YELLOW;
			TFT_print((char*)_wifi_ssid, 165, 70);
			Buttons_Add(  0, 200, 100, 40, (char *)"Annuleer", 0);
			Buttons_Add(110, 200, 100, 40, (char *)"Vergeet",  1);
			Buttons_Add(220, 200, 100, 40, (char *)"Verbind",  2);
			Buttons_Show();
			Buttons[0].dark = true;
			break;

	case MAIN_TOOLS_SETUP_WIFI_NEW:
			TopMessage((char *)"WiFi nieuw");
			snprintf(pmpt, 95, "Password for %s", _wifi_ssid);
			pwd[0] = '\0';
			EditTextMin(pmpt, pwd, 64, 8);
			/*
			 * Disconnect first
			 */
			_bg = TFT_BLACK;
			TFT_fillScreen(_bg);
			TFT_setFont(DEJAVU24_FONT, NULL);
			_fg = TFT_WHITE;
			TFT_print((char *)"Momentje ..", CENTER, CENTER);
			xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_REQUEST_STA_DISCONNECT);
			vTaskDelay(100 / portTICK_PERIOD_MS);
			xEventGroupWaitBits(xEventGroupWifi, TASK_WIFI_STA_DISCONNECTED, pdFALSE, pdTRUE, portMAX_DELAY );

			/*
			 * Setup new connection
			 */
			if (strlen(pwd)) {
			    wifi_config_t* config = task_wifi_ConfigSTA;
			    memset(config, 0x00, sizeof(wifi_config_t));
			    memcpy(config->sta.ssid, _wifi_ssid, strlen((char*)_wifi_ssid));
			    memcpy(config->sta.password, pwd, strlen(pwd));
			    ESP_LOGI(TAG, "new AP %s %s", _wifi_ssid, pwd);
			    xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_REQUEST_STA_CONNECT);
			} else {
			    // TODO: what about WPS, it works but how to insert it in this app.
			    return true;
			}
			// We must wait here for the result.
			break;

	default:
			break;
    }

    return false;
}


bool WiFi_Loop(void)
{
    uint8_t		idx;
    int			Choice;
    static int		AP[8];
    wifi_ap_record_t	ap;

    switch (Main_Screen) {
	case MAIN_TOOLS_SETUP_WIFI:
			if (_wifi_ScanAPs) {
			    xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_REQUEST_WIFI_SCAN);
			    _wifi_ScanAPs = false;
			    TimeSpent = 0;
			}
			if (_wifi_ScanDone) {
			    /*
			     * Show scan results. There is room for 7 entries. If we have a connection,
			     * the first one is that connection followed by available Access Points.
			     * If there is no connection yet, there is only a iist of available Access
			     * points.
			     * The list is sorted by signal strength and is filled by the eventhandler.
			     */
			    idx = 1;
			    _wifi_ScanDone = false;

			    if ((xEventGroupGetBits(xEventGroupWifi) & TASK_WIFI_STA_CONNECTED) == TASK_WIFI_STA_CONNECTED) {
				// We are connected. Search and display this one.
				if (xSemaphoreTake(xSemaphoreWiFi, 25) == pdTRUE) {
				    for (int i = 0; i < _wifi_Scanned; i++) {
					ap = accessp_records[i];
					if (strcmp(wifi_state->STA_ssid, (char *)ap.ssid) == 0) {
					    AP[idx] = i;
					    Show_AP(idx, ap, 3);
					    idx++;
					    break;
					}
				    }
				    xSemaphoreGive(xSemaphoreWiFi);
				}
			    }

			    // Display available Access Points.
			    for (int i = 0; i < _wifi_Scanned; i++) {
				// The connected AP can be somewhere in this list, don't display it again.
				ap = accessp_records[i];
				if (xSemaphoreTake(xSemaphoreWiFi, 25) == pdTRUE) {
				    if (strcmp(wifi_state->STA_ssid, (char *)ap.ssid) == 0) {
					xSemaphoreGive(xSemaphoreWiFi);
					continue;	// Skip connected AP, already on top of the list.
				    }
				    xSemaphoreGive(xSemaphoreWiFi);
				}
				// Check if we know this AP in the database.
				if ((read_station(ap.ssid) == -1)) {
				    Show_AP(idx, ap, 2);	// Unknown
				} else {
				    if (wifiStation.hide) {
					continue;		// Blacklisted.
				    }
				    Show_AP(idx, ap, 1);	// We know this one.
				}
				AP[idx] = i;
				idx++;
				if (idx ==  8)
				    break;
			    }
			    if (idx < 7) {
				for (int i = idx; i < 8; i++) {
				    Show_AP(i, ap, 0);
				    AP[i] = 0;
				}
			    }
			}

			Choice = Buttons_Scan();
			if ((Choice >= 1) && (Choice <= 7)) {
			    ap = accessp_records[AP[Choice]];
			    sprintf((char *)_wifi_ssid, "%s", ap.ssid);		// Save selected SSID.
			}
			if ((Choice == 1) && ((xEventGroupGetBits(xEventGroupWifi) & TASK_WIFI_STA_CONNECTED) == TASK_WIFI_STA_CONNECTED)) {
			    Main_Screen = MAIN_TOOLS_SETUP_WIFI_CUR;
			    // Cancel a possible scan.
			    ESP_ERROR_CHECK(esp_wifi_scan_stop());
			} else if ((Choice >= 1) && (Choice <= 7)) {
			    if ((read_station(_wifi_ssid) != -1)) {
				Main_Screen = MAIN_TOOLS_SETUP_WIFI_CON;
			    } else {
				Main_Screen = MAIN_TOOLS_SETUP_WIFI_NEW;
			    }
			    ESP_ERROR_CHECK(esp_wifi_scan_stop());
			} else if (Choice == 0) {
			    Main_Screen = MAIN_TOOLS_SETUP;
			    _wifi_ScanAPs = false;
			    _wifi_ScanDone = false;
			    ESP_ERROR_CHECK(esp_wifi_scan_stop());
			} else if (TimeSpent > 10) {
			     _wifi_ScanAPs = true;
			}
			break;

	case MAIN_TOOLS_SETUP_WIFI_CUR:

			if (Buttons_Scan() == 0) {
			    Main_Screen = MAIN_TOOLS_SETUP_WIFI;
			}
			break;

	case MAIN_TOOLS_SETUP_WIFI_CON:
			switch (Buttons_Scan()) {
			    case 0:	// Cancel choice
				    	Main_Screen = MAIN_TOOLS_SETUP_WIFI;
					break;

			    case 1:	// Forget connection
					remove_station(_wifi_ssid);
					Main_Screen = MAIN_TOOLS_SETUP_WIFI;
					break;

			    case 2:	// Connect
					_bg = TFT_BLACK;
					TFT_fillScreen(_bg);
					TFT_setFont(DEJAVU24_FONT, NULL);
					_fg = TFT_WHITE;
					TFT_print((char *)"Momentje ..", CENTER, CENTER);
					/*
					 * Disconnect old connections and wait until it's gone.
					 */
					xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_REQUEST_STA_DISCONNECT);
					vTaskDelay(100 / portTICK_PERIOD_MS);
					xEventGroupWaitBits(xEventGroupWifi, TASK_WIFI_STA_DISCONNECTED, pdFALSE, pdTRUE, portMAX_DELAY );
					/*
					 * Setup new connection.
					 */
					if ((read_station(_wifi_ssid) != -1)) {
					    wifi_config_t* config = task_wifi_ConfigSTA;
					    memset(config, 0x00, sizeof(wifi_config_t));
					    memcpy(config->sta.ssid, wifiStation.SSID, strlen(wifiStation.SSID));
					    memcpy(config->sta.password, wifiStation.Password, strlen(wifiStation.Password));
					    xEventGroupSetBits(xEventGroupWifi, TASK_WIFI_REQUEST_STA_CONNECT);
					    vTaskDelay(1000 / portTICK_PERIOD_MS);
					}
					Main_Screen = MAIN_TOOLS_SETUP_WIFI;
					break;

			    default:	break;
			}
			break;
												         
	case MAIN_TOOLS_SETUP_WIFI_NEW:
			// All work is already done, jump to the base screen.
			Main_Screen = MAIN_TOOLS_SETUP_WIFI;
			break;

	default:
			break;
    }

    return false;
}

mercurial