/****************************************************************************** * 2016 IdeasX v0.3.1 Module Firmware * * File Name: wifi.c * Author: Tyler Berezowsky * Description: This file is going to need alot of help by a C wizard, but it currently works...I think. * * 2016/8/8, v1.0 created this file *******************************************************************************/ #include "util/wifi.h" #include "esp_common.h" #define WIFI_TIMEOUT 30*1000 // time waited before swiping left in seconds #define WIFI_RECONNECT_LIMIT 5 // number of times connection to AP is attempted before moving on MQTT_Client mqttClient; // setup in user_main.c static struct station_config stationConf; static WIFI_PROCESS_FLAGS wifi_process_flags; static ETSTimer wifi_process_timer; static struct bss_info *bss_link, *head_bss_link; static void ICACHE_FLASH_ATTR wifi_process(void); static void ICACHE_FLASH_ATTR wifi_process_timer_cb(void); /****************************************************************************** * FunctionName : destory_linked_list * Description : * Parameters : * Returns : *******************************************************************************/ static bool ICACHE_FLASH_ATTR destory_linked_list(void) { if(head_bss_link != NULL) { while(head_bss_link != NULL) { bss_link = head_bss_link; head_bss_link = head_bss_link->next.stqe_next; os_free(bss_link); } return TRUE; } else { return FALSE; } } /****************************************************************************** * FunctionName : copy_linked_list * Description : * Parameters : * Returns : *******************************************************************************/ static struct bss_info* ICACHE_FLASH_ATTR copy_linked_list(struct bss_info *ptr_original_node) { struct bss_info *ptr_new_head, *ptr_new_node; if(ptr_original_node != NULL) { ptr_new_head = (struct bss_info *)os_malloc(sizeof(struct bss_info)); if (ptr_new_head == NULL) { os_printf("WIFI PROCESS: No RAM!\r\n"); return NULL; } } else return NULL; os_memcpy(ptr_new_head, ptr_original_node, sizeof(struct bss_info)); ptr_original_node = ptr_original_node->next.stqe_next; ptr_new_node = ptr_new_head; while(ptr_original_node != NULL) { ptr_new_node->next.stqe_next = (struct bss_info *)os_malloc(sizeof(struct bss_info)); ptr_new_node = ptr_new_node->next.stqe_next; os_memcpy(ptr_new_node, ptr_original_node, sizeof(struct bss_info)); if (ptr_new_node == NULL) { os_printf("WIFI PROCESS: No RAM!\r\n"); while(ptr_new_head != NULL) { ptr_new_node = ptr_new_head; ptr_new_head = ptr_new_head->next.stqe_next; os_free(ptr_new_node); } os_printf("WIFI PROCESS: Cleaned up list\r\n"); return NULL; } ptr_original_node = ptr_original_node->next.stqe_next; } return ptr_new_head; } /****************************************************************************** * FunctionName : wifi_set_station * Description : * Parameters : * Returns : *******************************************************************************/ void ICACHE_FLASH_ATTR wifi_set_station(uint8_t* ssid, uint8_t* pass) { bss_link = NULL; os_sprintf(stationConf.ssid, "%s", ssid); // load ssid into stationConf.ssid os_sprintf(stationConf.password, "%s", pass); // load pass into stationConf.password wifi_station_set_config_current(&stationConf); // connect to AP specified in stationConf wifi_station_set_auto_connect(TRUE); wifi_station_connect(); } /****************************************************************************** * FunctionName : disable_wifi_reconnect * Description : * Parameters : * Returns : *******************************************************************************/ void ICACHE_FLASH_ATTR disable_wifi_reconnect(void) { wifi_station_disconnect(); wifi_station_set_auto_connect(FALSE); } /****************************************************************************** * FunctionName : show_wifi_config * Description : print current wifi configurations * Parameters : * Returns : *******************************************************************************/ void ICACHE_FLASH_ATTR show_wifi_config(void) { uint8_t i, j; j = sysCfg.registered_stations; os_printf("Number of APs stored: %d\r\n", j); for (i=0;issid) <= 32) { os_memcpy(ssid, bss_link->ssid, os_strlen(bss_link->ssid)); } else { os_memcpy(ssid, bss_link->ssid, 32); } // compare current ssid from bss_link to registered stations for(i=0;inext.stqe_next; // inc to next station in case process fails //set wifi_process_timer for timeout. os_timer_setfn(&wifi_process_timer, (os_timer_func_t *)wifi_process_timer_cb, NULL); os_timer_arm(&wifi_process_timer, WIFI_TIMEOUT, 0); // arm timeout timer break; // break for loop } } // break while if station found, otherwise look for another station. if (wifi_process_flags.wifi_flag == TRUE) { break; // break while loop } else { bss_link = bss_link->next.stqe_next; } } } if ((wifi_process_flags.wifi_flag == FALSE) && (bss_link == NULL)) { // this is called twice...which could be a problem. sysCfg.module_state.searching = FALSE; sysCfg.module_state.asleep = FALSE; destory_linked_list(); os_printf("WIFI PROCESS: Station list removed\r\n"); os_printf("WIFI PROCESS: No Wi-Fi APs available.\r\n"); // try again in a second....three times...if motion flag is set keep trying otherwise you should goto sleep. if (lsm6ds3_read_motion()) // change this to a poll on the LSM6DS3 line. start_wifi_process(); else { GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 0xFFFFFFFF); // clear pending interrupts sysCfg.module_state.asleep = TRUE; ETS_GPIO_INTR_ENABLE(); // enable interrupts to permit PB and LSM6DS3 to wake ESP from sleep. /* * The ESP8266 should be placed into light sleep or modem sleep here */ } } } /****************************************************************************** * FunctionName : wifi_handle_event_cb * Description : callback function for wifi events. * Parameters : * Returns : *******************************************************************************/ static void ICACHE_FLASH_ATTR wifi_handle_event_cb(System_Event_t *evt) { switch (evt->event) { case EVENT_STAMODE_CONNECTED: { wifi_process_flags.wifi_count = 0; // rst wifi connection cnt os_printf("WIFI PROCESS: connect to ssid %s, channel %d\n", evt->event_info.connected.ssid, evt->event_info.connected.channel); break; } case EVENT_STAMODE_DISCONNECTED: { /* There is an issue where if the module is connected to an AP and then loses it, it continously scans for the last access point it attempted to connect to. */ sysCfg.module_state.connected = FALSE; // set global connected flag false sysCfg.module_state.searching = TRUE; /* The only way to cancel the auto-connect functionaity of the SDK's OS is to send the command wifi_station_disconnect(), this ALWAYS calles the wifi event handler. Checking for this flag lets us ignore it. */ if (wifi_process_flags.wifi_flag == TRUE) { MQTT_Disconnect(&mqttClient); wifi_process_flags.wifi_count++; os_printf("WIFI PROCESS: disconnect from ssid %s, reason %d, attempt %d/%d\r\n", evt->event_info.disconnected.ssid, evt->event_info.disconnected.reason, wifi_process_flags.wifi_count, WIFI_RECONNECT_LIMIT); if (wifi_process_flags.wifi_count >= WIFI_RECONNECT_LIMIT) // allow WIFI_RECONNECT_LIMIT for reconnect and 1st attempts { if (wifi_process_flags.ip_flag == TRUE) // if was connected, but then lost connection { start_wifi_process(); // rst apd mode process, flags will automatically be reset } else // if never got a connection finish looking at all the available stations. { wifi_process_flags.wifi_flag = FALSE; wifi_station_set_auto_connect(FALSE); wifi_station_disconnect(); wifi_process(); } } } break; } case EVENT_STAMODE_AUTHMODE_CHANGE: { os_printf("WIFI PROCESS: mode: %d -> %d\n", evt->event_info.auth_change.old_mode, evt->event_info.auth_change.new_mode); break; } case EVENT_STAMODE_GOT_IP: { os_timer_disarm(&wifi_process_timer); // disarm timeout timer wifi_process_flags.ip_flag = TRUE; sysCfg.module_state.connected = TRUE; // set global connected flag sysCfg.module_state.searching = FALSE; // set global searching flag false os_printf("WIFI PROCESS: ip:" IPSTR ",mask:" IPSTR ",gw:" IPSTR, IP2STR(&evt->event_info.got_ip.ip), IP2STR(&evt->event_info.got_ip.mask), IP2STR(&evt->event_info.got_ip.gw)); destory_linked_list(); os_printf("\r\nWIFI PROCESS: Station list removed\r\n"); lsm6ds3_motion_disable(); // disable LSM6DS3 significant motion functionality MQTT_Connect(&mqttClient); // start MQTT client break; } default: { os_printf("WIFI PROCESS: Oh, crap. I have no idea what is going on,\r\n"); break; } } } /****************************************************************************** * FunctionName : wifi_station_scan_done * Description : Cb function of scan, organizes a linked list of stations from scan function. Make sure to free linked list when done. * Parameters : * Returns : *******************************************************************************/ static void ICACHE_FLASH_ATTR wifi_station_scan_done(void *arg, STATUS status) { uint8_t ssid[33]; uint8_t bssid[6]; if (status == OK) // if the scan was sucessful { bss_link = (struct bss_info *)arg; struct bss_info *bss_link_next, *bss_link_curr, *bss_link_prev, *bss_link_head; uint32_t i,numAP,j; bss_link = copy_linked_list(bss_link); // SDK provived linked list will be destroyed automatically bss_link_head = bss_link; // store head for traversing list // get len of linked list numAP=0; while(bss_link != NULL) { numAP++; bss_link = bss_link->next.stqe_next; } os_printf("Number of APs found: %d\r\n", numAP); bss_link = bss_link_head; // reset to head // sort linked list by rssi for(i=0;inext.stqe_next; for(j=0; jrssi > bss_link_curr->rssi) { if (bss_link_prev == NULL) { // swap node position and head position bss_link_curr->next.stqe_next = bss_link_next->next.stqe_next; bss_link_next->next.stqe_next = bss_link_curr; bss_link = bss_link_next; // store new head bss_link_prev = bss_link_next; bss_link_next = bss_link_curr->next.stqe_next; } else { // swap node positions bss_link_prev->next.stqe_next = bss_link_next; bss_link_curr->next.stqe_next = bss_link_next->next.stqe_next; // could be null bss_link_next->next.stqe_next = bss_link_curr; bss_link_prev = bss_link_next; bss_link_next = bss_link_curr->next.stqe_next; } } else { // move along like nothing happened bss_link_prev = bss_link_curr; bss_link_curr = bss_link_next; bss_link_next = bss_link_next->next.stqe_next; } } } bss_link_head = bss_link; // store head // print sorted list of stations while (bss_link != NULL) { os_memset(ssid, 0, 33); os_memset(bssid, 0, 6); if (os_strlen(bss_link->ssid) <= 32) { os_memcpy(ssid, bss_link->ssid, os_strlen(bss_link->ssid)); } else { os_memcpy(ssid, bss_link->ssid, 32); } os_printf("WiFi Scan: (%d,\"%s\",%x, %d)\n", bss_link->authmode, ssid, bssid, bss_link->rssi); bss_link = bss_link->next.stqe_next; } bss_link = bss_link_head; // reset to head head_bss_link = bss_link_head; // ahhh, this is lazy. os_printf("Activating event handler\r\n"); wifi_set_event_handler_cb(wifi_handle_event_cb); os_printf("Analysis scan results\r\n"); wifi_process(); } else // if scan was unsuccessful { sysCfg.module_state.searching = FALSE; os_printf("Scan Failed\r\n"); GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 0xFFFFFFFF); // clear pending interrupts sysCfg.module_state.asleep = TRUE; ETS_GPIO_INTR_ENABLE(); // enable interrupts to permit PB and LSM6DS3 to wake ESP from sleep. } } /****************************************************************************** * FunctionName : start_wifi_process * Description : scans for available AP and attempts to connect to the AP with the highest RSSI. * Parameters : restart - resets wifi_process_count * Returns : *******************************************************************************/ void ICACHE_FLASH_ATTR start_wifi_process(void) { ETS_GPIO_INTR_DISABLE(); // disable I/O interrupts lsm6ds3_motion_enable(); // enable LSM6DS3 Significant Motion Process wifi_process_flags.process_count = 0; wifi_process_flags.wifi_flag = FALSE; // clear wifi_process_flags wifi_process_flags.ip_flag = FALSE; wifi_process_flags.wifi_count = 0; sysCfg.module_state.searching = TRUE; // setup global flags for searching sysCfg.module_state.connected = FALSE; sysCfg.module_state.broker = FALSE; wifi_station_set_auto_connect(FALSE); // disable wifi auto connect wifi_station_ap_number_set(0); // don't store Wi-Fi configurations in SDK flash os_printf("Start Wi-Fi Scan\r\n"); wifi_set_opmode(STATION_MODE); wifi_station_scan(NULL, wifi_station_scan_done); // start Wi-Fi scan. }