Station API

Station API is used to work with ESP acting in station mode. It allows to join other access point, scan for available access points or simply disconnect from it.

An example below is showing how all examples (coming with this library) scan for access point and then try to connect to AP from list of preferred one.

Station manager used with all examples
  1/*
  2 * Station manager to connect station to access point.
  3 *
  4 * It is consider as a utility module, simple set of helper functions
  5 * to quickly connect to access point.
  6 * 
  7 * It utilizes 2 different modes, sequential or asynchronous.
  8 * 
  9 * Sequential:
 10 * ==========
 11 * Call station_manager_connect_to_preferred_access_point function to connect to access point
 12 * in blocking mode until being ready to move forward.
 13 * 
 14 * Asynchronous:
 15 * ============
 16 * Call station_manager_connect_to_access_point_async_init to initialize
 17 * asynchronous connect mode and activity will react upon received LwESP events to application.
 18 * 
 19 * Define list of access points:
 20 * ============================
 21 * Have a look at "ap_list_preferred" variable and define
 22 * list of preferred access point's SSID and password.
 23 * Ordered by "most preferred" at the lower array index.
 24 */
 25#include "station_manager.h"
 26#include "utils.h"
 27#include "lwesp/lwesp.h"
 28
 29/**
 30 * \brief           Private access-point and station management system
 31 * 
 32 * This is used for asynchronous connection to access point
 33 */
 34typedef struct {
 35    size_t index_preferred_list;                /*!< Current index position of preferred array */
 36    size_t index_scanned_list;                  /*!< Current index position in array of scanned APs */
 37
 38    uint8_t command_is_running;                 /*!< Indicating if command is currently in progress */
 39} prv_ap_data_t;
 40
 41/* Arguments for callback function */
 42#define ARG_SCAN                (void*)1
 43#define ARG_CONNECT             (void*)2
 44
 45/* Function declaration */
 46static void prv_cmd_event_fn(lwespr_t status, void* arg);
 47static void prv_try_next_access_point(void);
 48
 49/*
 50 * List of preferred access points for ESP device
 51 * SSID and password
 52 *
 53 * ESP will try to scan for access points
 54 * and then compare them with the one on the list below
 55 */
 56static const ap_entry_t ap_list_preferred[] = {
 57    //{ .ssid = "SSID name", .pass = "SSID password" },
 58    { .ssid = "TilenM_ST", .pass = "its private" },
 59    { .ssid = "Kaja", .pass = "ginkaja2021" },
 60    { .ssid = "Majerle WIFI", .pass = "majerle_internet_private" },
 61    { .ssid = "Majerle AMIS", .pass = "majerle_internet_private" },
 62};
 63static lwesp_ap_t ap_list_scanned[100];         /* Scanned access points information */
 64static size_t ap_list_scanned_len = 0;          /* Number of scanned access points */
 65static prv_ap_data_t ap_async_data;             /* Asynchronous data structure */
 66
 67/* Command to execute to start scanning access points */
 68#define prv_scan_ap_command_ex(blocking)        lwesp_sta_list_ap(NULL, ap_list_scanned, LWESP_ARRAYSIZE(ap_list_scanned), &ap_list_scanned_len, NULL, NULL, (blocking))
 69#define prv_scan_ap_command()                   do {\
 70    if (!ap_async_data.command_is_running) {    \
 71        ap_async_data.command_is_running = lwesp_sta_list_ap(NULL, ap_list_scanned, LWESP_ARRAYSIZE(ap_list_scanned), &ap_list_scanned_len, prv_cmd_event_fn, ARG_SCAN, 0) == lwespOK;   \
 72    }       \
 73} while (0)
 74
 75/**
 76 * \brief           Every internal command execution callback
 77 * \param[in]       status: Execution status result
 78 * \param[in]       arg: Custom user argument
 79 */
 80static void
 81prv_cmd_event_fn(lwespr_t status, void* arg) {
 82    LWESP_UNUSED(status);
 83    /*
 84     * Command has now successfully finish
 85     * and callbacks have been properly processed
 86     */
 87    ap_async_data.command_is_running = 0;
 88
 89    if (arg == ARG_SCAN) {
 90        /* Immediately try to connect to access point after successful scan */
 91        prv_try_next_access_point();
 92    }
 93}
 94
 95/**
 96 * \brief           Try to connect to next access point on a list 
 97 */
 98static void
 99prv_try_next_access_point(void) {
100    uint8_t tried = 0;
101
102    /* No action to be done if command is currently in progress or already connected to network */
103    if (ap_async_data.command_is_running
104        || lwesp_sta_has_ip()) {
105        return;
106    }
107
108    /*
109     * Process complete list and try to find suitable match
110     *
111     * Use global variable for indexes to be able to call function multiple times
112     * and continue where it finished previously
113     */
114
115    /* List all preferred access points */
116    for (; ap_async_data.index_preferred_list < LWESP_ARRAYSIZE(ap_list_preferred);
117            ap_async_data.index_preferred_list++, ap_async_data.index_scanned_list = 0) {
118
119        /* List all scanned access points */
120        for (; ap_async_data.index_scanned_list < ap_list_scanned_len; ap_async_data.index_scanned_list++) {
121
122            /* Find a match if available */
123            if (strncmp(ap_list_scanned[ap_async_data.index_scanned_list].ssid,
124                        ap_list_preferred[ap_async_data.index_preferred_list].ssid,
125                        strlen(ap_list_preferred[ap_async_data.index_preferred_list].ssid)) == 0) {
126
127                /* Try to connect to the network */
128                if (!ap_async_data.command_is_running
129                    && lwesp_sta_join(ap_list_preferred[ap_async_data.index_preferred_list].ssid,
130                                    ap_list_preferred[ap_async_data.index_preferred_list].pass,
131                                    NULL, prv_cmd_event_fn, ARG_CONNECT, 0) == lwespOK) {
132                    ap_async_data.command_is_running = 1;
133
134                    /* Go to next index for sub-for loop and exit */
135                    ap_async_data.index_scanned_list++;
136                    tried = 1;
137                    goto stp;
138                } else {
139                    /* We have a problem, needs to resume action in next run */
140                }
141            }
142        }
143    }
144
145    /* Restart scan operation if there was no try to connect and station has no IP */
146    if (!tried && !lwesp_sta_has_ip()) {
147        prv_scan_ap_command();
148    }
149stp:
150    return;
151}
152
153/**
154 * \brief           Private event function for asynchronous scanning
155 * \param[in]       evt: Event information
156 * \return          \ref lwespOK on success, member of \ref lwespr_t otherwise
157 */
158static lwespr_t
159prv_evt_fn(lwesp_evt_t* evt) {
160    switch (evt->type) {
161        case LWESP_EVT_KEEP_ALIVE:
162        case LWESP_EVT_WIFI_DISCONNECTED: {
163            /* Try to connect to next access point */
164            prv_try_next_access_point();
165            break;
166        }
167        case LWESP_EVT_STA_LIST_AP: {
168            /*
169             * After scanning gets completed
170             * manually reset all indexes for comparison purposes
171             */
172            ap_async_data.index_scanned_list = 0;
173            ap_async_data.index_preferred_list = 0;
174
175            /* Actual connection try is done in function callback */
176            break;
177        }
178        default: break;
179    }
180    return lwespOK;
181}
182
183/**
184 * \brief           Initialize asynchronous mode to connect to preferred access point
185 *
186 * Asynchronous mode relies on system events received by the application,
187 * to determine current device status if station is being, or not, connected to access point.
188 *
189 * When used, async acts only upon station connection change through callbacks,
190 * therefore it does not require additional system thread or user code,
191 * to be able to properly handle preferred access points.
192 * This certainly decreases memory consumption of the complete system.
193 *
194 * \ref LWESP_CFG_KEEP_ALIVE feature must be enable to properly handle all events
195 * \return          \ref lwespOK on success, member of \ref lwespr_t otherwise
196 */
197lwespr_t
198station_manager_connect_to_access_point_async_init(void) {
199    /* Register system event function */
200    lwesp_evt_register(prv_evt_fn);
201
202    /*
203     * Start scanning process in non-blocking mode
204     *
205     * This is the only command being executed from non-callback mode,
206     * therefore it must be protected against other threads trying to access the same core
207     */
208    lwesp_core_lock();
209    prv_scan_ap_command();
210    lwesp_core_unlock();
211
212    /* Return all good, things will progress (from now-on) asynchronously */
213    return lwespOK;
214}
215
216/**
217 * \brief           Connect to preferred access point in blocking mode
218 * 
219 * This functionality can only be used if non-blocking approach is not used
220 * 
221 * \note            List of access points should be set by user in \ref ap_list structure
222 * \param[in]       unlimited: When set to 1, function will block until SSID is found and connected
223 * \return          \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
224 */
225lwespr_t
226station_manager_connect_to_preferred_access_point(uint8_t unlimited) {
227    lwespr_t eres;
228    uint8_t tried;
229
230    /*
231     * Scan for network access points
232     * In case we have access point,
233     * try to connect to known AP
234     */
235    do {
236        if (lwesp_sta_has_ip()) {
237            return lwespOK;
238        }
239
240        /* Scan for access points visible to ESP device */
241        printf("Scanning access points...\r\n");
242        if ((eres = prv_scan_ap_command_ex(1)) == lwespOK) {
243            tried = 0;
244
245            /* Print all access points found by ESP */
246            for (size_t i = 0; i < ap_list_scanned_len; i++) {
247                printf("AP found: %s, CH: %d, RSSI: %d\r\n", ap_list_scanned[i].ssid, ap_list_scanned[i].ch, ap_list_scanned[i].rssi);
248            }
249
250            /* Process array of preferred access points with array of found points */
251            for (size_t j = 0; j < LWESP_ARRAYSIZE(ap_list_preferred); j++) {
252
253                /* Go through all scanned list */
254                for (size_t i = 0; i < ap_list_scanned_len; i++) {
255
256                    /* Try to find a match between preferred and scanned */
257                    if (strncmp(ap_list_scanned[i].ssid, ap_list_preferred[j].ssid, strlen(ap_list_scanned[i].ssid)) == 0) {
258                        tried = 1;
259                        printf("Connecting to \"%s\" network...\r\n", ap_list_preferred[j].ssid);
260
261                        /* Try to join to access point */
262                        if ((eres = lwesp_sta_join(ap_list_preferred[j].ssid, ap_list_preferred[j].pass, NULL, NULL, NULL, 1)) == lwespOK) {
263                            lwesp_ip_t ip;
264                            uint8_t is_dhcp;
265
266                            printf("Connected to %s network!\r\n", ap_list_preferred[j].ssid);
267
268                            lwesp_sta_copy_ip(&ip, NULL, NULL, &is_dhcp);
269                            utils_print_ip("Station IP address: ", &ip, "\r\n");
270                            printf("; Is DHCP: %d\r\n", (int)is_dhcp);
271                            return lwespOK;
272                        } else {
273                            printf("Connection error: %d\r\n", (int)eres);
274                        }
275                    }
276                }
277            }
278            if (!tried) {
279                printf("No access points available with preferred SSID!\r\nPlease check station_manager.c file and edit preferred SSID access points!\r\n");
280            }
281        } else if (eres == lwespERRNODEVICE) {
282            printf("Device is not present!\r\n");
283            break;
284        } else {
285            printf("Error on WIFI scan procedure!\r\n");
286        }
287        if (!unlimited) {
288            break;
289        }
290    } while (1);
291    return lwespERR;
292}
group LWESP_STA

