Summary
Part 2 of the discussion of using the Infineon (Cypress) PSoC 6 with a CYW4343W and the AnyCloud Connection Manager with Modus Toolbox. The AnyCloud Connection Manager is an RTOS thread that lets you manage a connection to a WiFi network. It knows how to scan for networks, attach, detach etc. and was recently released for use with PSoC6 and 43xxx WiFi combos.
The Story
In the last article I walked you through creating a project using the wireless connection manager (WCM) which is one of the libraries that is part of the Infineon/Cypress AnyCloud SDK. I introduced the wifi-mw-core, wifi-connection-manager and ntshell libraries. In this article I will update the program to include commands to
- connect (to an Access Point)
- disconnect (from an Access Point)
- print (mac address and ip address)
Add the Connect
I want to add a command that will let me issue a command line like
- connect SSID (if it doesn’t have a passphrase)
- connect SSID passphrase
In order to connect to an Access Point you need to call the WCM API, cy_wcm_connect_ap. Here is the function prototype:
cy_rslt_t cy_wcm_connect_ap(const cy_wcm_connect_params_t *connect_params, cy_wcm_ip_address_t *ip_addr)
This function requires that you give it two arguments
- A pointer for a place to store the IP address (that comes from the DHCP server)
- A pointer to a structure of connection parameters. That structure is as follows:
typedef struct { cy_wcm_ap_credentials_t ap_credentials; /**< Access point credentials */ cy_wcm_mac_t BSSID; /**< Specifies the MAC address of Access Point (optional) */ cy_wcm_ip_setting_t *static_ip_settings; /**< Specifies the static IP settings of the device (optional) */ cy_wcm_wifi_band_t band; /**< Specifies the Radio band to be connected (optional) */ } cy_wcm_connect_params_t;
Typically you would setup the “ap_credentials” part of the structure (unless you happen to know the MAC address of the AP you want to connect to). Those credentials include the SSID and password as well as the security (WPA2 PSK etc…)
typedef struct { cy_wcm_ssid_t SSID; /**< SSID of the Wi-Fi network to join, SSID should be a null terminated string. */ cy_wcm_passphrase_t password; /**< Password needed to join the AP, password should be a null terminated string. */ cy_wcm_security_t security; /**< Wi-Fi Security. @see cy_wcm_security_t. */ } cy_wcm_ap_credentials_t;
Security is an enumeration of possible security types.
typedef enum { WHD_SECURITY_OPEN = 0, /**< Open security */ WHD_SECURITY_WEP_PSK = WEP_ENABLED, /**< WEP PSK Security with open authentication */ WHD_SECURITY_WEP_SHARED = (WEP_ENABLED | SHARED_ENABLED), /**< WEP PSK Security with shared authentication */ WHD_SECURITY_WPA_TKIP_PSK = (WPA_SECURITY | TKIP_ENABLED), /**< WPA PSK Security with TKIP */ WHD_SECURITY_WPA_AES_PSK = (WPA_SECURITY | AES_ENABLED), /**< WPA PSK Security with AES */ WHD_SECURITY_WPA_MIXED_PSK = (WPA_SECURITY | AES_ENABLED | TKIP_ENABLED), /**< WPA PSK Security with AES & TKIP */ WHD_SECURITY_WPA2_AES_PSK = (WPA2_SECURITY | AES_ENABLED), /**< WPA2 PSK Security with AES */ WHD_SECURITY_WPA2_TKIP_PSK = (WPA2_SECURITY | TKIP_ENABLED), /**< WPA2 PSK Security with TKIP */ WHD_SECURITY_WPA2_MIXED_PSK = (WPA2_SECURITY | AES_ENABLED | TKIP_ENABLED), /**< WPA2 PSK Security with AES & TKIP */ WHD_SECURITY_WPA2_FBT_PSK = (WPA2_SECURITY | AES_ENABLED | FBT_ENABLED), /**< WPA2 FBT PSK Security with AES & TKIP */ WHD_SECURITY_WPA3_SAE = (WPA3_SECURITY | AES_ENABLED), /**< WPA3 Security with AES */ WHD_SECURITY_WPA3_WPA2_PSK = (WPA3_SECURITY | WPA2_SECURITY | AES_ENABLED), /**< WPA3 WPA2 PSK Security with AES */ WHD_SECURITY_WPA_TKIP_ENT = (ENTERPRISE_ENABLED | WPA_SECURITY | TKIP_ENABLED), /**< WPA Enterprise Security with TKIP */ WHD_SECURITY_WPA_AES_ENT = (ENTERPRISE_ENABLED | WPA_SECURITY | AES_ENABLED), /**< WPA Enterprise Security with AES */ WHD_SECURITY_WPA_MIXED_ENT = (ENTERPRISE_ENABLED | WPA_SECURITY | AES_ENABLED | TKIP_ENABLED), /**< WPA Enterprise Security with AES & TKIP */ WHD_SECURITY_WPA2_TKIP_ENT = (ENTERPRISE_ENABLED | WPA2_SECURITY | TKIP_ENABLED), /**< WPA2 Enterprise Security with TKIP */ WHD_SECURITY_WPA2_AES_ENT = (ENTERPRISE_ENABLED | WPA2_SECURITY | AES_ENABLED), /**< WPA2 Enterprise Security with AES */ WHD_SECURITY_WPA2_MIXED_ENT = (ENTERPRISE_ENABLED | WPA2_SECURITY | AES_ENABLED | TKIP_ENABLED), /**< WPA2 Enterprise Security with AES & TKIP */ WHD_SECURITY_WPA2_FBT_ENT = (ENTERPRISE_ENABLED | WPA2_SECURITY | AES_ENABLED | FBT_ENABLED), /**< WPA2 Enterprise Security with AES & FBT */ WHD_SECURITY_IBSS_OPEN = (IBSS_ENABLED), /**< Open security on IBSS ad-hoc network */ WHD_SECURITY_WPS_SECURE = AES_ENABLED, /**< WPS with AES security */ WHD_SECURITY_UNKNOWN = -1, /**< May be returned by scan function if security is unknown. Do not pass this to the join function! */ WHD_SECURITY_FORCE_32_BIT = 0x7fffffff /**< Exists only to force whd_security_t type to 32 bits */ } whd_security_t;
Having to know the security of the AP is a total pain in the neck. Where do you find the security from? It turns out that when an AP beacons, the security of that SSID is one of the things that is broadcast. What this means is that my program will need to
- When the connect command is called it should scan for the SSID that is part of the connect command & wait
- When the scan finds that SSID it will put the security type into the correct datastructure
- Then call the connect.
The way that I will do this is to
- Build a filter (that looks only for the user specified SSID)
- Provides a pointer for a place to store the security type.
I use the cy_wcm_scan function to do this. Here is the function prototype:
cy_rslt_t cy_wcm_start_scan(cy_wcm_scan_result_callback_t callback, void *user_data, cy_wcm_scan_filter_t *scan_filter)
The scan filter is just a structure that specified
- A mode (which type of filter you want)
- The specific thing that you are looking for.
typedef struct { cy_wcm_scan_filter_type_t mode; /**< Scan filter mode */ union { cy_wcm_ssid_t SSID; /**< Service Set Identification */ cy_wcm_mac_t BSSID; /**< MAC address of Access Point */ cy_wcm_wifi_band_t band; /**< Radio band */ cy_wcm_scan_rssi_range_t rssi_range; /**< RSSI range */ } param; /**< Scan filter mode specific paramter */ } cy_wcm_scan_filter_t;
The mode is simply an enumeration of the types of filters:
typedef enum { CY_WCM_SCAN_FILTER_TYPE_SSID = 0, /**< Denotes SSID based scan filtering */ CY_WCM_SCAN_FILTER_TYPE_MAC, /**< Denotes MAC based scan filtering */ CY_WCM_SCAN_FILTER_TYPE_BAND, /**< Denotes BAND based scan filtering */ CY_WCM_SCAN_FILTER_TYPE_RSSI, /**< Denotes RSSI based scan filtering */ }cy_wcm_scan_filter_type_t;
What I want to do is start the scan and then wait for a semaphore. To do this I will create a semaphore variable at the top of the netTask.c
SemaphoreHandle_t scanApSempahore = NULL;
Inside of the switch I will
- Create a connection parameters structure (line 226-227)
- setup the scan filter (line 231-232)
- create the semaphore (line 235)
- run the scan (line 236)
- wait for the semaphore to be set or timeout (line 239) notice that I hardcoded it to 10 seconds
cy_wcm_connect_params_t connect_params; memset(&connect_params, 0, sizeof(cy_wcm_connect_params_t)); // setup scan filter - In order to connect to an SSID you need to know the security type // To find the security I scan for JUST that SSID which will tell me the security type scanFilter.mode = CY_WCM_SCAN_FILTER_TYPE_SSID; strcpy((char *)scanFilter.param.SSID,(char *)msg.val0); // The scan callback will either 1) unlock the semaphore or 2) timeout (meaning it didnt find it) scanApSempahore = xSemaphoreCreateBinary(); cy_wcm_start_scan(findApCallback,&connect_params.ap_credentials.security,&scanFilter); // The semaphore will return pdFALSE if it TIMES out or pdTrue IF it got unlocked by the scan if(xSemaphoreTake( scanApSempahore, pdMS_TO_TICKS(10000)) == pdTRUE)
In the scan callback I will check to see if I have real data (in other words the scan is not complete). In the setup above I made the user data be a pointer to the place to store the security. On line 54 I will store the security type that came back from the scan in the place pointed to by the user data pointer. Then I will stop the scan and give the semaphore.
// This callback is used to find a specific SSID and then store the security type into the user data // When I want to connect it will scan with a "filter" and the user data will be a pointer to the // place to store the security void findApCallback( cy_wcm_scan_result_t *result_ptr, void *user_data, cy_wcm_scan_status_t status ) { if(status == CY_WCM_SCAN_INCOMPLETE) { whd_security_t *mySecurity = (whd_security_t *)user_data; *mySecurity = result_ptr->security; cy_wcm_stop_scan(); xSemaphoreGive(scanApSempahore); } }
Now back in the switch statement you can actually connect because you know the security type (line 244) The else clause on line 253 handles the case where the timeout of the semaphore occurred, meaning that the scan didn’t find the AP.
// The semaphore will return pdFALSE if it TIMES out or pdTrue IF it got unlocked by the scan if(xSemaphoreTake( scanApSempahore, pdMS_TO_TICKS(10000)) == pdTRUE) { strcpy((char *)connect_params.ap_credentials.SSID,(char *)msg.val0); strcpy((char *)connect_params.ap_credentials.password,(char *)msg.val1); result = cy_wcm_connect_ap(&connect_params,&ip_addr); if(result == CY_RSLT_SUCCESS) printf("Connect Succeeded SSID=%s\n",(char *)msg.val0); else { printf("Connect to %s failed\n",(char *)msg.val0); } } else { printf("Scan semaphore failed - couldnt find AP\n"); } free((void *)msg.val0); // Free the SSID and PW that was passed by the caller free((void *)msg.val1); } break;
With all of the connection work done, you can add the scan command to “usrcmd.c”. It just looks at the number of arguments (either 2 or 3), then sets up the message to send to the network task, the queues the message.
static int usrcmd_connect(int argc, char **argv) { networkQueueMsg_t msg; if(argc == 2) { msg.val0 = (uint32_t)malloc(strlen(argv[1])+1); msg.val1 = (uint32_t)malloc(sizeof("")); strcpy((char *)msg.val0,argv[1]); strcpy((char *)msg.val1,""); msg.cmd = net_connect; xQueueSend(networkQueue,(const void *)&msg,portMAX_DELAY); } if(argc == 3) { msg.val0 = (uint32_t)malloc(strlen(argv[1])+1); msg.val1 = (uint32_t)malloc(strlen(argv[2])+1); strcpy((char *)msg.val0,argv[1]); strcpy((char *)msg.val1,argv[2]); msg.cmd = net_connect; xQueueSend(networkQueue,(const void *)&msg,portMAX_DELAY); } return 0; }
Add the Disconnect Command
The disconnect command is trivial. Just call the disconnect api.
case net_disconnect: cy_wcm_disconnect_ap(); break;
Which you also need to add to the usercmd.c
static int usrcmd_disconnect(int argc, char **argv) { networkQueueMsg_t msg; msg.cmd = net_disconnect; xQueueSend(networkQueue,(const void *)&msg,portMAX_DELAY); return 0; }
Add the Print Command
The print command will have two optional parameters, IP (to print the current IP address) and MAC (to print our MAC address). The first command is print ip.
case net_printip: result = cy_wcm_get_ip_addr(CY_WCM_INTERFACE_TYPE_STA,&ip_addr,1); if(result == CY_RSLT_SUCCESS) { printf("IP Address="); printIp(&ip_addr); printf("\n"); } else if(result == CY_RSLT_WCM_NETWORK_DOWN) printf("Network disconnected\n"); else printf("IP Address call return unknown %d\n",(int)result); break;
The MAC address command is also simple:
case net_printmac: result = cy_wcm_get_mac_addr(CY_WCM_INTERFACE_TYPE_STA,&mac_addr,1); if(result == CY_RSLT_SUCCESS) { printf("MAC Address ="); printMac(mac_addr); printf("\n"); } else printf("MAC Address = Unknown\n"); break; }
And you need to add the print command to usrcmd.c
static int usrcmd_print(int argc, char **argv) { networkQueueMsg_t msg; if(argc == 2 && strcmp(argv[1],"ip")==0) { msg.cmd = net_printip; xQueueSend(networkQueue,(const void *)&msg,portMAX_DELAY); } if(argc == 2 && strcmp(argv[1],"mac")==0) { msg.cmd = net_printmac; xQueueSend(networkQueue,(const void *)&msg,portMAX_DELAY); } return 0; }
All of this code is available on github at
- git@github.com:iotexpert/wcm_example
- https://github.com/iotexpert/wcm_example