1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-16 00:00:07 +01:00
libwebsockets/plugins/protocol_esp32_lws_scan.c
2017-03-10 14:31:43 +08:00

333 lines
7.6 KiB
C

/*
* Example ESP32 app code using Libwebsockets
*
* Copyright (C) 2017 Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* The person who associated a work with this deed has dedicated
* the work to the public domain by waiving all of his or her rights
* to the work worldwide under copyright law, including all related
* and neighboring rights, to the extent allowed by law. You can copy,
* modify, distribute and perform the work, even for commercial purposes,
* all without asking permission.
*
* The test apps are intended to be adapted for use in your code, which
* may be proprietary. So unlike the library itself, they are licensed
* Public Domain.
*
*/
#include <string.h>
#include <nvs.h>
typedef enum {
SCAN_STATE_NONE,
SCAN_STATE_INITIAL,
SCAN_STATE_LIST,
SCAN_STATE_FINAL
} scan_state;
struct store_json {
const char *j;
const char *nvs;
};
struct per_session_data__esplws_scan {
struct per_session_data__esplws_scan *next;
scan_state scan_state;
char ap_record;
unsigned char subsequent:1;
unsigned char changed_partway:1;
};
struct per_vhost_data__esplws_scan {
wifi_ap_record_t ap_records[20];
TimerHandle_t timer;
struct per_session_data__esplws_scan *live_pss_list;
struct lws_context *context;
struct lws_vhost *vhost;
const struct lws_protocols *protocol;
uint16_t count_ap_records;
char count_live_pss;
unsigned char scan_ongoing:1;
unsigned char completed_any_scan:1;
unsigned char reboot:1;
};
static const struct store_json store_json[] = {
{ "ssid\":\"", "ssid" },
{ ",\"pw\":\"", "password" },
{ ",\"serial\":\"", "serial" },
{ ",\"region\":\"", "region" },
};
static wifi_scan_config_t scan_config = {
.ssid = 0,
.bssid = 0,
.channel = 0,
.show_hidden = true
};
extern void (*lws_cb_scan_done)(void *);
extern void *lws_cb_scan_done_arg;
static void
scan_finished(void *v);
static int
esplws_simple_arg(char *dest, int len, const char *in, const char *match)
{
const char *p = strstr(in, match);
int n = 0;
if (!p) {
lwsl_err("No match %s\n", match);
return 1;
}
p += strlen(match);
while (*p && *p != '\"' && n < len - 1)
dest[n++] = *p++;
dest[n] = '\0';
return 0;
}
static void
scan_start(struct per_vhost_data__esplws_scan *vhd)
{
int n;
if (vhd->reboot)
esp_restart();
if (vhd->scan_ongoing)
return;
vhd->scan_ongoing = 1;
lws_cb_scan_done = scan_finished;
lws_cb_scan_done_arg = vhd;
n = esp_wifi_scan_start(&scan_config, false);
if (n != ESP_OK)
lwsl_err("scan start failed %d\n", n);
}
static void timer_cb(TimerHandle_t t)
{
struct per_vhost_data__esplws_scan *vhd = pvTimerGetTimerID(t);
scan_start(vhd);
}
static void
scan_finished(void *v)
{
struct per_vhost_data__esplws_scan *vhd = v;
struct per_session_data__esplws_scan *p = vhd->live_pss_list;
vhd->scan_ongoing = 0;
vhd->count_ap_records = ARRAY_SIZE(vhd->ap_records);
if (esp_wifi_scan_get_ap_records(&vhd->count_ap_records, vhd->ap_records) != ESP_OK) {
lwsl_err("%s: failed\n", __func__);
return;
}
while (p) {
if (p->scan_state != SCAN_STATE_INITIAL && p->scan_state != SCAN_STATE_NONE)
p->changed_partway = 1;
else
p->scan_state = SCAN_STATE_INITIAL;
p = p->next;
}
lws_callback_on_writable_all_protocol(vhd->context, vhd->protocol);
}
static int
callback_esplws_scan(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
struct per_session_data__esplws_scan *pss =
(struct per_session_data__esplws_scan *)user;
struct per_vhost_data__esplws_scan *vhd =
(struct per_vhost_data__esplws_scan *)
lws_protocol_vh_priv_get(lws_get_vhost(wsi),
lws_get_protocol(wsi));
char buf[LWS_PRE + 384], /*ip[24],*/ *start = buf + LWS_PRE - 1, *p = start,
*end = buf + sizeof(buf) - 1;
wifi_ap_record_t *r;
int n, m;
switch (reason) {
case LWS_CALLBACK_PROTOCOL_INIT:
vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
lws_get_protocol(wsi),
sizeof(struct per_vhost_data__esplws_scan));
vhd->context = lws_get_context(wsi);
vhd->protocol = lws_get_protocol(wsi);
vhd->vhost = lws_get_vhost(wsi);
vhd->timer = xTimerCreate("x", pdMS_TO_TICKS(10000), 1, vhd,
(TimerCallbackFunction_t)timer_cb);
xTimerStart(vhd->timer, 0);
vhd->scan_ongoing = 0;
scan_start(vhd);
break;
case LWS_CALLBACK_PROTOCOL_DESTROY:
if (!vhd)
break;
xTimerStop(vhd->timer, 0);
xTimerDelete(vhd->timer, 0);
break;
case LWS_CALLBACK_ESTABLISHED:
vhd->count_live_pss++;
pss->next = vhd->live_pss_list;
vhd->live_pss_list = pss;
/* if we have scan results, update them. Otherwise wait */
if (vhd->count_ap_records) {
pss->scan_state = SCAN_STATE_INITIAL;
lws_callback_on_writable(wsi);
}
break;
case LWS_CALLBACK_SERVER_WRITEABLE:
switch (pss->scan_state) {
case SCAN_STATE_INITIAL:
n = LWS_WRITE_TEXT | LWS_WRITE_NO_FIN;;
p += snprintf(p, end - p,
"{ \"model\":\"%s\","
" \"serial\":\"%s\","
" \"host\":\"%s-%s\","
" \"region\":\"%d\","
" \"aps\":[",
lws_esp32_model,
lws_esp32_serial,
lws_esp32_model, lws_esp32_serial,
lws_esp32_region);
pss->scan_state = SCAN_STATE_LIST;
pss->ap_record = 0;
pss->subsequent = 0;
break;
case SCAN_STATE_LIST:
n = LWS_WRITE_CONTINUATION | LWS_WRITE_NO_FIN;
if (pss->ap_record >= vhd->count_ap_records)
goto scan_state_final;
if (pss->subsequent)
*p++ = ',';
pss->subsequent = 1;
r = &vhd->ap_records[(int)pss->ap_record++];
p += snprintf(p, end - p,
"{\"ssid\":\"%s\","
"\"bssid\":\"%02X:%02X:%02X:%02X:%02X:%02X\","
"\"rssi\":\"%d\","
"\"chan\":\"%d\","
"\"auth\":\"%d\"}",
r->ssid,
r->bssid[0], r->bssid[1], r->bssid[2],
r->bssid[3], r->bssid[4], r->bssid[5],
r->rssi, r->primary, r->authmode);
if (pss->ap_record >= vhd->count_ap_records)
pss->scan_state = SCAN_STATE_FINAL;
break;
case SCAN_STATE_FINAL:
scan_state_final:
n = LWS_WRITE_CONTINUATION;
p += sprintf(p, "]}");
if (pss->changed_partway) {
pss->subsequent = 0;
pss->scan_state = SCAN_STATE_INITIAL;
} else
pss->scan_state = SCAN_STATE_NONE;
break;
default:
return 0;
}
m = lws_write(wsi, (unsigned char *)start, p - start, n);
if (m < 0) {
lwsl_err("ERROR %d writing to di socket\n", m);
return -1;
}
if (pss->scan_state != SCAN_STATE_NONE)
lws_callback_on_writable(wsi);
break;
case LWS_CALLBACK_RECEIVE:
{
nvs_handle nvh;
char p[64];
int n;
if (strstr((const char *)in, "identify")) {
lws_esp32_identify_physical_device();
break;
}
if (nvs_open("lws-station", NVS_READWRITE, &nvh) != ESP_OK) {
lwsl_err("Unable to open nvs\n");
break;
}
for (n = 0; n < ARRAY_SIZE(store_json); n++) {
if (esplws_simple_arg(p, sizeof(p), in, store_json[n].j))
goto bail_nvs;
if (nvs_set_str(nvh, store_json[n].nvs, p) != ESP_OK) {
lwsl_err("Unable to store %s in nvm\n", store_json[n].nvs);
goto bail_nvs;
}
}
nvs_commit(nvh);
nvs_close(nvh);
vhd->reboot = 1;
break;
bail_nvs:
nvs_close(nvh);
return 1;
}
case LWS_CALLBACK_CLOSED:
{
struct per_session_data__esplws_scan **p = &vhd->live_pss_list;
while (*p) {
if ((*p) == pss) {
*p = pss->next;
continue;
}
p = &((*p)->next);
}
vhd->count_live_pss--;
}
break;
default:
break;
}
return 0;
}
#define LWS_PLUGIN_PROTOCOL_ESPLWS_SCAN \
{ \
"esplws-scan", \
callback_esplws_scan, \
sizeof(struct per_session_data__esplws_scan), \
512, 0, NULL \
}