main/task_wifi.c

Sat, 06 Jun 2020 13:28:46 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sat, 06 Jun 2020 13:28:46 +0200
changeset 77
66c77497d86d
parent 71
7a4446a77d09
child 87
47253f294a9f
permissions
-rw-r--r--

Changed the recipe database so that it is expandable, version 2. More mash fields and allow 16 steps. Allow 20 Additions. Removed separate mash steps from the state machine, the steps are moved to the runtime data. There is no fixed step number for mashout anymore. There is no fixed step for mash-in anymore, just use the first step and heat to the infusion temperature. After malt add, switch to the normal step temperature. Implemented decoction steps.

/**
 * @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;
}



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_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 WIFI_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;

	case WIFI_EVENT_STA_CONNECTED: {
		system_event_sta_connected_t* event = (wifi_event_sta_connected_t*) event_data;
		wifi_ap_record_t ap_info;
                esp_wifi_sta_get_ap_info(&ap_info);
            	ESP_LOGI(TAG, "Event STA connected, ssid:%s, bssid:" MACSTR ", channel:%d, rssi: %d, authmode:%s",
                       ap_info.ssid, MAC2STR(ap_info.bssid), event->channel, ap_info.rssi, apsec[event->authmode]);
		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);
		} 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;
		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, 25) == pdTRUE) {
		    wifi_state->STA_connected = false;
		    wifi_state->STA_online = false;
		    wifi_state->STA_rssi = 0;
		    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);
		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;
	}
    	case WIFI_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);
		} else {
                    ESP_LOGE(TAG, "wifi_event_handler() lock error WIFI_EVENT_AP_START");
                }
		break;

	case WIFI_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);
		} else {
                    ESP_LOGE(TAG, "wifi_event_handler() lock error WIFI_EVENT_AP_STOP");
                }
		break;

    	case WIFI_EVENT_AP_STACONNECTED: {
		if (xSemaphoreTake(xSemaphoreWiFi, 25) == pdTRUE) {
		    wifi_state->AP_clients++;
		    xSemaphoreGive(xSemaphoreWiFi);
		} else {
                    ESP_LOGE(TAG, "wifi_event_handler() lock error WIFI_EVENT_AP_STACONNECTED");
                }
		wifi_event_ap_staconnected_t* staconnected = (wifi_event_ap_staconnected_t*) event_data;
//		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 WIFI_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);
		} else {
                    ESP_LOGE(TAG, "wifi_event_handler() lock error WIFI_EVENT_AP_STADISCONNECTED");
                }
		wifi_event_ap_stadisconnected_t* stadisconnected = (wifi_event_ap_stadisconnected_t*) event_data;
//		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;
	}
	default:
		ESP_LOGW(TAG, "Unknown WiFi event %d", 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:
                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, "%s", ip4addr_ntoa(&event->ip_info.ip));
                    snprintf(wifi_state->STA_nm, 16, "%s", ip4addr_ntoa(&event->ip_info.netmask));
                    snprintf(wifi_state->STA_gw, 16, "%s", ip4addr_ntoa(&event->ip_info.gw));
                    xSemaphoreGive(xSemaphoreWiFi);
                } else {
                    ESP_LOGE(TAG, "got_ip_event_handler() lock error IP_EVENT_STA_GOT_IP");
            	}

                /*
                 * 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 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");
            	}
                sntp_stop();
                break;

	case IP_EVENT_AP_STAIPASSIGNED:
		ESP_LOGI(TAG, "IP_EVENT_AP_STAIPASSIGNED");
		break;

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



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);
	log_msg(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_create_default());

    /*
     * 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_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL) );
    ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID /*IP_EVENT_STA_GOT_IP*/, &got_ip_event_handler, NULL) );

    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