Station API.

Functions

lwespr_t lwesp_sta_join(const char *name, const char *pass, const lwesp_mac_t *mac, const lwesp_api_cmd_evt_fn evt_fn, void *const evt_arg, const uint32_t blocking)

Join as station to access point.

Configuration changes will be saved in the NVS area of ESP device.

Parameters
  • name[in] SSID of access point to connect to

  • pass[in] Password of access point. Use NULL if AP does not have password

  • mac[in] Pointer to MAC address of AP. If multiple APs with same name exist, MAC may help to select proper one. Set to NULL if not needed

  • evt_fn[in] Callback function called when command has finished. Set to NULL when not used

  • evt_arg[in] Custom argument for event callback function

  • blocking[in] Status whether command should be blocking or not

Returns

lwespOK on success, member of lwespr_t enumeration otherwise

lwespr_t lwesp_sta_quit(const lwesp_api_cmd_evt_fn evt_fn, void *const evt_arg, const uint32_t blocking)

Quit (disconnect) from access point.

Parameters
  • evt_fn[in] Callback function called when command has finished. Set to NULL when not used

  • evt_arg[in] Custom argument for event callback function

  • blocking[in] Status whether command should be blocking or not

Returns

lwespOK on success, member of lwespr_t enumeration otherwise

lwespr_t lwesp_sta_autojoin(uint8_t en, const lwesp_api_cmd_evt_fn evt_fn, void *const evt_arg, const uint32_t blocking)

Configure auto join to access point on startup.

Note

For auto join feature, you need to do a join to access point with default mode. Check lwesp_sta_join for more information

Parameters
  • en[in] Set to 1 to enable or 0 to disable

  • evt_fn[in] Callback function called when command has finished. Set to NULL when not used

  • evt_arg[in] Custom argument for event callback function

  • blocking[in] Status whether command should be blocking or not

Returns

lwespOK on success, member of lwespr_t enumeration otherwise

lwespr_t lwesp_sta_reconnect_set_config(uint16_t interval, uint16_t rep_cnt, const lwesp_api_cmd_evt_fn evt_fn, void *const evt_arg, const uint32_t blocking)

Set reconnect interval and maximum tries when connection drops.

Parameters
  • interval[in] Interval in units of seconds. Valid numbers are 1-7200 or 0 to disable reconnect feature

  • rep_cnt[in] Repeat counter. Number of maximum tries for reconnect. Valid entries are 1-1000 or 0 to always try. This parameter is only valid if interval is not 0

  • evt_fn[in] Callback function called when command has finished. Set to NULL when not used

  • evt_arg[in] Custom argument for event callback function

  • blocking[in] Status whether command should be blocking or not

Returns

lwespOK on success, member of lwespr_t enumeration otherwise

lwespr_t lwesp_sta_getip(lwesp_ip_t *ip, lwesp_ip_t *gw, lwesp_ip_t *nm, const lwesp_api_cmd_evt_fn evt_fn, void *const evt_arg, const uint32_t blocking)

Get station IP address.

Parameters
  • ip[out] Pointer to variable to save IP address

  • gw[out] Pointer to output variable to save gateway address

  • nm[out] Pointer to output variable to save netmask address

  • evt_fn[in] Callback function called when command has finished. Set to NULL when not used

  • evt_arg[in] Custom argument for event callback function

  • blocking[in] Status whether command should be blocking or not

Returns

lwespOK on success, member of lwespr_t enumeration otherwise

lwespr_t lwesp_sta_setip(const lwesp_ip_t *ip, const lwesp_ip_t *gw, const lwesp_ip_t *nm, const lwesp_api_cmd_evt_fn evt_fn, void *const evt_arg, const uint32_t blocking)

Set station IP address.

Application may manually set IP address. When this happens, stack will check for DHCP settings and will read actual IP address from device. Once procedure is finished, LWESP_EVT_WIFI_IP_ACQUIRED event will be sent to application where user may read the actual new IP and DHCP settings.

Configuration changes will be saved in the NVS area of ESP device.

Note

DHCP is automatically disabled when using static IP address

Parameters
  • ip[in] Pointer to IP address

  • gw[in] Pointer to gateway address. Set to NULL to use default gateway

  • nm[in] Pointer to netmask address. Set to NULL to use default netmask

  • evt_fn[in] Callback function called when command has finished. Set to NULL when not used

  • evt_arg[in] Custom argument for event callback function

  • blocking[in] Status whether command should be blocking or not

Returns

lwespOK on success, member of lwespr_t enumeration otherwise

lwespr_t lwesp_sta_getmac(lwesp_mac_t *mac, const lwesp_api_cmd_evt_fn evt_fn, void *const evt_arg, const uint32_t blocking)

Get station MAC address.

Parameters
  • mac[out] Pointer to output variable to save MAC address

  • evt_fn[in] Callback function called when command has finished. Set to NULL when not used

  • evt_arg[in] Custom argument for event callback function

  • blocking[in] Status whether command should be blocking or not

Returns

lwespOK on success, member of lwespr_t enumeration otherwise

lwespr_t lwesp_sta_setmac(const lwesp_mac_t *mac, const lwesp_api_cmd_evt_fn evt_fn, void *const evt_arg, const uint32_t blocking)

Set station MAC address.

Configuration changes will be saved in the NVS area of ESP device.

Parameters
  • mac[in] Pointer to variable with MAC address

  • evt_fn[in] Callback function called when command has finished. Set to NULL when not used

  • evt_arg[in] Custom argument for event callback function

  • blocking[in] Status whether command should be blocking or not

Returns

lwespOK on success, member of lwespr_t enumeration otherwise

uint8_t lwesp_sta_has_ip(void)

Check if ESP got IP from access point.

Returns

1 on success, 0 otherwise

uint8_t lwesp_sta_is_joined(void)

Check if station is connected to WiFi network.

Returns

1 on success, 0 otherwise

lwespr_t lwesp_sta_copy_ip(lwesp_ip_t *ip, lwesp_ip_t *gw, lwesp_ip_t *nm, uint8_t *is_dhcp)

Copy IP address from internal value to user variable.

Note

Use lwesp_sta_getip to refresh actual IP value from device

Parameters
  • ip[out] Pointer to output IP variable. Set to NULL if not interested in IP address

  • gw[out] Pointer to output gateway variable. Set to NULL if not interested in gateway address

  • nm[out] Pointer to output netmask variable. Set to NULL if not interested in netmask address

  • is_dhcp[out] Pointer to output DHCP status variable. Set to NULL if not interested

Returns

lwespOK on success, member of lwespr_t enumeration otherwise

lwespr_t lwesp_sta_list_ap(const char *ssid, lwesp_ap_t *aps, size_t apsl, size_t *apf, const lwesp_api_cmd_evt_fn evt_fn, void *const evt_arg, const uint32_t blocking)

List for available access points ESP can connect to.

Parameters
  • ssid[in] Optional SSID name to search for. Set to NULL to disable filter

  • aps[in] Pointer to array of available access point parameters

  • apsl[in] Length of aps array

  • apf[out] Pointer to output variable to save number of access points found

  • evt_fn[in] Callback function called when command has finished. Set to NULL when not used

  • evt_arg[in] Custom argument for event callback function

  • blocking[in] Status whether command should be blocking or not

Returns

lwespOK on success, member of lwespr_t enumeration otherwise

lwespr_t lwesp_sta_get_ap_info(lwesp_sta_info_ap_t *info, const lwesp_api_cmd_evt_fn evt_fn, void *const evt_arg, const uint32_t blocking)

Get current access point information (name, mac, channel, rssi)

Note

Access point station is currently connected to

Parameters
  • info[in] Pointer to connected access point information

  • evt_fn[in] Callback function called when command has finished. Set to NULL when not used

  • evt_arg[in] Custom argument for event callback function

  • blocking[in] Status whether command should be blocking or not

Returns

lwespOK on success, member of lwespr_t enumeration otherwise

uint8_t lwesp_sta_is_ap_802_11b(lwesp_ap_t *ap)

Check if access point is 802.11b compatible.

Parameters

ap[in] Access point detailes acquired by lwesp_sta_list_ap

Returns

1 on success, 0 otherwise

uint8_t lwesp_sta_is_ap_802_11g(lwesp_ap_t *ap)

Check if access point is 802.11g compatible.

Parameters

ap[in] Access point detailes acquired by lwesp_sta_list_ap

Returns

1 on success, 0 otherwise

uint8_t lwesp_sta_is_ap_802_11n(lwesp_ap_t *ap)

Check if access point is 802.11n compatible.

Parameters

ap[in] Access point detailes acquired by lwesp_sta_list_ap

Returns

1 on success, 0 otherwise

uint8_t lwesp_sta_has_ipv6_local(void)

Check if station has local IPV6 IP Local IP is used between station and router.

Note

Defined as macro with 0 constant if LWESP_CFG_IPV6 is disabled

Returns

1 if local IPv6 is available, 0 otherwise

uint8_t lwesp_sta_has_ipv6_global(void)

Check if station has global IPV6 IP Global IP is used router and outside network.

Note

Defined as macro with 0 constant if LWESP_CFG_IPV6 is disabled

Returns

1 if global IPv6 is available, 0 otherwise

struct lwesp_sta_t
#include <lwesp_types.h>

Station data structure.

Public Members

lwesp_ip_t ip

IP address of connected station

lwesp_mac_t mac

MAC address of connected station