socket ver
This commit is contained in:
parent
9c5f05027a
commit
4a0d3af4c1
15 changed files with 3753 additions and 30527 deletions
|
|
@ -1,8 +1,8 @@
|
|||
idf_component_register(SRCS "controlplane.c" "lib/meshtalos.c" "lib/mongoose.c" "lib/storage.c" "network.c" "lib/log.c" "lib/publish.c" "mesh_if.c"
|
||||
PRIV_REQUIRES esp_wifi esp_timer mpack lwip esp_partition nvs_flash joltwallet__littlefs esp-tls
|
||||
idf_component_register(SRCS "controlplane.c" "lib/meshtalos.c" "lib/storage.c" "network.c" "lib/log.c" "lib/picohttpparser.c"
|
||||
PRIV_REQUIRES esp_wifi esp_timer mpack lwip esp_partition nvs_flash joltwallet__littlefs esp-tls app_update
|
||||
INCLUDE_DIRS ".")
|
||||
|
||||
idf_component_set_property(main MINIMAL_BUILD ON)
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE -std=gnu23)
|
||||
target_compile_definitions(${COMPONENT_LIB} PUBLIC MG_ARCH=MG_ARCH_ESP32 PUBLIC MG_ENABLE_POSIX_FS=0 PUBLIC MG_OTA=MG_OTA_NONE)
|
||||
target_compile_definitions(${COMPONENT_LIB} PUBLIC MG_ARCH=MG_ARCH_ESP32 PUBLIC MG_ENABLE_POSIX_FS=0 PUBLIC MG_OTA=MG_OTA_NONE PUBLIC MG_IO_SIZE=2048)
|
||||
|
|
|
|||
|
|
@ -1,106 +1,84 @@
|
|||
menu "Example Configuration"
|
||||
menu "Meshtalos Configuration"
|
||||
|
||||
config MESH_CHANNEL
|
||||
int "channel"
|
||||
range 0 14
|
||||
default 0
|
||||
|
||||
menu "SoftAP Configuration"
|
||||
comment "SoftAP Configuration"
|
||||
|
||||
config ESP_WIFI_AP_SSID
|
||||
string "WiFi AP SSID"
|
||||
default "myssid"
|
||||
help
|
||||
mesh network channel.
|
||||
SSID (network name) of the AP for the example to connect to.
|
||||
|
||||
config MESH_ROUTER_SSID
|
||||
string "Router SSID"
|
||||
default "ROUTER_SSID"
|
||||
config ESP_WIFI_AP_PASSWORD
|
||||
string "WiFi AP Password"
|
||||
default "mypassword"
|
||||
help
|
||||
Router SSID.
|
||||
WiFi password of the AP for the example to use.
|
||||
|
||||
config MESH_ROUTER_PASSWD
|
||||
string "Router password"
|
||||
default "ROUTER_PASSWD"
|
||||
config ESP_WIFI_AP_CHANNEL
|
||||
int "WiFi AP Channel"
|
||||
range 1 14
|
||||
default 1
|
||||
help
|
||||
Router password.
|
||||
WiFi channel (network channel) of the AP for the example to use.
|
||||
|
||||
choice
|
||||
bool "Mesh AP Authentication Mode"
|
||||
default WIFI_AUTH_WPA2_PSK
|
||||
config ESP_MAX_STA_CONN_AP
|
||||
int "Maximal STA connections"
|
||||
default 4
|
||||
help
|
||||
Authentication mode.
|
||||
Max number of the STA connects to AP.
|
||||
endmenu
|
||||
|
||||
config WIFI_AUTH_OPEN
|
||||
bool "WIFI_AUTH_OPEN"
|
||||
config WIFI_AUTH_WPA_PSK
|
||||
bool "WIFI_AUTH_WPA_PSK"
|
||||
config WIFI_AUTH_WPA2_PSK
|
||||
bool "WIFI_AUTH_WPA2_PSK"
|
||||
config WIFI_AUTH_WPA_WPA2_PSK
|
||||
bool "WIFI_AUTH_WPA_WPA2_PSK"
|
||||
menu "STA Configuration"
|
||||
comment "STA Configuration"
|
||||
|
||||
config ESP_WIFI_REMOTE_AP_SSID
|
||||
string "WiFi Remote AP SSID"
|
||||
default "otherapssid"
|
||||
help
|
||||
SSID (network name) for the example's sta to connect to.
|
||||
|
||||
config ESP_WIFI_REMOTE_AP_PASSWORD
|
||||
string "WiFi Remote AP Password"
|
||||
default "otherappassword"
|
||||
help
|
||||
WiFi password for the example to use.
|
||||
|
||||
config ESP_MAXIMUM_STA_RETRY
|
||||
int "Maximum retry"
|
||||
default 5
|
||||
help
|
||||
Set the maximum retry value to prevent the station from continuously
|
||||
attempting to reconnect to the Access Point (AP) when the AP doesn't exist.
|
||||
|
||||
choice ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD
|
||||
prompt "WiFi Scan auth mode threshold"
|
||||
default ESP_WIFI_AUTH_WPA2_PSK
|
||||
help
|
||||
The weakest authmode to accept in the scan mode.
|
||||
This value defaults to ESP_WIFI_AUTH_WPA2_PSK in case password is present
|
||||
and ESP_WIFI_AUTH_OPEN is used. Please select ESP_WIFI_AUTH_WEP / ESP_WIFI_AUTH_WPA_PSK
|
||||
in case AP is operating in WEP / WPA mode.
|
||||
|
||||
config ESP_WIFI_AUTH_OPEN
|
||||
bool "OPEN"
|
||||
config ESP_WIFI_AUTH_WEP
|
||||
bool "WEP"
|
||||
config ESP_WIFI_AUTH_WPA_PSK
|
||||
bool "WPA PSK"
|
||||
config ESP_WIFI_AUTH_WPA2_PSK
|
||||
bool "WPA2 PSK"
|
||||
config ESP_WIFI_AUTH_WPA_WPA2_PSK
|
||||
bool "WPA/WPA2 PSK"
|
||||
config ESP_WIFI_AUTH_WPA3_PSK
|
||||
bool "WPA3 PSK"
|
||||
config ESP_WIFI_AUTH_WPA2_WPA3_PSK
|
||||
bool "WPA2/WPA3 PSK"
|
||||
config ESP_WIFI_AUTH_WAPI_PSK
|
||||
bool "WAPI PSK"
|
||||
endchoice
|
||||
|
||||
config MESH_AP_AUTHMODE
|
||||
int
|
||||
default 0 if WIFI_AUTH_OPEN
|
||||
default 2 if WIFI_AUTH_WPA_PSK
|
||||
default 3 if WIFI_AUTH_WPA2_PSK
|
||||
default 4 if WIFI_AUTH_WPA_WPA2_PSK
|
||||
help
|
||||
Mesh AP authentication mode.
|
||||
|
||||
config MESH_AP_PASSWD
|
||||
string "Mesh AP Password"
|
||||
default "MAP_PASSWD"
|
||||
help
|
||||
Mesh AP password.
|
||||
|
||||
config MESH_AP_CONNECTIONS
|
||||
int "Mesh AP Connections"
|
||||
range 1 10
|
||||
default 6
|
||||
help
|
||||
The number of mesh stations allowed to connect in.
|
||||
|
||||
config MESH_NON_MESH_AP_CONNECTIONS
|
||||
int "Mesh Non Mesh AP Connections"
|
||||
range 0 9
|
||||
default 0
|
||||
help
|
||||
The number of non-mesh stations allowed to connect in.
|
||||
|
||||
config MESH_IE_ENCRYPTED
|
||||
bool "Mesh IE encrypted"
|
||||
default y
|
||||
help
|
||||
The mesh IE is encrypted by default.
|
||||
|
||||
config MESH_MAX_LAYER
|
||||
int "Mesh Max Layer"
|
||||
range 1 25
|
||||
default 6
|
||||
help
|
||||
Max layer allowed in mesh network.
|
||||
|
||||
config MESH_ROUTE_TABLE_SIZE
|
||||
int "Mesh Routing Table Size"
|
||||
range 1 300
|
||||
default 50
|
||||
help
|
||||
The number of devices over the network(max: 300).
|
||||
|
||||
config MESH_USE_GLOBAL_DNS_IP
|
||||
bool "Use global DNS IP"
|
||||
default n
|
||||
help
|
||||
Use fixed DNS server IP address. If enabled, root node
|
||||
advertises the specified DNS address in DHCP packets.
|
||||
By default it uses DNS record received from the router.
|
||||
|
||||
config MESH_GLOBAL_DNS_IP
|
||||
hex "Global DNS"
|
||||
depends on MESH_USE_GLOBAL_DNS_IP
|
||||
default 0x08080808
|
||||
help
|
||||
The IP address of global DNS server that is used
|
||||
for internal IP subnet formed by the mesh network
|
||||
if MESH_USE_GLOBAL_DNS_IP is enabled.
|
||||
Note: The IP address is in platform (not network)
|
||||
format.
|
||||
endmenu
|
||||
|
||||
endmenu
|
||||
|
|
|
|||
|
|
@ -5,18 +5,12 @@
|
|||
#include <esp_littlefs.h>
|
||||
#include <esp_log.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define BLOCKSIZE 4096
|
||||
|
||||
const char *TAG = "main";
|
||||
|
||||
void fatal_err(int code) {
|
||||
while (1) {
|
||||
ESP_LOGE(TAG, "fatal err: %d", code);
|
||||
sleep(5);
|
||||
}
|
||||
}
|
||||
|
||||
void app_main(void) {
|
||||
|
||||
/* event initialization */
|
||||
|
|
@ -40,9 +34,16 @@ void app_main(void) {
|
|||
return;
|
||||
}
|
||||
|
||||
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
|
||||
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
|
||||
pdFALSE, pdFALSE, portMAX_DELAY);
|
||||
|
||||
if (bits & WIFI_CONNECTED_BIT) {
|
||||
struct Meshtalos *control = mtsh_init();
|
||||
if (control == NULL) {
|
||||
fatal_err(1);
|
||||
ESP_LOGE("MESHTALOS", "failed init control");
|
||||
abort();
|
||||
}
|
||||
mtsh_listen(control, 3030, 80);
|
||||
}
|
||||
mtsh_listen(control, "http://0.0.0.0:80");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,58 +1,484 @@
|
|||
#include "meshtalos.h"
|
||||
#include "freertos/idf_additions.h"
|
||||
#include "lib/mongoose.h"
|
||||
#include "./mpack.h"
|
||||
#include "./picohttpparser.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
uint8_t image_buf[IMAGE_SIZE] = {0};
|
||||
bool flag_refresh = true;
|
||||
#define STB_DS_IMPLEMENTATION
|
||||
#include "./stb_ds.h"
|
||||
|
||||
static void ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
#define HTTPVER "HTTP/1.1"
|
||||
#define IMG_W_SIZE 152
|
||||
#define IMG_H_SIZE 296
|
||||
#define DISPLAY_SIZE IMG_W_SIZE *IMG_H_SIZE
|
||||
#define IMG_HEADER_SIZE 15
|
||||
#define IMG_SIZE IMG_HEADER_SIZE + DISPLAY_SIZE
|
||||
|
||||
// struct Storage *storage = (struct Storage *)(ev_data);
|
||||
struct client {
|
||||
int fd;
|
||||
struct sockaddr_in ip4;
|
||||
bool invalid;
|
||||
};
|
||||
|
||||
if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
struct mg_http_message *hm =
|
||||
(struct mg_http_message *)ev_data; // Parsed HTTP request
|
||||
if (mg_match(hm->uri, mg_str("/api/tag"), NULL)) { // REST API call?
|
||||
if (hm->body.len == sizeof(image_buf)) {
|
||||
memcpy(image_buf, hm->body.buf, sizeof(image_buf));
|
||||
struct sync_jobs jb;
|
||||
xQueueSend(sub_jobs, &jb, 0);
|
||||
mg_http_reply(c, 200, "", "fin");
|
||||
flag_refresh = true;
|
||||
return;
|
||||
}
|
||||
mg_http_reply(c, 400, "", "bad");
|
||||
struct conn {
|
||||
int rootfd;
|
||||
fd_set master, recv;
|
||||
struct client *c;
|
||||
int max_client_fd;
|
||||
struct timeval interval;
|
||||
};
|
||||
|
||||
struct http_param {
|
||||
const char *method, *path;
|
||||
struct phr_header *headers;
|
||||
size_t method_len, path_len, headers_len, body_len;
|
||||
int minor_ver;
|
||||
char *body_start;
|
||||
};
|
||||
|
||||
struct conn workers, http;
|
||||
char iobuf[2048] = {0};
|
||||
char httpbuf[2048] = {0};
|
||||
char imgbuf[IMG_SIZE];
|
||||
struct phr_header headers[30];
|
||||
const size_t header_nums = 30;
|
||||
|
||||
void tsend();
|
||||
int natoi(const char *a, int len) {
|
||||
int ret = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (a[i] >= '0' && a[i] <= '9') {
|
||||
ret = ret * 10 + a[i] - '0';
|
||||
} else {
|
||||
mg_http_reply(c, 404, "", "bad req");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t client_send(struct client *cli, const char *buf, size_t len) {
|
||||
if (cli->invalid) {
|
||||
return -1;
|
||||
}
|
||||
int ret = send(cli->fd, buf, len, MSG_NOSIGNAL);
|
||||
if (ret < 0) {
|
||||
cli->invalid = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
ssize_t client_recv(struct client *cli, char *buf, size_t len) {
|
||||
if (cli->invalid) {
|
||||
return -1;
|
||||
}
|
||||
int ret = recv(cli->fd, buf, len, MSG_NOSIGNAL);
|
||||
if (ret <= 0) {
|
||||
cli->invalid = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
ssize_t client_dprintf(struct client *cli, const char *fmt, ...) {
|
||||
va_list v;
|
||||
va_start(v, fmt);
|
||||
size_t l = vsnprintf(httpbuf, sizeof(httpbuf), fmt, v);
|
||||
va_end(v);
|
||||
return client_send(cli, httpbuf, l);
|
||||
}
|
||||
|
||||
void conn_init(struct conn *c) {
|
||||
FD_ZERO(&c->recv);
|
||||
arrsetcap(c->c, 10);
|
||||
}
|
||||
void conn_poll_accept(struct conn *c) {
|
||||
while (1) {
|
||||
struct client temp;
|
||||
socklen_t tlen = sizeof(temp.ip4);
|
||||
temp.fd = accept(c->rootfd, (struct sockaddr *)&temp.ip4, &tlen);
|
||||
if (temp.fd != -1) {
|
||||
c->max_client_fd =
|
||||
c->max_client_fd < temp.fd ? temp.fd : c->max_client_fd;
|
||||
FD_SET(temp.fd, &c->master);
|
||||
printf("poll accepted: %s\n", inet_ntoa(temp.ip4.sin_addr));
|
||||
temp.invalid = false;
|
||||
arrput(c->c, temp);
|
||||
} else {
|
||||
if (errno != EAGAIN || errno != EWOULDBLOCK) {
|
||||
perror("accept error");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void conn_poll_clients(struct conn *c,
|
||||
void (*cb)(struct conn *c, struct client *client,
|
||||
int client_id)) {
|
||||
memcpy(&c->recv, &c->master, sizeof(c->master));
|
||||
int conn = select(c->max_client_fd + 1, &c->recv, NULL, NULL, &c->interval);
|
||||
if (conn < 0) {
|
||||
perror("select error");
|
||||
}
|
||||
for (int i = 0; i < arrlen(c->c) && conn; ++i) {
|
||||
if (FD_ISSET(c->c[i].fd, &c->recv)) {
|
||||
cb(c, &c->c[i], i);
|
||||
conn--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void conn_collect_bad_client(struct conn *c) {
|
||||
for (int i = 0; i < arrlen(c->c); i++) {
|
||||
if (c->c[i].invalid) {
|
||||
FD_CLR(c->c[i].fd, &c->master);
|
||||
close(c->c[i].fd);
|
||||
arrdelswap(c->c, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int app_close = 0;
|
||||
|
||||
/* http path handle */
|
||||
#define CLRF "\r\n"
|
||||
#define HEAD_MATCH(h, g) \
|
||||
(strlen(h) == g.name_len && strncmp(h, g.name, g.name_len) == 0)
|
||||
#define HEAD_VAL_MATCH(h, g) \
|
||||
(strlen(h) == g.value_len && strncmp(h, g.value, g.value_len) == 0)
|
||||
int badreq(struct client *c) {
|
||||
client_dprintf(c,
|
||||
HTTPVER " 400 Bad Request" CLRF "Connection: close" CLRF CLRF);
|
||||
return -1;
|
||||
}
|
||||
int fbadreq(struct client *c, const char *s, ...) {
|
||||
va_list p, tp;
|
||||
va_start(p, s);
|
||||
va_copy(tp, p);
|
||||
size_t body_len = vsnprintf(NULL, 0, s, tp);
|
||||
client_dprintf(c,
|
||||
HTTPVER " 400 Bad Request" CLRF "Connection: close" CLRF
|
||||
"Content-Length: %ld" CLRF CLRF,
|
||||
body_len);
|
||||
vsnprintf(httpbuf, sizeof(httpbuf), s, p);
|
||||
client_send(c, httpbuf, body_len);
|
||||
va_end(p);
|
||||
return -1;
|
||||
}
|
||||
int api_tag_upload(struct client *c, struct http_param *p) {
|
||||
|
||||
printf("INTO TAG RECV\n");
|
||||
int acc = 0;
|
||||
for (int i = 0; i < p->headers_len; i++) {
|
||||
if (HEAD_MATCH("Content-Type", p->headers[i]) &&
|
||||
HEAD_VAL_MATCH("image/x-portable-greymap", p->headers[i])) {
|
||||
acc++;
|
||||
}
|
||||
if (HEAD_MATCH("Content-Length", p->headers[i])) {
|
||||
if (strtol(p->headers[i].value, NULL, 10) == IMG_SIZE) {
|
||||
acc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (acc != 2) {
|
||||
|
||||
return fbadreq(c,
|
||||
"Only accept request with PGM P5 format that height and "
|
||||
"width is either %03d,%03d",
|
||||
IMG_W_SIZE, IMG_H_SIZE);
|
||||
}
|
||||
|
||||
size_t upload_size = p->body_len;
|
||||
memcpy(imgbuf, p->body_start, p->body_len);
|
||||
|
||||
while (upload_size < IMG_SIZE) {
|
||||
// upload PGM sanitize
|
||||
if (upload_size > IMG_HEADER_SIZE) {
|
||||
if (strncmp(imgbuf, "P5", 2) != 0) {
|
||||
return badreq(c);
|
||||
}
|
||||
int santinize_acc = 0;
|
||||
int width = 0, height = IMG_W_SIZE ^ IMG_H_SIZE;
|
||||
int p_start = 3, p_len = 0;
|
||||
for (int i = 3; i < upload_size; i++) {
|
||||
if (imgbuf[i] == ' ' || imgbuf[i] == '\r' || imgbuf[i] == '\n') {
|
||||
p_len = i - p_start;
|
||||
if (santinize_acc == 0) {
|
||||
width = natoi(&imgbuf[p_start], p_len);
|
||||
if (width != IMG_W_SIZE && width != IMG_H_SIZE) {
|
||||
return badreq(c);
|
||||
}
|
||||
height ^= width;
|
||||
} else if (santinize_acc == 1) {
|
||||
if (height != natoi(&imgbuf[p_start], p_len)) {
|
||||
return badreq(c);
|
||||
}
|
||||
}
|
||||
p_start = i + 1;
|
||||
p_len = 0;
|
||||
santinize_acc += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// read MIN(iobuf, left_to_read)
|
||||
size_t next_size = IMG_SIZE - upload_size > sizeof(httpbuf)
|
||||
? sizeof(httpbuf)
|
||||
: IMG_SIZE - upload_size;
|
||||
ssize_t rcv_size = client_recv(c, httpbuf, next_size);
|
||||
memcpy(imgbuf + upload_size, httpbuf, rcv_size);
|
||||
upload_size += next_size;
|
||||
}
|
||||
|
||||
client_dprintf(c, HTTPVER " 200 Good" CLRF "Connection: close" CLRF CLRF);
|
||||
tsend();
|
||||
return -1;
|
||||
}
|
||||
int nopath(struct client *c, struct http_param *p) {
|
||||
int bodylen = snprintf(NULL, 0, "No Path: %.*s", p->path_len, p->path);
|
||||
client_dprintf(c,
|
||||
HTTPVER " 404 Not Found" CLRF "Connection: close" CLRF
|
||||
"Content-Length: %d" CLRF
|
||||
"Content-Type: text/plain" CLRF CLRF,
|
||||
bodylen);
|
||||
|
||||
client_dprintf(c, "No Path: %.*s", p->path_len, p->path);
|
||||
return -1;
|
||||
}
|
||||
/* http path handle */
|
||||
|
||||
void http_client_handle(struct conn *c, struct client *client, int client_id) {
|
||||
printf("handle client\n");
|
||||
ssize_t rcv_len;
|
||||
size_t buflen = 0;
|
||||
while ((rcv_len = read(client->fd, iobuf + buflen, sizeof(iobuf) - buflen)) ==
|
||||
-1 &&
|
||||
errno == EINTR)
|
||||
;
|
||||
|
||||
if (rcv_len < 0) {
|
||||
perror("failed to recv\n");
|
||||
return;
|
||||
} else if (rcv_len == 0) {
|
||||
printf("closing request\n");
|
||||
client->invalid = true;
|
||||
return;
|
||||
} else {
|
||||
printf("read %d bytes\n", rcv_len);
|
||||
}
|
||||
|
||||
struct http_param p;
|
||||
p.headers_len = header_nums;
|
||||
p.headers = headers;
|
||||
|
||||
int pret = phr_parse_request(iobuf, rcv_len, &p.method, &p.method_len,
|
||||
&p.path, &p.path_len, &p.minor_ver, p.headers,
|
||||
&p.headers_len, 0);
|
||||
if (pret > 0) {
|
||||
p.body_start = memmem(iobuf, rcv_len, "\r\n\r\n", 4);
|
||||
if (p.body_start - iobuf + 1 >= rcv_len) {
|
||||
p.body_start = NULL;
|
||||
} else {
|
||||
p.body_start += 4;
|
||||
p.body_len = rcv_len - (p.body_start - iobuf);
|
||||
}
|
||||
printf("PATH: \"%.*s\"\n", (int)p.path_len, p.path);
|
||||
int handler_ret = 0;
|
||||
#define PEQ(g) (strlen(g) <= p.path_len && strncmp(g, p.path, strlen(g)) == 0)
|
||||
if (PEQ("/api/tag") && p.method_len == 3 &&
|
||||
strncmp("PUT", p.method, p.method_len) == 0) {
|
||||
handler_ret = api_tag_upload(client, &p);
|
||||
} else {
|
||||
handler_ret = nopath(client, &p);
|
||||
}
|
||||
|
||||
if (handler_ret == -1) {
|
||||
client->invalid = true;
|
||||
}
|
||||
} else {
|
||||
printf("can't handle request\n");
|
||||
badreq(client);
|
||||
client->invalid = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Rotates an 8-bit image 90 degrees clockwise IN-PLACE.
|
||||
* @param data Pointer to the image buffer (1 byte per pixel)
|
||||
* @param W Original width (e.g., 296)
|
||||
* @param H Original height (e.g., 152)
|
||||
* * NOTE: After this function, the logical width is H and height is W.
|
||||
* The buffer must be large enough to hold W*H bytes.
|
||||
*/
|
||||
void rotate90_clockwise_inplace(uint8_t *data, int W, int H) {
|
||||
int total_pixels = W * H;
|
||||
|
||||
// We need 1 bit per pixel to track if we've moved it.
|
||||
// For 296x152, this is 44,992 bits = 5,624 bytes.
|
||||
uint8_t *visited = (uint8_t *)calloc((total_pixels + 7) / 8, 1);
|
||||
if (!visited)
|
||||
return; // Handle allocation failure (OOM)
|
||||
|
||||
for (int i = 0; i < total_pixels; i++) {
|
||||
// Skip if this pixel was already moved as part of a previous cycle
|
||||
if (visited[i >> 3] & (1 << (i & 7)))
|
||||
continue;
|
||||
|
||||
int curr_idx = i;
|
||||
uint8_t val_to_move = data[i];
|
||||
|
||||
while (!(visited[curr_idx >> 3] & (1 << (curr_idx & 7)))) {
|
||||
// Mark as visited
|
||||
visited[curr_idx >> 3] |= (1 << (curr_idx & 7));
|
||||
|
||||
// Calculate new coordinates for 90-degree clockwise
|
||||
// (x, y) -> (H - 1 - y, x)
|
||||
int x = curr_idx % W;
|
||||
int y = curr_idx / W;
|
||||
|
||||
// The NEW dimensions are Height H and Width W (swapped)
|
||||
// New 1D Index = new_y * NEW_WIDTH + new_x
|
||||
// For 90deg CW: new_x = H - 1 - y, new_y = x
|
||||
int next_idx = x * H + (H - 1 - y);
|
||||
|
||||
// Swap values
|
||||
uint8_t temp = data[next_idx];
|
||||
data[next_idx] = val_to_move;
|
||||
val_to_move = temp;
|
||||
|
||||
curr_idx = next_idx;
|
||||
}
|
||||
}
|
||||
free(visited);
|
||||
}
|
||||
#define IMG_BLK_SIZE 1024
|
||||
|
||||
void flush_mpack(mpack_writer_t *wr, const char *b, size_t len) {
|
||||
struct client *c = mpack_writer_context(wr);
|
||||
printf("written %d bytes\n", len);
|
||||
if (client_send(c, b, len) <= 0) {
|
||||
mpack_writer_flag_error(wr, mpack_error_io);
|
||||
}
|
||||
}
|
||||
|
||||
void send_image(struct client *c) {
|
||||
mpack_writer_t wr;
|
||||
mpack_writer_init(&wr, iobuf, sizeof(iobuf));
|
||||
mpack_writer_set_flush(&wr, flush_mpack);
|
||||
mpack_writer_set_context(&wr, c);
|
||||
for (uint64_t i = IMG_HEADER_SIZE; i < IMG_SIZE; i += IMG_BLK_SIZE) {
|
||||
mpack_start_map(&wr, 3);
|
||||
mpack_write_cstr(&wr, "command");
|
||||
mpack_write_cstr(&wr, "update_image");
|
||||
mpack_write_cstr(&wr, "index");
|
||||
mpack_write_u64(&wr, i);
|
||||
mpack_write_cstr(&wr, "data");
|
||||
mpack_write_bin(&wr, &imgbuf[i], MIN(IMG_BLK_SIZE, IMG_SIZE - i));
|
||||
mpack_finish_map(&wr);
|
||||
mpack_writer_flush_message(&wr);
|
||||
}
|
||||
mpack_start_map(&wr, 1);
|
||||
mpack_write_cstr(&wr, "command");
|
||||
mpack_write_cstr(&wr, "push_image");
|
||||
mpack_writer_destroy(&wr);
|
||||
}
|
||||
|
||||
void tsend() {
|
||||
|
||||
int param_acc = 0, param_start = 0, param_len = 0;
|
||||
int img_w = 0, img_h = 0;
|
||||
for (int i = 0; i < sizeof(imgbuf); i++) {
|
||||
if (imgbuf[i] == ' ' || imgbuf[i] == '\r' || imgbuf[i] == '\n') {
|
||||
param_len = i - param_start;
|
||||
switch (param_acc) {
|
||||
case 1:
|
||||
img_w = natoi(&imgbuf[param_start], param_len);
|
||||
break;
|
||||
case 2:
|
||||
img_h = natoi(&imgbuf[param_start], param_len);
|
||||
break;
|
||||
}
|
||||
|
||||
param_start = i + 1;
|
||||
param_acc += 1;
|
||||
}
|
||||
}
|
||||
printf("img_w: %d, img_h: %d\n", img_w, img_h);
|
||||
if (img_w == IMG_H_SIZE) {
|
||||
rotate90_clockwise_inplace((uint8_t *)imgbuf + IMG_HEADER_SIZE, IMG_H_SIZE,
|
||||
IMG_W_SIZE);
|
||||
}
|
||||
for (int i = 0; i < arrlen(workers.c); i++) {
|
||||
send_image(workers.c + i);
|
||||
}
|
||||
}
|
||||
|
||||
int main_listen(const int worker_port, int http_port) {
|
||||
// workers
|
||||
int sfd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
|
||||
struct sockaddr_in b = {0};
|
||||
memset(&b, 0, sizeof(struct sockaddr_in));
|
||||
b.sin_port = htons(worker_port);
|
||||
b.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
b.sin_family = AF_INET;
|
||||
int val = 1;
|
||||
assert(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != -1);
|
||||
assert(bind(sfd, (struct sockaddr *)&b, sizeof(b)) != -1);
|
||||
assert(listen(sfd, 5) != -1);
|
||||
printf("start listen\n");
|
||||
int flags = fcntl(sfd, F_GETFL);
|
||||
assert(fcntl(sfd, F_SETFL, flags | O_NONBLOCK) != -1);
|
||||
|
||||
// HTTP
|
||||
|
||||
int httpfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
memset(&b, 0, sizeof(struct sockaddr_in));
|
||||
b.sin_port = htons(http_port);
|
||||
b.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
b.sin_family = AF_INET;
|
||||
val = 1;
|
||||
assert(setsockopt(httpfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != -1);
|
||||
assert(bind(httpfd, (struct sockaddr *)&b, sizeof(b)) != -1);
|
||||
assert(listen(httpfd, 5) != -1);
|
||||
printf("start listen\n");
|
||||
flags = fcntl(httpfd, F_GETFL);
|
||||
assert(fcntl(httpfd, F_SETFL, flags | O_NONBLOCK) != -1);
|
||||
|
||||
workers.interval.tv_usec = 300;
|
||||
workers.rootfd = sfd;
|
||||
conn_init(&workers);
|
||||
http.interval.tv_usec = 300;
|
||||
http.rootfd = httpfd;
|
||||
conn_init(&http);
|
||||
|
||||
// event loop
|
||||
while (!app_close) {
|
||||
conn_poll_accept(&workers);
|
||||
conn_poll_accept(&http);
|
||||
conn_poll_clients(&http, &http_client_handle);
|
||||
conn_collect_bad_client(&workers);
|
||||
conn_collect_bad_client(&http);
|
||||
|
||||
// printf("iter\n");
|
||||
// usleep(500000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mtsh_listen(struct Meshtalos *mtsh, const int worker_port, int http_port) {
|
||||
main_listen(worker_port, http_port);
|
||||
}
|
||||
|
||||
struct Meshtalos *mtsh_init() {
|
||||
struct Meshtalos *mtsh = calloc(1, sizeof(struct Meshtalos));
|
||||
if (mtsh == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// if ((mtsh->storage = storage_init()) == NULL) {
|
||||
// goto lfs_err;
|
||||
// }
|
||||
mg_mgr_init(&mtsh->mg);
|
||||
|
||||
return mtsh;
|
||||
// lfs_err:
|
||||
// if (mtsh != NULL) {
|
||||
// free(mtsh);
|
||||
// }
|
||||
// return NULL;
|
||||
}
|
||||
|
||||
void mtsh_listen(struct Meshtalos *mtsh, const char *http_endpoint) {
|
||||
mg_http_listen(&mtsh->mg, http_endpoint, ev_handler, &mtsh->storage);
|
||||
mtsh_tcp_sender(&mtsh->mg);
|
||||
for (;;) {
|
||||
mg_mgr_poll(&mtsh->mg, 1000);
|
||||
}
|
||||
struct Meshtalos *m = calloc(sizeof(struct Meshtalos), 1);
|
||||
return m;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,32 +8,29 @@
|
|||
* mpack formatted: [ uint8_t id = 1, char topic[TOPIC_LEN] ]
|
||||
*/
|
||||
#pragma once
|
||||
#include "./mongoose.h"
|
||||
#include "./storage.h"
|
||||
|
||||
#define TOPIC_LEN 20
|
||||
#define IMAGE_SIZE 152 * 296
|
||||
#define IMAGE_BLK_SIZE 1024
|
||||
#define IMG_W_SIZE 152
|
||||
#define IMG_H_SIZE 296
|
||||
#define DISPLAY_SIZE IMG_W_SIZE *IMG_H_SIZE
|
||||
#define IMG_HEADER_SIZE 15
|
||||
#define IMG_SIZE IMG_HEADER_SIZE + DISPLAY_SIZE
|
||||
#define IMG_BLK_SIZE 300
|
||||
|
||||
struct Meshtalos {
|
||||
struct Storage *storage;
|
||||
struct mg_mgr mg;
|
||||
};
|
||||
|
||||
struct sync_jobs {
|
||||
// char *tag_name;
|
||||
char topic[TOPIC_LEN];
|
||||
};
|
||||
struct sub {
|
||||
struct mg_connection *c;
|
||||
char topic[TOPIC_LEN];
|
||||
struct sub *next;
|
||||
};
|
||||
extern struct sub *sub_list;
|
||||
extern QueueHandle_t sub_jobs;
|
||||
extern SemaphoreHandle_t sub_mutex;
|
||||
extern uint8_t image_buf[IMAGE_SIZE];
|
||||
// struct sync_jobs {
|
||||
// // char *tag_name;
|
||||
// char topic[TOPIC_LEN];
|
||||
// };
|
||||
// struct sub {
|
||||
// char topic[TOPIC_LEN];
|
||||
// struct sub *next;
|
||||
// };
|
||||
// extern struct sub *sub_list;
|
||||
|
||||
struct Meshtalos *mtsh_init();
|
||||
void mtsh_listen(struct Meshtalos *mtsh, const char *http_endpoint);
|
||||
void mtsh_tcp_sender(struct mg_mgr *mgr);
|
||||
void mtsh_listen(struct Meshtalos *mtsh, const int worker_port, int http_port);
|
||||
|
|
|
|||
25608
main/lib/mongoose.c
25608
main/lib/mongoose.c
File diff suppressed because it is too large
Load diff
3781
main/lib/mongoose.h
3781
main/lib/mongoose.h
File diff suppressed because it is too large
Load diff
738
main/lib/picohttpparser.c
Normal file
738
main/lib/picohttpparser.c
Normal file
|
|
@ -0,0 +1,738 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
|
||||
* Shigeo Mitsunari
|
||||
*
|
||||
* The software is licensed under either the MIT License (below) or the Perl
|
||||
* license.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#ifdef __SSE4_2__
|
||||
#ifdef _MSC_VER
|
||||
#include <nmmintrin.h>
|
||||
#else
|
||||
#include <x86intrin.h>
|
||||
#endif
|
||||
#endif
|
||||
#include "picohttpparser.h"
|
||||
|
||||
#if __GNUC__ >= 3
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
#else
|
||||
#define likely(x) (x)
|
||||
#define unlikely(x) (x)
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define ALIGNED(n) _declspec(align(n))
|
||||
#else
|
||||
#define ALIGNED(n) __attribute__((aligned(n)))
|
||||
#endif
|
||||
|
||||
#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c) - 040u < 0137u)
|
||||
|
||||
#define CHECK_EOF() \
|
||||
if (buf == buf_end) { \
|
||||
*ret = -2; \
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
#define EXPECT_CHAR_NO_CHECK(ch) \
|
||||
if (*buf++ != ch) { \
|
||||
*ret = -1; \
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
#define EXPECT_CHAR(ch) \
|
||||
CHECK_EOF(); \
|
||||
EXPECT_CHAR_NO_CHECK(ch);
|
||||
|
||||
#define ADVANCE_TOKEN(tok, toklen) \
|
||||
do { \
|
||||
const char *tok_start = buf; \
|
||||
static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \
|
||||
int found2; \
|
||||
buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \
|
||||
if (!found2) { \
|
||||
CHECK_EOF(); \
|
||||
} \
|
||||
while (1) { \
|
||||
if (*buf == ' ') { \
|
||||
break; \
|
||||
} else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \
|
||||
if ((unsigned char)*buf < '\040' || *buf == '\177') { \
|
||||
*ret = -1; \
|
||||
return NULL; \
|
||||
} \
|
||||
} \
|
||||
++buf; \
|
||||
CHECK_EOF(); \
|
||||
} \
|
||||
tok = tok_start; \
|
||||
toklen = buf - tok_start; \
|
||||
} while (0)
|
||||
|
||||
static const char *token_char_map =
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0"
|
||||
"\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1"
|
||||
"\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
||||
|
||||
static const char *findchar_fast(const char *buf, const char *buf_end,
|
||||
const char *ranges, size_t ranges_size,
|
||||
int *found) {
|
||||
*found = 0;
|
||||
#if __SSE4_2__
|
||||
if (likely(buf_end - buf >= 16)) {
|
||||
__m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges);
|
||||
|
||||
size_t left = (buf_end - buf) & ~15;
|
||||
do {
|
||||
__m128i b16 = _mm_loadu_si128((const __m128i *)buf);
|
||||
int r = _mm_cmpestri(ranges16, ranges_size, b16, 16,
|
||||
_SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES |
|
||||
_SIDD_UBYTE_OPS);
|
||||
if (unlikely(r != 16)) {
|
||||
buf += r;
|
||||
*found = 1;
|
||||
break;
|
||||
}
|
||||
buf += 16;
|
||||
left -= 16;
|
||||
} while (likely(left != 0));
|
||||
}
|
||||
#else
|
||||
/* suppress unused parameter warning */
|
||||
(void)buf_end;
|
||||
(void)ranges;
|
||||
(void)ranges_size;
|
||||
#endif
|
||||
return buf;
|
||||
}
|
||||
|
||||
static const char *get_token_to_eol(const char *buf, const char *buf_end,
|
||||
const char **token, size_t *token_len,
|
||||
int *ret) {
|
||||
const char *token_start = buf;
|
||||
|
||||
#ifdef __SSE4_2__
|
||||
static const char ALIGNED(16) ranges1[16] =
|
||||
"\0\010" /* allow HT */
|
||||
"\012\037" /* allow SP and up to but not including DEL */
|
||||
"\177\177"; /* allow chars w. MSB set */
|
||||
int found;
|
||||
buf = findchar_fast(buf, buf_end, ranges1, 6, &found);
|
||||
if (found)
|
||||
goto FOUND_CTL;
|
||||
#else
|
||||
/* find non-printable char within the next 8 bytes, this is the hottest code;
|
||||
* manually inlined */
|
||||
while (likely(buf_end - buf >= 8)) {
|
||||
#define DOIT() \
|
||||
do { \
|
||||
if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \
|
||||
goto NonPrintable; \
|
||||
++buf; \
|
||||
} while (0)
|
||||
DOIT();
|
||||
DOIT();
|
||||
DOIT();
|
||||
DOIT();
|
||||
DOIT();
|
||||
DOIT();
|
||||
DOIT();
|
||||
DOIT();
|
||||
#undef DOIT
|
||||
continue;
|
||||
NonPrintable:
|
||||
if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) ||
|
||||
unlikely(*buf == '\177')) {
|
||||
goto FOUND_CTL;
|
||||
}
|
||||
++buf;
|
||||
}
|
||||
#endif
|
||||
for (;; ++buf) {
|
||||
CHECK_EOF();
|
||||
if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {
|
||||
if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) ||
|
||||
unlikely(*buf == '\177')) {
|
||||
goto FOUND_CTL;
|
||||
}
|
||||
}
|
||||
}
|
||||
FOUND_CTL:
|
||||
if (likely(*buf == '\015')) {
|
||||
++buf;
|
||||
EXPECT_CHAR('\012');
|
||||
*token_len = buf - 2 - token_start;
|
||||
} else if (*buf == '\012') {
|
||||
*token_len = buf - token_start;
|
||||
++buf;
|
||||
} else {
|
||||
*ret = -1;
|
||||
return NULL;
|
||||
}
|
||||
*token = token_start;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static const char *is_complete(const char *buf, const char *buf_end,
|
||||
size_t last_len, int *ret) {
|
||||
int ret_cnt = 0;
|
||||
buf = last_len < 3 ? buf : buf + last_len - 3;
|
||||
|
||||
while (1) {
|
||||
CHECK_EOF();
|
||||
if (*buf == '\015') {
|
||||
++buf;
|
||||
CHECK_EOF();
|
||||
EXPECT_CHAR('\012');
|
||||
++ret_cnt;
|
||||
} else if (*buf == '\012') {
|
||||
++buf;
|
||||
++ret_cnt;
|
||||
} else {
|
||||
++buf;
|
||||
ret_cnt = 0;
|
||||
}
|
||||
if (ret_cnt == 2) {
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
*ret = -2;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define PARSE_INT(valp_, mul_) \
|
||||
if (*buf < '0' || '9' < *buf) { \
|
||||
buf++; \
|
||||
*ret = -1; \
|
||||
return NULL; \
|
||||
} \
|
||||
*(valp_) = (mul_) * (*buf++ - '0');
|
||||
|
||||
#define PARSE_INT_3(valp_) \
|
||||
do { \
|
||||
int res_ = 0; \
|
||||
PARSE_INT(&res_, 100) \
|
||||
*valp_ = res_; \
|
||||
PARSE_INT(&res_, 10) \
|
||||
*valp_ += res_; \
|
||||
PARSE_INT(&res_, 1) \
|
||||
*valp_ += res_; \
|
||||
} while (0)
|
||||
|
||||
/* returned pointer is always within [buf, buf_end), or null */
|
||||
static const char *parse_token(const char *buf, const char *buf_end,
|
||||
const char **token, size_t *token_len,
|
||||
char next_char, int *ret) {
|
||||
/* We use pcmpestri to detect non-token characters. This instruction can take
|
||||
* no more than eight character ranges (8*2*8=128 bits that is the size of a
|
||||
* SSE register). Due to this restriction, characters `|` and `~` are handled
|
||||
* in the slow loop. */
|
||||
static const char ALIGNED(16) ranges[] =
|
||||
"\x00 " /* control chars and up to SP */
|
||||
"\"\"" /* 0x22 */
|
||||
"()" /* 0x28,0x29 */
|
||||
",," /* 0x2c */
|
||||
"//" /* 0x2f */
|
||||
":@" /* 0x3a-0x40 */
|
||||
"[]" /* 0x5b-0x5d */
|
||||
"{\xff"; /* 0x7b-0xff */
|
||||
const char *buf_start = buf;
|
||||
int found;
|
||||
buf = findchar_fast(buf, buf_end, ranges, sizeof(ranges) - 1, &found);
|
||||
if (!found) {
|
||||
CHECK_EOF();
|
||||
}
|
||||
while (1) {
|
||||
if (*buf == next_char) {
|
||||
break;
|
||||
} else if (!token_char_map[(unsigned char)*buf]) {
|
||||
*ret = -1;
|
||||
return NULL;
|
||||
}
|
||||
++buf;
|
||||
CHECK_EOF();
|
||||
}
|
||||
*token = buf_start;
|
||||
*token_len = buf - buf_start;
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* returned pointer is always within [buf, buf_end), or null */
|
||||
static const char *parse_http_version(const char *buf, const char *buf_end,
|
||||
int *minor_version, int *ret) {
|
||||
/* we want at least [HTTP/1.<two chars>] to try to parse */
|
||||
if (buf_end - buf < 9) {
|
||||
*ret = -2;
|
||||
return NULL;
|
||||
}
|
||||
EXPECT_CHAR_NO_CHECK('H');
|
||||
EXPECT_CHAR_NO_CHECK('T');
|
||||
EXPECT_CHAR_NO_CHECK('T');
|
||||
EXPECT_CHAR_NO_CHECK('P');
|
||||
EXPECT_CHAR_NO_CHECK('/');
|
||||
EXPECT_CHAR_NO_CHECK('1');
|
||||
EXPECT_CHAR_NO_CHECK('.');
|
||||
PARSE_INT(minor_version, 1);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static const char *parse_headers(const char *buf, const char *buf_end,
|
||||
struct phr_header *headers,
|
||||
size_t *num_headers, size_t max_headers,
|
||||
int *ret) {
|
||||
for (;; ++*num_headers) {
|
||||
CHECK_EOF();
|
||||
if (*buf == '\015') {
|
||||
++buf;
|
||||
EXPECT_CHAR('\012');
|
||||
break;
|
||||
} else if (*buf == '\012') {
|
||||
++buf;
|
||||
break;
|
||||
}
|
||||
if (*num_headers == max_headers) {
|
||||
*ret = -1;
|
||||
return NULL;
|
||||
}
|
||||
if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) {
|
||||
/* parsing name, but do not discard SP before colon, see
|
||||
* http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */
|
||||
if ((buf = parse_token(buf, buf_end, &headers[*num_headers].name,
|
||||
&headers[*num_headers].name_len, ':', ret)) ==
|
||||
NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (headers[*num_headers].name_len == 0) {
|
||||
*ret = -1;
|
||||
return NULL;
|
||||
}
|
||||
++buf;
|
||||
for (;; ++buf) {
|
||||
CHECK_EOF();
|
||||
if (!(*buf == ' ' || *buf == '\t')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
headers[*num_headers].name = NULL;
|
||||
headers[*num_headers].name_len = 0;
|
||||
}
|
||||
const char *value;
|
||||
size_t value_len;
|
||||
if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len, ret)) ==
|
||||
NULL) {
|
||||
return NULL;
|
||||
}
|
||||
/* remove trailing SPs and HTABs */
|
||||
const char *value_end = value + value_len;
|
||||
for (; value_end != value; --value_end) {
|
||||
const char c = *(value_end - 1);
|
||||
if (!(c == ' ' || c == '\t')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
headers[*num_headers].value = value;
|
||||
headers[*num_headers].value_len = value_end - value;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
static const char *parse_request(const char *buf, const char *buf_end,
|
||||
const char **method, size_t *method_len,
|
||||
const char **path, size_t *path_len,
|
||||
int *minor_version, struct phr_header *headers,
|
||||
size_t *num_headers, size_t max_headers,
|
||||
int *ret) {
|
||||
/* skip first empty line (some clients add CRLF after POST content) */
|
||||
CHECK_EOF();
|
||||
if (*buf == '\015') {
|
||||
++buf;
|
||||
EXPECT_CHAR('\012');
|
||||
} else if (*buf == '\012') {
|
||||
++buf;
|
||||
}
|
||||
|
||||
/* parse request line */
|
||||
if ((buf = parse_token(buf, buf_end, method, method_len, ' ', ret)) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
do {
|
||||
++buf;
|
||||
CHECK_EOF();
|
||||
} while (*buf == ' ');
|
||||
ADVANCE_TOKEN(*path, *path_len);
|
||||
do {
|
||||
++buf;
|
||||
CHECK_EOF();
|
||||
} while (*buf == ' ');
|
||||
if (*method_len == 0 || *path_len == 0) {
|
||||
*ret = -1;
|
||||
return NULL;
|
||||
}
|
||||
if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (*buf == '\015') {
|
||||
++buf;
|
||||
EXPECT_CHAR('\012');
|
||||
} else if (*buf == '\012') {
|
||||
++buf;
|
||||
} else {
|
||||
*ret = -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
|
||||
}
|
||||
|
||||
int phr_parse_request(const char *buf_start, size_t len, const char **method,
|
||||
size_t *method_len, const char **path, size_t *path_len,
|
||||
int *minor_version, struct phr_header *headers,
|
||||
size_t *num_headers, size_t last_len) {
|
||||
const char *buf = buf_start, *buf_end = buf_start + len;
|
||||
size_t max_headers = *num_headers;
|
||||
int r;
|
||||
|
||||
*method = NULL;
|
||||
*method_len = 0;
|
||||
*path = NULL;
|
||||
*path_len = 0;
|
||||
*minor_version = -1;
|
||||
*num_headers = 0;
|
||||
|
||||
/* if last_len != 0, check if the request is complete (a fast countermeasure
|
||||
againt slowloris */
|
||||
if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
|
||||
return r;
|
||||
}
|
||||
|
||||
if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len,
|
||||
minor_version, headers, num_headers, max_headers,
|
||||
&r)) == NULL) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return (int)(buf - buf_start);
|
||||
}
|
||||
|
||||
static const char *parse_response(const char *buf, const char *buf_end,
|
||||
int *minor_version, int *status,
|
||||
const char **msg, size_t *msg_len,
|
||||
struct phr_header *headers,
|
||||
size_t *num_headers, size_t max_headers,
|
||||
int *ret) {
|
||||
/* parse "HTTP/1.x" */
|
||||
if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
/* skip space */
|
||||
if (*buf != ' ') {
|
||||
*ret = -1;
|
||||
return NULL;
|
||||
}
|
||||
do {
|
||||
++buf;
|
||||
CHECK_EOF();
|
||||
} while (*buf == ' ');
|
||||
/* parse status code, we want at least [:digit:][:digit:][:digit:]<other char>
|
||||
* to try to parse */
|
||||
if (buf_end - buf < 4) {
|
||||
*ret = -2;
|
||||
return NULL;
|
||||
}
|
||||
PARSE_INT_3(status);
|
||||
|
||||
/* get message including preceding space */
|
||||
if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (*msg_len == 0) {
|
||||
/* ok */
|
||||
} else if (**msg == ' ') {
|
||||
/* Remove preceding space. Successful return from `get_token_to_eol`
|
||||
* guarantees that we would hit something other than SP before running past
|
||||
* the end of the given buffer. */
|
||||
do {
|
||||
++*msg;
|
||||
--*msg_len;
|
||||
} while (**msg == ' ');
|
||||
} else {
|
||||
/* garbage found after status code */
|
||||
*ret = -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
|
||||
}
|
||||
|
||||
int phr_parse_response(const char *buf_start, size_t len, int *minor_version,
|
||||
int *status, const char **msg, size_t *msg_len,
|
||||
struct phr_header *headers, size_t *num_headers,
|
||||
size_t last_len) {
|
||||
const char *buf = buf_start, *buf_end = buf + len;
|
||||
size_t max_headers = *num_headers;
|
||||
int r;
|
||||
|
||||
*minor_version = -1;
|
||||
*status = 0;
|
||||
*msg = NULL;
|
||||
*msg_len = 0;
|
||||
*num_headers = 0;
|
||||
|
||||
/* if last_len != 0, check if the response is complete (a fast countermeasure
|
||||
against slowloris */
|
||||
if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
|
||||
return r;
|
||||
}
|
||||
|
||||
if ((buf = parse_response(buf, buf_end, minor_version, status, msg, msg_len,
|
||||
headers, num_headers, max_headers, &r)) == NULL) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return (int)(buf - buf_start);
|
||||
}
|
||||
|
||||
int phr_parse_headers(const char *buf_start, size_t len,
|
||||
struct phr_header *headers, size_t *num_headers,
|
||||
size_t last_len) {
|
||||
const char *buf = buf_start, *buf_end = buf + len;
|
||||
size_t max_headers = *num_headers;
|
||||
int r;
|
||||
|
||||
*num_headers = 0;
|
||||
|
||||
/* if last_len != 0, check if the response is complete (a fast countermeasure
|
||||
against slowloris */
|
||||
if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
|
||||
return r;
|
||||
}
|
||||
|
||||
if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers,
|
||||
&r)) == NULL) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return (int)(buf - buf_start);
|
||||
}
|
||||
|
||||
enum {
|
||||
CHUNKED_IN_CHUNK_SIZE,
|
||||
CHUNKED_IN_CHUNK_EXT,
|
||||
CHUNKED_IN_CHUNK_HEADER_EXPECT_LF,
|
||||
CHUNKED_IN_CHUNK_DATA,
|
||||
CHUNKED_IN_CHUNK_DATA_EXPECT_CR,
|
||||
CHUNKED_IN_CHUNK_DATA_EXPECT_LF,
|
||||
CHUNKED_IN_TRAILERS_LINE_HEAD,
|
||||
CHUNKED_IN_TRAILERS_LINE_MIDDLE
|
||||
};
|
||||
|
||||
static int decode_hex(int ch) {
|
||||
if ('0' <= ch && ch <= '9') {
|
||||
return ch - '0';
|
||||
} else if ('A' <= ch && ch <= 'F') {
|
||||
return ch - 'A' + 0xa;
|
||||
} else if ('a' <= ch && ch <= 'f') {
|
||||
return ch - 'a' + 0xa;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf,
|
||||
size_t *_bufsz) {
|
||||
size_t dst = 0, src = 0, bufsz = *_bufsz;
|
||||
ssize_t ret = -2; /* incomplete */
|
||||
|
||||
decoder->_total_read += bufsz;
|
||||
|
||||
while (1) {
|
||||
switch (decoder->_state) {
|
||||
case CHUNKED_IN_CHUNK_SIZE:
|
||||
for (;; ++src) {
|
||||
int v;
|
||||
if (src == bufsz)
|
||||
goto Exit;
|
||||
if ((v = decode_hex(buf[src])) == -1) {
|
||||
if (decoder->_hex_count == 0) {
|
||||
ret = -1;
|
||||
goto Exit;
|
||||
}
|
||||
/* the only characters that may appear after the chunk size are BWS,
|
||||
* semicolon, or CRLF */
|
||||
switch (buf[src]) {
|
||||
case ' ':
|
||||
case '\011':
|
||||
case ';':
|
||||
case '\012':
|
||||
case '\015':
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
goto Exit;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (decoder->_hex_count == sizeof(size_t) * 2) {
|
||||
ret = -1;
|
||||
goto Exit;
|
||||
}
|
||||
decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + v;
|
||||
++decoder->_hex_count;
|
||||
}
|
||||
decoder->_hex_count = 0;
|
||||
decoder->_state = CHUNKED_IN_CHUNK_EXT;
|
||||
/* fallthru */
|
||||
case CHUNKED_IN_CHUNK_EXT:
|
||||
/* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */
|
||||
for (;; ++src) {
|
||||
if (src == bufsz)
|
||||
goto Exit;
|
||||
if (buf[src] == '\015') {
|
||||
break;
|
||||
} else if (buf[src] == '\012') {
|
||||
ret = -1;
|
||||
goto Exit;
|
||||
}
|
||||
}
|
||||
++src;
|
||||
decoder->_state = CHUNKED_IN_CHUNK_HEADER_EXPECT_LF;
|
||||
/* fallthru */
|
||||
case CHUNKED_IN_CHUNK_HEADER_EXPECT_LF:
|
||||
if (src == bufsz)
|
||||
goto Exit;
|
||||
if (buf[src] != '\012') {
|
||||
ret = -1;
|
||||
goto Exit;
|
||||
}
|
||||
++src;
|
||||
if (decoder->bytes_left_in_chunk == 0) {
|
||||
if (decoder->consume_trailer) {
|
||||
decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
|
||||
break;
|
||||
} else {
|
||||
goto Complete;
|
||||
}
|
||||
}
|
||||
decoder->_state = CHUNKED_IN_CHUNK_DATA;
|
||||
/* fallthru */
|
||||
case CHUNKED_IN_CHUNK_DATA: {
|
||||
size_t avail = bufsz - src;
|
||||
if (avail < decoder->bytes_left_in_chunk) {
|
||||
if (dst != src)
|
||||
memmove(buf + dst, buf + src, avail);
|
||||
src += avail;
|
||||
dst += avail;
|
||||
decoder->bytes_left_in_chunk -= avail;
|
||||
goto Exit;
|
||||
}
|
||||
if (dst != src)
|
||||
memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk);
|
||||
src += decoder->bytes_left_in_chunk;
|
||||
dst += decoder->bytes_left_in_chunk;
|
||||
decoder->bytes_left_in_chunk = 0;
|
||||
decoder->_state = CHUNKED_IN_CHUNK_DATA_EXPECT_CR;
|
||||
}
|
||||
/* fallthru */
|
||||
case CHUNKED_IN_CHUNK_DATA_EXPECT_CR:
|
||||
if (src == bufsz)
|
||||
goto Exit;
|
||||
if (buf[src] != '\015') {
|
||||
ret = -1;
|
||||
goto Exit;
|
||||
}
|
||||
++src;
|
||||
decoder->_state = CHUNKED_IN_CHUNK_DATA_EXPECT_LF;
|
||||
/* fallthru */
|
||||
case CHUNKED_IN_CHUNK_DATA_EXPECT_LF:
|
||||
if (src == bufsz)
|
||||
goto Exit;
|
||||
if (buf[src] != '\012') {
|
||||
ret = -1;
|
||||
goto Exit;
|
||||
}
|
||||
++src;
|
||||
decoder->_state = CHUNKED_IN_CHUNK_SIZE;
|
||||
break;
|
||||
case CHUNKED_IN_TRAILERS_LINE_HEAD:
|
||||
for (;; ++src) {
|
||||
if (src == bufsz)
|
||||
goto Exit;
|
||||
if (buf[src] != '\015')
|
||||
break;
|
||||
}
|
||||
if (buf[src++] == '\012')
|
||||
goto Complete;
|
||||
decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE;
|
||||
/* fallthru */
|
||||
case CHUNKED_IN_TRAILERS_LINE_MIDDLE:
|
||||
for (;; ++src) {
|
||||
if (src == bufsz)
|
||||
goto Exit;
|
||||
if (buf[src] == '\012')
|
||||
break;
|
||||
}
|
||||
++src;
|
||||
decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
|
||||
break;
|
||||
default:
|
||||
assert(!"decoder is corrupt");
|
||||
}
|
||||
}
|
||||
|
||||
Complete:
|
||||
ret = bufsz - src;
|
||||
Exit:
|
||||
if (dst != src)
|
||||
memmove(buf + dst, buf + src, bufsz - src);
|
||||
*_bufsz = dst;
|
||||
/* if incomplete but the overhead of the chunked encoding is >=100KB and >80%,
|
||||
* signal an error */
|
||||
if (ret == -2) {
|
||||
decoder->_total_overhead += bufsz - dst;
|
||||
if (decoder->_total_overhead >= 100 * 1024 &&
|
||||
decoder->_total_read - decoder->_total_overhead <
|
||||
decoder->_total_read / 4)
|
||||
ret = -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder) {
|
||||
return decoder->_state == CHUNKED_IN_CHUNK_DATA;
|
||||
}
|
||||
|
||||
#undef CHECK_EOF
|
||||
#undef EXPECT_CHAR
|
||||
#undef ADVANCE_TOKEN
|
||||
96
main/lib/picohttpparser.h
Normal file
96
main/lib/picohttpparser.h
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
|
||||
* Shigeo Mitsunari
|
||||
*
|
||||
* The software is licensed under either the MIT License (below) or the Perl
|
||||
* license.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef picohttpparser_h
|
||||
#define picohttpparser_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define ssize_t intptr_t
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* contains name and value of a header (name == NULL if is a continuing line
|
||||
* of a multiline header */
|
||||
struct phr_header {
|
||||
const char *name;
|
||||
size_t name_len;
|
||||
const char *value;
|
||||
size_t value_len;
|
||||
};
|
||||
|
||||
/* returns number of bytes consumed if successful, -2 if request is partial,
|
||||
* -1 if failed */
|
||||
int phr_parse_request(const char *buf, size_t len, const char **method,
|
||||
size_t *method_len, const char **path, size_t *path_len,
|
||||
int *minor_version, struct phr_header *headers,
|
||||
size_t *num_headers, size_t last_len);
|
||||
|
||||
/* ditto */
|
||||
int phr_parse_response(const char *_buf, size_t len, int *minor_version,
|
||||
int *status, const char **msg, size_t *msg_len,
|
||||
struct phr_header *headers, size_t *num_headers,
|
||||
size_t last_len);
|
||||
|
||||
/* ditto */
|
||||
int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers,
|
||||
size_t *num_headers, size_t last_len);
|
||||
|
||||
/* should be zero-filled before start */
|
||||
struct phr_chunked_decoder {
|
||||
size_t bytes_left_in_chunk; /* number of bytes left in current chunk */
|
||||
char consume_trailer; /* if trailing headers should be consumed */
|
||||
char _hex_count;
|
||||
char _state;
|
||||
uint64_t _total_read;
|
||||
uint64_t _total_overhead;
|
||||
};
|
||||
|
||||
/* the function rewrites the buffer given as (buf, bufsz) removing the chunked-
|
||||
* encoding headers. When the function returns without an error, bufsz is
|
||||
* updated to the length of the decoded data available. Applications should
|
||||
* repeatedly call the function while it returns -2 (incomplete) every time
|
||||
* supplying newly arrived data. If the end of the chunked-encoded data is
|
||||
* found, the function returns a non-negative number indicating the number of
|
||||
* octets left undecoded, that starts from the offset returned by `*bufsz`.
|
||||
* Returns -1 on error.
|
||||
*/
|
||||
ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf,
|
||||
size_t *bufsz);
|
||||
|
||||
/* returns if the chunked decoder is in middle of chunked data */
|
||||
int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
#include "freertos/idf_additions.h"
|
||||
#include "freertos/projdefs.h"
|
||||
#include "meshtalos.h"
|
||||
#include "mongoose.h"
|
||||
#include "portmacro.h"
|
||||
#include <esp_log.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <mpack.h>
|
||||
|
||||
struct sub *sub_list = NULL;
|
||||
SemaphoreHandle_t sub_mutex = NULL;
|
||||
QueueHandle_t sub_jobs = NULL;
|
||||
|
||||
static void send_image(struct mg_connection *c) {
|
||||
char mp_buffer[IMAGE_BLK_SIZE + 50];
|
||||
for (int i = 0; i < IMAGE_SIZE; i += IMAGE_BLK_SIZE) {
|
||||
mpack_writer_t wr;
|
||||
mpack_writer_init(&wr, mp_buffer, sizeof(mp_buffer));
|
||||
mpack_build_map(&wr);
|
||||
mpack_write_cstr(&wr, "command");
|
||||
mpack_write_cstr(&wr, "update_image");
|
||||
mpack_write_cstr(&wr, "index");
|
||||
mpack_write_u64(&wr, i);
|
||||
mpack_write_cstr(&wr, "data");
|
||||
mpack_write_bytes(&wr, (const char *)&image_buf[i],
|
||||
MIN(IMAGE_SIZE, IMAGE_SIZE - i));
|
||||
mpack_complete_map(&wr);
|
||||
size_t used = mpack_writer_buffer_used(&wr);
|
||||
if (mpack_writer_destroy(&wr) != mpack_ok) {
|
||||
MG_INFO(("faild build update image mpack"));
|
||||
return;
|
||||
}
|
||||
mg_send(c, mp_buffer, used);
|
||||
}
|
||||
mpack_writer_t wr;
|
||||
mpack_writer_init(&wr, mp_buffer, sizeof(mp_buffer));
|
||||
mpack_build_map(&wr);
|
||||
mpack_write_cstr(&wr, "command");
|
||||
mpack_write_cstr(&wr, "push_image");
|
||||
size_t used = mpack_writer_buffer_used(&wr);
|
||||
if (mpack_writer_destroy(&wr) != mpack_ok) {
|
||||
MG_INFO(("faild build update image mpack"));
|
||||
return;
|
||||
}
|
||||
mg_send(c, mp_buffer, used);
|
||||
}
|
||||
|
||||
static void sfn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_OPEN && c->is_listening == 1) {
|
||||
MG_INFO(("SERVER is listening"));
|
||||
} else if (ev == MG_EV_ACCEPT) {
|
||||
MG_INFO(("SERVER accepted a connection"));
|
||||
struct sub *newsub = calloc(1, sizeof(struct sub));
|
||||
for (struct sub **ptr = &sub_list; *ptr != NULL; ptr = &(*ptr)->next) {
|
||||
if ((*ptr)->next == NULL) {
|
||||
(*ptr)->next = newsub;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (ev == MG_EV_READ) {
|
||||
} else if (ev == MG_EV_CLOSE) {
|
||||
MG_INFO(("SERVER disconnected"));
|
||||
if (sub_list->c == c) {
|
||||
sub_list = sub_list->next;
|
||||
}
|
||||
for (struct sub *ptr = sub_list; ptr != NULL; ptr = ptr->next) {
|
||||
if (ptr->next && ptr->next->c == c) {
|
||||
ptr->next = ptr->next->next;
|
||||
}
|
||||
}
|
||||
} else if (ev == MG_EV_ERROR) {
|
||||
MG_INFO(("SERVER error: %s", (char *)ev_data));
|
||||
} else if (ev == MG_EV_POLL) {
|
||||
struct sync_jobs job;
|
||||
if (xQueueReceive(sub_jobs, &job, (TickType_t)20) == pdPASS) {
|
||||
MG_INFO(("SERVER CLEAN QUEUE "));
|
||||
for (struct sub *child = sub_list; child != NULL; child = child->next) {
|
||||
// if (strncmp(child->topic, job.topic, TOPIC_LEN) == 0) {
|
||||
send_image(child->c);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mtsh_tcp_sender(struct mg_mgr *mgr) {
|
||||
sub_mutex = xSemaphoreCreateMutex();
|
||||
sub_jobs = xQueueCreate(10, sizeof(struct sync_jobs));
|
||||
|
||||
if (sub_mutex == NULL || sub_jobs == NULL) {
|
||||
// errhandle
|
||||
MG_ERROR(("failed to init tcp jobs"));
|
||||
return;
|
||||
}
|
||||
mg_listen(mgr, "tcp://localhost:3030", &sfn, NULL);
|
||||
}
|
||||
2100
main/lib/stb_ds.h
Normal file
2100
main/lib/stb_ds.h
Normal file
File diff suppressed because it is too large
Load diff
483
main/mesh_if.c
483
main/mesh_if.c
|
|
@ -1,483 +0,0 @@
|
|||
/* Mesh IP Internal Communication Example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include "dhcpserver/dhcpserver.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_mac.h"
|
||||
#include "esp_mesh.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_wifi_netif.h"
|
||||
#include "lwip/lwip_napt.h"
|
||||
#include "mesh_netif.h"
|
||||
#include <string.h>
|
||||
|
||||
/*******************************************************
|
||||
* Macros
|
||||
*******************************************************/
|
||||
#define RX_SIZE (1560)
|
||||
|
||||
#if CONFIG_MESH_USE_GLOBAL_DNS_IP
|
||||
#define DNS_IP_ADDR CONFIG_MESH_GLOBAL_DNS_IP
|
||||
#endif
|
||||
|
||||
/*******************************************************
|
||||
* Type Definitions
|
||||
*******************************************************/
|
||||
typedef struct mesh_netif_driver *mesh_netif_driver_t;
|
||||
|
||||
typedef struct mesh_netif_driver {
|
||||
esp_netif_driver_base_t base;
|
||||
uint8_t sta_mac_addr[MAC_ADDR_LEN];
|
||||
} *mesh_netif_driver_t;
|
||||
|
||||
/*******************************************************
|
||||
* Constants
|
||||
*******************************************************/
|
||||
static const char *TAG = "mesh_netif";
|
||||
const esp_netif_ip_info_t g_mesh_netif_subnet_ip = {
|
||||
// mesh subnet IP info
|
||||
.ip = {.addr = ESP_IP4TOADDR(10, 0, 0, 1)},
|
||||
.gw = {.addr = ESP_IP4TOADDR(10, 0, 0, 1)},
|
||||
.netmask = {.addr = ESP_IP4TOADDR(255, 255, 0, 0)},
|
||||
};
|
||||
|
||||
/*******************************************************
|
||||
* Variable Definitions
|
||||
*******************************************************/
|
||||
static esp_netif_t *netif_sta = NULL;
|
||||
static esp_netif_t *netif_ap = NULL;
|
||||
static bool receive_task_is_running = false;
|
||||
static mesh_addr_t s_route_table[CONFIG_MESH_ROUTE_TABLE_SIZE] = {0};
|
||||
static mesh_raw_recv_cb_t *s_mesh_raw_recv_cb = NULL;
|
||||
|
||||
/*******************************************************
|
||||
* Function Definitions
|
||||
*******************************************************/
|
||||
// setup DHCP server's DNS OFFER
|
||||
//
|
||||
static esp_err_t set_dhcps_dns(esp_netif_t *netif, uint32_t addr) {
|
||||
esp_netif_dns_info_t dns;
|
||||
dns.ip.u_addr.ip4.addr = addr;
|
||||
dns.ip.type = IPADDR_TYPE_V4;
|
||||
dhcps_offer_t dhcps_dns_value = OFFER_DNS;
|
||||
ESP_ERROR_CHECK(esp_netif_dhcps_option(
|
||||
netif, ESP_NETIF_OP_SET, ESP_NETIF_DOMAIN_NAME_SERVER, &dhcps_dns_value,
|
||||
sizeof(dhcps_dns_value)));
|
||||
ESP_ERROR_CHECK(esp_netif_set_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns));
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_start(netif));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Receive task
|
||||
//
|
||||
static void receive_task(void *arg) {
|
||||
esp_err_t err;
|
||||
mesh_addr_t from;
|
||||
int flag = 0;
|
||||
mesh_data_t data;
|
||||
static uint8_t rx_buf[RX_SIZE] = {
|
||||
0,
|
||||
};
|
||||
|
||||
ESP_LOGD(TAG, "Receiving task started");
|
||||
while (receive_task_is_running) {
|
||||
data.data = rx_buf;
|
||||
data.size = RX_SIZE;
|
||||
err = esp_mesh_recv(&from, &data, portMAX_DELAY, &flag, NULL, 0);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Received with err code %d %s", err, esp_err_to_name(err));
|
||||
continue;
|
||||
}
|
||||
if (data.proto == MESH_PROTO_BIN && s_mesh_raw_recv_cb) {
|
||||
s_mesh_raw_recv_cb(&from, &data);
|
||||
}
|
||||
if (esp_mesh_is_root()) {
|
||||
if (data.proto == MESH_PROTO_AP) {
|
||||
ESP_LOGD(TAG, "Root received: from: " MACSTR " to " MACSTR " size: %d",
|
||||
MAC2STR((uint8_t *)data.data),
|
||||
MAC2STR((uint8_t *)(data.data + 6)), data.size);
|
||||
if (netif_ap) {
|
||||
// actual receive to TCP/IP stack
|
||||
esp_netif_receive(netif_ap, data.data, data.size, NULL);
|
||||
}
|
||||
} else if (data.proto == MESH_PROTO_STA) {
|
||||
ESP_LOGE(TAG, "Root station Should never receive data from mesh!");
|
||||
}
|
||||
} else {
|
||||
if (data.proto == MESH_PROTO_AP) {
|
||||
ESP_LOGD(TAG, "Node AP should never receive data from mesh");
|
||||
} else if (data.proto == MESH_PROTO_STA) {
|
||||
ESP_LOGD(TAG, "Node received: from: " MACSTR " to " MACSTR " size: %d",
|
||||
MAC2STR((uint8_t *)data.data),
|
||||
MAC2STR((uint8_t *)(data.data + 6)), data.size);
|
||||
if (netif_sta) {
|
||||
// actual receive to TCP/IP stack
|
||||
esp_netif_receive(netif_sta, data.data, data.size, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
// Free RX buffer (not used as the buffer is static)
|
||||
//
|
||||
static void mesh_free(void *h, void *buffer) { free(buffer); }
|
||||
|
||||
// Transmit function variants
|
||||
//
|
||||
static esp_err_t mesh_netif_transmit_from_root_ap(void *h, void *buffer,
|
||||
size_t len) {
|
||||
// Use only to transmit data from root AP to node's AP
|
||||
static const uint8_t eth_broadcast[MAC_ADDR_LEN] = {0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF};
|
||||
int route_table_size = 0;
|
||||
mesh_netif_driver_t mesh_driver = h;
|
||||
mesh_addr_t dest_addr;
|
||||
mesh_data_t data;
|
||||
ESP_LOGD(TAG, "Sending to node: " MACSTR ", size: %d",
|
||||
MAC2STR((uint8_t *)buffer), len);
|
||||
memcpy(dest_addr.addr, buffer, MAC_ADDR_LEN);
|
||||
data.data = buffer;
|
||||
data.size = len;
|
||||
data.proto = MESH_PROTO_STA; // sending from root AP -> Node's STA
|
||||
data.tos = MESH_TOS_P2P;
|
||||
if (MAC_ADDR_EQUAL(dest_addr.addr, eth_broadcast)) {
|
||||
ESP_LOGD(TAG, "Broadcasting!");
|
||||
esp_mesh_get_routing_table((mesh_addr_t *)&s_route_table,
|
||||
CONFIG_MESH_ROUTE_TABLE_SIZE * 6,
|
||||
&route_table_size);
|
||||
for (int i = 0; i < route_table_size; i++) {
|
||||
if (MAC_ADDR_EQUAL(s_route_table[i].addr, mesh_driver->sta_mac_addr)) {
|
||||
ESP_LOGD(TAG, "That was me, skipping!");
|
||||
continue;
|
||||
}
|
||||
ESP_LOGD(TAG, "Broadcast: Sending to [%d] " MACSTR, i,
|
||||
MAC2STR(s_route_table[i].addr));
|
||||
esp_err_t err =
|
||||
esp_mesh_send(&s_route_table[i], &data, MESH_DATA_P2P, NULL, 0);
|
||||
if (ESP_OK != err) {
|
||||
ESP_LOGE(TAG, "Send with err code %d %s", err, esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Standard P2P
|
||||
esp_err_t err = esp_mesh_send(&dest_addr, &data, MESH_DATA_P2P, NULL, 0);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Send with err code %d %s", err, esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
static esp_err_t mesh_netif_transmit_from_root_ap_wrap(void *h, void *buffer,
|
||||
size_t len,
|
||||
void *netstack_buf) {
|
||||
return mesh_netif_transmit_from_root_ap(h, buffer, len);
|
||||
}
|
||||
|
||||
static esp_err_t mesh_netif_transmit_from_node_sta(void *h, void *buffer,
|
||||
size_t len) {
|
||||
mesh_data_t data;
|
||||
ESP_LOGD(TAG, "Sending to root, dest addr: " MACSTR ", size: %d",
|
||||
MAC2STR((uint8_t *)buffer), len);
|
||||
data.data = buffer;
|
||||
data.size = len;
|
||||
data.proto = MESH_PROTO_AP; // Node's station transmits data to root's AP
|
||||
data.tos = MESH_TOS_P2P;
|
||||
esp_err_t err = esp_mesh_send(NULL, &data, MESH_DATA_TODS, NULL, 0);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Send with err code %d %s", err, esp_err_to_name(err));
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t mesh_netif_transmit_from_node_sta_wrap(void *h, void *buffer,
|
||||
size_t len,
|
||||
void *netstack_buf) {
|
||||
return mesh_netif_transmit_from_node_sta(h, buffer, len);
|
||||
}
|
||||
|
||||
// Construct and Destruct functions
|
||||
//
|
||||
static esp_err_t mesh_driver_start_root_ap(esp_netif_t *esp_netif, void *args) {
|
||||
mesh_netif_driver_t driver = args;
|
||||
driver->base.netif = esp_netif;
|
||||
esp_netif_driver_ifconfig_t driver_ifconfig = {
|
||||
.handle = driver,
|
||||
.transmit = mesh_netif_transmit_from_root_ap,
|
||||
.transmit_wrap = mesh_netif_transmit_from_root_ap_wrap,
|
||||
.driver_free_rx_buffer = mesh_free};
|
||||
|
||||
return esp_netif_set_driver_config(esp_netif, &driver_ifconfig);
|
||||
}
|
||||
|
||||
static esp_err_t mesh_driver_start_node_sta(esp_netif_t *esp_netif,
|
||||
void *args) {
|
||||
mesh_netif_driver_t driver = args;
|
||||
driver->base.netif = esp_netif;
|
||||
esp_netif_driver_ifconfig_t driver_ifconfig = {
|
||||
.handle = driver,
|
||||
.transmit = mesh_netif_transmit_from_node_sta,
|
||||
.transmit_wrap = mesh_netif_transmit_from_node_sta_wrap,
|
||||
.driver_free_rx_buffer = mesh_free};
|
||||
|
||||
return esp_netif_set_driver_config(esp_netif, &driver_ifconfig);
|
||||
}
|
||||
|
||||
void mesh_delete_if_driver(mesh_netif_driver_t driver) {
|
||||
// Stop the task once both drivers are removed
|
||||
// receive_task_is_running = true;
|
||||
free(driver);
|
||||
}
|
||||
|
||||
mesh_netif_driver_t mesh_create_if_driver(bool is_ap, bool is_root) {
|
||||
mesh_netif_driver_t driver = calloc(1, sizeof(struct mesh_netif_driver));
|
||||
if (driver == NULL) {
|
||||
ESP_LOGE(TAG, "No memory to create a wifi interface handle");
|
||||
return NULL;
|
||||
}
|
||||
if (is_ap && is_root) {
|
||||
driver->base.post_attach = mesh_driver_start_root_ap;
|
||||
} else if (!is_ap && !is_root) {
|
||||
driver->base.post_attach = mesh_driver_start_node_sta;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!receive_task_is_running) {
|
||||
receive_task_is_running = true;
|
||||
xTaskCreate(receive_task, "netif rx task", 3072, NULL, 5, NULL);
|
||||
}
|
||||
|
||||
// save station mac address to exclude it from routing-table on broadcast
|
||||
esp_wifi_get_mac(WIFI_IF_STA, driver->sta_mac_addr);
|
||||
|
||||
return driver;
|
||||
}
|
||||
|
||||
esp_err_t mesh_netifs_destroy(void) {
|
||||
receive_task_is_running = false;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void mesh_netif_init_station(void) {
|
||||
// By default create a station that would connect to AP (expecting root to
|
||||
// connect to external network)
|
||||
esp_netif_config_t cfg_sta = ESP_NETIF_DEFAULT_WIFI_STA();
|
||||
netif_sta = esp_netif_new(&cfg_sta);
|
||||
assert(netif_sta);
|
||||
ESP_ERROR_CHECK(esp_netif_attach_wifi_station(netif_sta));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_default_wifi_sta_handlers());
|
||||
}
|
||||
|
||||
// Init by default for both potential root and node
|
||||
//
|
||||
esp_err_t mesh_netifs_init(mesh_raw_recv_cb_t *cb) {
|
||||
mesh_netif_init_station();
|
||||
s_mesh_raw_recv_cb = cb;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts AP esp-netif link over mesh (root's AP on mesh)
|
||||
*/
|
||||
static esp_err_t start_mesh_link_ap(void) {
|
||||
uint8_t mac[MAC_ADDR_LEN];
|
||||
esp_wifi_get_mac(WIFI_IF_AP, mac);
|
||||
esp_netif_set_mac(netif_ap, mac);
|
||||
esp_netif_action_start(netif_ap, NULL, 0, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts station link over wifi (root node to the router)
|
||||
*/
|
||||
static esp_err_t start_wifi_link_sta(void) {
|
||||
uint8_t mac[6];
|
||||
esp_wifi_get_mac(WIFI_IF_STA, mac);
|
||||
esp_err_t ret;
|
||||
void *driver = esp_netif_get_io_driver(netif_sta);
|
||||
if ((ret = esp_wifi_register_if_rxcb(driver, esp_netif_receive, netif_sta)) !=
|
||||
ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_wifi_register_if_rxcb for if=%p failed with %d", driver,
|
||||
ret);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_netif_set_mac(netif_sta, mac);
|
||||
esp_netif_action_start(netif_sta, NULL, 0, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts station link over mesh (node to root over mesh)
|
||||
*/
|
||||
static esp_err_t start_mesh_link_sta(void) {
|
||||
uint8_t mac[MAC_ADDR_LEN];
|
||||
esp_wifi_get_mac(WIFI_IF_STA, mac);
|
||||
esp_netif_set_mac(netif_sta, mac);
|
||||
esp_netif_action_start(netif_sta, NULL, 0, NULL);
|
||||
esp_netif_action_connected(netif_sta, NULL, 0, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates esp-netif for AP interface over mesh
|
||||
*
|
||||
* @return Pointer to esp-netif instance
|
||||
*/
|
||||
static esp_netif_t *create_mesh_link_ap(void) {
|
||||
esp_netif_inherent_config_t base_cfg = ESP_NETIF_INHERENT_DEFAULT_WIFI_AP();
|
||||
base_cfg.if_desc = "mesh_link_ap";
|
||||
base_cfg.ip_info = &g_mesh_netif_subnet_ip;
|
||||
|
||||
esp_netif_config_t cfg = {.base = &base_cfg,
|
||||
.driver = NULL,
|
||||
.stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_AP};
|
||||
esp_netif_t *netif = esp_netif_new(&cfg);
|
||||
assert(netif);
|
||||
return netif;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroy esp-netif for AP interface over mesh
|
||||
*/
|
||||
static void destory_mesh_link_ap(void) {
|
||||
if (netif_ap) {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_stop(netif_ap));
|
||||
esp_netif_action_disconnected(netif_ap, NULL, 0, NULL);
|
||||
mesh_delete_if_driver(esp_netif_get_io_driver(netif_ap));
|
||||
esp_netif_destroy(netif_ap);
|
||||
netif_ap = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates esp-netif for station interface over mesh
|
||||
*
|
||||
* @note Interface needs to be started (later) using the above APIs
|
||||
* based on the actual configuration root/node,
|
||||
* since root connects normally over wifi
|
||||
*
|
||||
* @return Pointer to esp-netif instance
|
||||
*/
|
||||
static esp_netif_t *create_mesh_link_sta(void) {
|
||||
esp_netif_inherent_config_t base_cfg = ESP_NETIF_INHERENT_DEFAULT_WIFI_STA();
|
||||
base_cfg.if_desc = "mesh_link_sta";
|
||||
|
||||
esp_netif_config_t cfg = {.base = &base_cfg,
|
||||
.driver = NULL,
|
||||
.stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_STA};
|
||||
esp_netif_t *netif = esp_netif_new(&cfg);
|
||||
assert(netif);
|
||||
return netif;
|
||||
}
|
||||
|
||||
esp_err_t mesh_netif_start_root_ap(bool is_root, uint32_t addr) {
|
||||
if (is_root) {
|
||||
destory_mesh_link_ap();
|
||||
netif_ap = create_mesh_link_ap();
|
||||
mesh_netif_driver_t driver = mesh_create_if_driver(true, true);
|
||||
if (driver == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to create wifi interface handle");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_netif_attach(netif_ap, driver);
|
||||
set_dhcps_dns(netif_ap, addr);
|
||||
start_mesh_link_ap();
|
||||
ip_napt_enable(g_mesh_netif_subnet_ip.ip.addr, 1);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mesh_netifs_start(bool is_root) {
|
||||
if (is_root) {
|
||||
// ROOT: need both sta should use standard wifi, AP mesh link netif
|
||||
|
||||
// Root: Station
|
||||
if (netif_sta && strcmp(esp_netif_get_desc(netif_sta), "sta") == 0) {
|
||||
ESP_LOGI(TAG, "Already wifi station, no need to do anything");
|
||||
} else if (netif_sta &&
|
||||
strcmp(esp_netif_get_desc(netif_sta), "mesh_link_sta") == 0) {
|
||||
esp_netif_action_disconnected(netif_sta, NULL, 0, NULL);
|
||||
mesh_delete_if_driver(esp_netif_get_io_driver(netif_sta));
|
||||
esp_netif_destroy(netif_sta);
|
||||
mesh_netif_init_station();
|
||||
} else if (netif_sta == NULL) {
|
||||
mesh_netif_init_station();
|
||||
}
|
||||
|
||||
// Root: AP is initialized only if GLOBAL DNS configured
|
||||
// (otherwise have to wait until the actual DNS record received from the
|
||||
// router)
|
||||
#if CONFIG_MESH_USE_GLOBAL_DNS_IP
|
||||
mesh_netif_start_root_ap(true, htonl(DNS_IP_ADDR));
|
||||
#endif
|
||||
|
||||
} else {
|
||||
// NODE: create only STA in form of mesh link
|
||||
if (netif_sta &&
|
||||
strcmp(esp_netif_get_desc(netif_sta), "mesh_link_sta") == 0) {
|
||||
ESP_LOGI(TAG, "Already mesh link station, no need to do anything");
|
||||
return ESP_OK;
|
||||
}
|
||||
if (netif_sta) {
|
||||
esp_netif_action_disconnected(netif_sta, NULL, 0, NULL);
|
||||
// should remove the actual driver
|
||||
if (strcmp(esp_netif_get_desc(netif_sta), "sta") == 0) {
|
||||
ESP_LOGI(TAG, "It was a wifi station removing stuff");
|
||||
esp_wifi_clear_default_wifi_driver_and_handlers(netif_sta);
|
||||
}
|
||||
esp_netif_destroy(netif_sta);
|
||||
}
|
||||
netif_sta = create_mesh_link_sta();
|
||||
// now we create a mesh driver and attach it to the existing netif
|
||||
mesh_netif_driver_t driver = mesh_create_if_driver(false, false);
|
||||
if (driver == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to create wifi interface handle");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_netif_attach(netif_sta, driver);
|
||||
start_mesh_link_sta();
|
||||
// If we have a AP on NODE -> stop and remove it!
|
||||
destory_mesh_link_ap();
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mesh_netifs_stop(void) {
|
||||
if (netif_sta && strcmp(esp_netif_get_desc(netif_sta), "sta") == 0 &&
|
||||
netif_ap == NULL) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (netif_sta) {
|
||||
if (strcmp(esp_netif_get_desc(netif_sta), "sta") == 0) {
|
||||
esp_netif_action_disconnected(netif_sta, NULL, 0, NULL);
|
||||
esp_netif_action_stop(netif_sta, NULL, 0, NULL);
|
||||
esp_wifi_clear_default_wifi_driver_and_handlers(netif_sta);
|
||||
} else {
|
||||
esp_netif_action_disconnected(netif_sta, NULL, 0, NULL);
|
||||
mesh_delete_if_driver(esp_netif_get_io_driver(netif_sta));
|
||||
}
|
||||
esp_netif_destroy(netif_sta);
|
||||
netif_sta = NULL;
|
||||
}
|
||||
|
||||
destory_mesh_link_ap();
|
||||
// reserve the default (STA gets ready to become root)
|
||||
mesh_netif_init_station();
|
||||
start_wifi_link_sta();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint8_t *mesh_netif_get_station_mac(void) {
|
||||
mesh_netif_driver_t mesh = esp_netif_get_io_driver(netif_sta);
|
||||
return mesh->sta_mac_addr;
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
/* Mesh IP Internal Networking Example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#pragma once
|
||||
#include "esp_mesh.h"
|
||||
|
||||
/*******************************************************
|
||||
* Macros
|
||||
*******************************************************/
|
||||
#define MAC_ADDR_LEN (6u)
|
||||
#define MAC_ADDR_EQUAL(a, b) (0 == memcmp(a, b, MAC_ADDR_LEN))
|
||||
|
||||
/*******************************************************
|
||||
* Type Definitions
|
||||
*******************************************************/
|
||||
typedef void(mesh_raw_recv_cb_t)(mesh_addr_t *from, mesh_data_t *data);
|
||||
|
||||
/*******************************************************
|
||||
* Function Declarations
|
||||
*******************************************************/
|
||||
|
||||
/**
|
||||
* @brief Initializes netifs in a default way before knowing if we are going to
|
||||
* be a root
|
||||
*
|
||||
* @param cb callback receive function for mesh raw packets
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
*/
|
||||
esp_err_t mesh_netifs_init(mesh_raw_recv_cb_t *cb);
|
||||
|
||||
/**
|
||||
* @brief Destroy the netifs and related structures
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
*/
|
||||
esp_err_t mesh_netifs_destroy(void);
|
||||
|
||||
/**
|
||||
* @brief Start the mesh netifs based on the configuration (root/node)
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
*/
|
||||
esp_err_t mesh_netifs_start(bool is_root);
|
||||
|
||||
/**
|
||||
* @brief Stop the netifs and reset to the default mode
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
*/
|
||||
esp_err_t mesh_netifs_stop(void);
|
||||
|
||||
/**
|
||||
* @brief Start the netif for root AP
|
||||
*
|
||||
* Note: The AP netif needs to be started separately after root received
|
||||
* an IP address from the router so the DNS address could be used for dhcps
|
||||
*
|
||||
* @param is_root must be true, ignored otherwise
|
||||
* @param dns_addr DNS address to use in DHCP server running on roots AP
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
*/
|
||||
esp_err_t mesh_netif_start_root_ap(bool is_root, uint32_t dns_addr);
|
||||
|
||||
/**
|
||||
* @brief Returns MAC address of the station interface
|
||||
*
|
||||
* Used mainly for checking node addresses of the peers in routing table
|
||||
* to avoid sending data to oneself
|
||||
*
|
||||
* @return Pointer to MAC address
|
||||
*/
|
||||
uint8_t *mesh_netif_get_station_mac(void);
|
||||
514
main/network.c
514
main/network.c
|
|
@ -1,4 +1,9 @@
|
|||
/* Mesh Internal Communication Example
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
/* WiFi softAP & station Example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
|
|
@ -6,330 +11,211 @@
|
|||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include "esp_err.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_mac.h"
|
||||
#include "esp_mesh.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_netif_net_stack.h"
|
||||
#include "esp_netif_types.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_wifi_types_generic.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "mesh_netif.h"
|
||||
#include "lwip/inet.h"
|
||||
#include "lwip/ip4_addr.h"
|
||||
#include "lwip/netdb.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "nvs_flash.h"
|
||||
#include <inttypes.h>
|
||||
#include <network.h>
|
||||
#include <string.h>
|
||||
|
||||
/*******************************************************
|
||||
* Macros
|
||||
*******************************************************/
|
||||
|
||||
// commands for internal mesh communication:
|
||||
// <CMD> <PAYLOAD>, where CMD is one character, payload is variable dep. on
|
||||
// command
|
||||
#define CMD_KEYPRESSED 0x55
|
||||
// CMD_KEYPRESSED: payload is always 6 bytes identifying address of node sending
|
||||
// keypress event
|
||||
#define CMD_ROUTE_TABLE 0x56
|
||||
// CMD_KEYPRESSED: payload is a multiple of 6 listing addresses in a routing
|
||||
// table
|
||||
/*******************************************************
|
||||
* Constants
|
||||
*******************************************************/
|
||||
static const char *MESH_TAG = "mesh_main";
|
||||
static const uint8_t MESH_ID[6] = {0x77, 0x77, 0x77, 0x77, 0x77, 0x76};
|
||||
|
||||
/*******************************************************
|
||||
* Variable Definitions
|
||||
*******************************************************/
|
||||
static mesh_addr_t mesh_parent_addr;
|
||||
static int mesh_layer = -1;
|
||||
static esp_ip4_addr_t s_current_ip;
|
||||
static mesh_addr_t s_route_table[CONFIG_MESH_ROUTE_TABLE_SIZE];
|
||||
static int s_route_table_size = 0;
|
||||
static SemaphoreHandle_t s_route_table_lock = NULL;
|
||||
// static uint8_t s_mesh_tx_payload[CONFIG_MESH_ROUTE_TABLE_SIZE * 6 + 1];
|
||||
|
||||
/*******************************************************
|
||||
* Function Definitions
|
||||
*******************************************************/
|
||||
|
||||
void static recv_cb(mesh_addr_t *from, mesh_data_t *data) {
|
||||
if (data->data[0] == CMD_ROUTE_TABLE) {
|
||||
int size = data->size - 1;
|
||||
if (s_route_table_lock == NULL || size % 6 != 0) {
|
||||
ESP_LOGE(MESH_TAG, "Error in receiving raw mesh data: Unexpected size");
|
||||
return;
|
||||
}
|
||||
xSemaphoreTake(s_route_table_lock, portMAX_DELAY);
|
||||
s_route_table_size = size / 6;
|
||||
for (int i = 0; i < s_route_table_size; ++i) {
|
||||
ESP_LOGI(MESH_TAG, "Received Routing table [%d] " MACSTR, i,
|
||||
MAC2STR(data->data + 6 * i + 1));
|
||||
}
|
||||
memcpy(&s_route_table, data->data + 1, size);
|
||||
xSemaphoreGive(s_route_table_lock);
|
||||
} else if (data->data[0] == CMD_KEYPRESSED) {
|
||||
if (data->size != 7) {
|
||||
ESP_LOGE(MESH_TAG, "Error in receiving raw mesh data: Unexpected size");
|
||||
return;
|
||||
}
|
||||
ESP_LOGW(MESH_TAG, "Keypressed detected on node: " MACSTR,
|
||||
MAC2STR(data->data + 1));
|
||||
} else {
|
||||
ESP_LOGE(MESH_TAG, "Error in receiving raw mesh data: Unknown command");
|
||||
}
|
||||
}
|
||||
|
||||
void mesh_event_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data) {
|
||||
mesh_addr_t id = {
|
||||
0,
|
||||
};
|
||||
static uint8_t last_layer = 0;
|
||||
|
||||
switch (event_id) {
|
||||
case MESH_EVENT_STARTED: {
|
||||
esp_mesh_get_id(&id);
|
||||
ESP_LOGI(MESH_TAG, "<MESH_EVENT_MESH_STARTED>ID:" MACSTR "",
|
||||
MAC2STR(id.addr));
|
||||
mesh_layer = esp_mesh_get_layer();
|
||||
} break;
|
||||
case MESH_EVENT_STOPPED: {
|
||||
ESP_LOGI(MESH_TAG, "<MESH_EVENT_STOPPED>");
|
||||
mesh_layer = esp_mesh_get_layer();
|
||||
} break;
|
||||
case MESH_EVENT_CHILD_CONNECTED: {
|
||||
mesh_event_child_connected_t *child_connected =
|
||||
(mesh_event_child_connected_t *)event_data;
|
||||
ESP_LOGI(MESH_TAG, "<MESH_EVENT_CHILD_CONNECTED>aid:%d, " MACSTR "",
|
||||
child_connected->aid, MAC2STR(child_connected->mac));
|
||||
} break;
|
||||
case MESH_EVENT_CHILD_DISCONNECTED: {
|
||||
mesh_event_child_disconnected_t *child_disconnected =
|
||||
(mesh_event_child_disconnected_t *)event_data;
|
||||
ESP_LOGI(MESH_TAG, "<MESH_EVENT_CHILD_DISCONNECTED>aid:%d, " MACSTR "",
|
||||
child_disconnected->aid, MAC2STR(child_disconnected->mac));
|
||||
} break;
|
||||
case MESH_EVENT_ROUTING_TABLE_ADD: {
|
||||
mesh_event_routing_table_change_t *routing_table =
|
||||
(mesh_event_routing_table_change_t *)event_data;
|
||||
ESP_LOGW(MESH_TAG, "<MESH_EVENT_ROUTING_TABLE_ADD>add %d, new:%d",
|
||||
routing_table->rt_size_change, routing_table->rt_size_new);
|
||||
} break;
|
||||
case MESH_EVENT_ROUTING_TABLE_REMOVE: {
|
||||
mesh_event_routing_table_change_t *routing_table =
|
||||
(mesh_event_routing_table_change_t *)event_data;
|
||||
ESP_LOGW(MESH_TAG, "<MESH_EVENT_ROUTING_TABLE_REMOVE>remove %d, new:%d",
|
||||
routing_table->rt_size_change, routing_table->rt_size_new);
|
||||
} break;
|
||||
case MESH_EVENT_NO_PARENT_FOUND: {
|
||||
mesh_event_no_parent_found_t *no_parent =
|
||||
(mesh_event_no_parent_found_t *)event_data;
|
||||
ESP_LOGI(MESH_TAG, "<MESH_EVENT_NO_PARENT_FOUND>scan times:%d",
|
||||
no_parent->scan_times);
|
||||
}
|
||||
/* TODO handler for the failure */
|
||||
break;
|
||||
case MESH_EVENT_PARENT_CONNECTED: {
|
||||
mesh_event_connected_t *connected = (mesh_event_connected_t *)event_data;
|
||||
esp_mesh_get_id(&id);
|
||||
mesh_layer = connected->self_layer;
|
||||
memcpy(&mesh_parent_addr.addr, connected->connected.bssid, 6);
|
||||
ESP_LOGI(MESH_TAG,
|
||||
"<MESH_EVENT_PARENT_CONNECTED>layer:%d-->%d, parent:" MACSTR
|
||||
"%s, ID:" MACSTR "",
|
||||
last_layer, mesh_layer, MAC2STR(mesh_parent_addr.addr),
|
||||
esp_mesh_is_root() ? "<ROOT>"
|
||||
: (mesh_layer == 2) ? "<layer2>"
|
||||
: "",
|
||||
MAC2STR(id.addr));
|
||||
last_layer = mesh_layer;
|
||||
mesh_netifs_start(esp_mesh_is_root());
|
||||
} break;
|
||||
case MESH_EVENT_PARENT_DISCONNECTED: {
|
||||
mesh_event_disconnected_t *disconnected =
|
||||
(mesh_event_disconnected_t *)event_data;
|
||||
ESP_LOGI(MESH_TAG, "<MESH_EVENT_PARENT_DISCONNECTED>reason:%d",
|
||||
disconnected->reason);
|
||||
mesh_layer = esp_mesh_get_layer();
|
||||
mesh_netifs_stop();
|
||||
} break;
|
||||
case MESH_EVENT_LAYER_CHANGE: {
|
||||
mesh_event_layer_change_t *layer_change =
|
||||
(mesh_event_layer_change_t *)event_data;
|
||||
mesh_layer = layer_change->new_layer;
|
||||
ESP_LOGI(MESH_TAG, "<MESH_EVENT_LAYER_CHANGE>layer:%d-->%d%s", last_layer,
|
||||
mesh_layer,
|
||||
esp_mesh_is_root() ? "<ROOT>"
|
||||
: (mesh_layer == 2) ? "<layer2>"
|
||||
: "");
|
||||
last_layer = mesh_layer;
|
||||
} break;
|
||||
case MESH_EVENT_ROOT_ADDRESS: {
|
||||
mesh_event_root_address_t *root_addr =
|
||||
(mesh_event_root_address_t *)event_data;
|
||||
ESP_LOGI(MESH_TAG, "<MESH_EVENT_ROOT_ADDRESS>root address:" MACSTR "",
|
||||
MAC2STR(root_addr->addr));
|
||||
} break;
|
||||
case MESH_EVENT_VOTE_STARTED: {
|
||||
mesh_event_vote_started_t *vote_started =
|
||||
(mesh_event_vote_started_t *)event_data;
|
||||
ESP_LOGI(MESH_TAG,
|
||||
"<MESH_EVENT_VOTE_STARTED>attempts:%d, reason:%d, rc_addr:" MACSTR
|
||||
"",
|
||||
vote_started->attempts, vote_started->reason,
|
||||
MAC2STR(vote_started->rc_addr.addr));
|
||||
} break;
|
||||
case MESH_EVENT_VOTE_STOPPED: {
|
||||
ESP_LOGI(MESH_TAG, "<MESH_EVENT_VOTE_STOPPED>");
|
||||
break;
|
||||
}
|
||||
case MESH_EVENT_ROOT_SWITCH_REQ: {
|
||||
mesh_event_root_switch_req_t *switch_req =
|
||||
(mesh_event_root_switch_req_t *)event_data;
|
||||
ESP_LOGI(MESH_TAG,
|
||||
"<MESH_EVENT_ROOT_SWITCH_REQ>reason:%d, rc_addr:" MACSTR "",
|
||||
switch_req->reason, MAC2STR(switch_req->rc_addr.addr));
|
||||
} break;
|
||||
case MESH_EVENT_ROOT_SWITCH_ACK: {
|
||||
/* new root */
|
||||
mesh_layer = esp_mesh_get_layer();
|
||||
esp_mesh_get_parent_bssid(&mesh_parent_addr);
|
||||
ESP_LOGI(MESH_TAG,
|
||||
"<MESH_EVENT_ROOT_SWITCH_ACK>layer:%d, parent:" MACSTR "",
|
||||
mesh_layer, MAC2STR(mesh_parent_addr.addr));
|
||||
} break;
|
||||
case MESH_EVENT_TODS_STATE: {
|
||||
mesh_event_toDS_state_t *toDs_state = (mesh_event_toDS_state_t *)event_data;
|
||||
ESP_LOGI(MESH_TAG, "<MESH_EVENT_TODS_REACHABLE>state:%d", *toDs_state);
|
||||
} break;
|
||||
case MESH_EVENT_ROOT_FIXED: {
|
||||
mesh_event_root_fixed_t *root_fixed = (mesh_event_root_fixed_t *)event_data;
|
||||
ESP_LOGI(MESH_TAG, "<MESH_EVENT_ROOT_FIXED>%s",
|
||||
root_fixed->is_fixed ? "fixed" : "not fixed");
|
||||
} break;
|
||||
case MESH_EVENT_ROOT_ASKED_YIELD: {
|
||||
mesh_event_root_conflict_t *root_conflict =
|
||||
(mesh_event_root_conflict_t *)event_data;
|
||||
ESP_LOGI(MESH_TAG,
|
||||
"<MESH_EVENT_ROOT_ASKED_YIELD>" MACSTR ", rssi:%d, capacity:%d",
|
||||
MAC2STR(root_conflict->addr), root_conflict->rssi,
|
||||
root_conflict->capacity);
|
||||
} break;
|
||||
case MESH_EVENT_CHANNEL_SWITCH: {
|
||||
mesh_event_channel_switch_t *channel_switch =
|
||||
(mesh_event_channel_switch_t *)event_data;
|
||||
ESP_LOGI(MESH_TAG, "<MESH_EVENT_CHANNEL_SWITCH>new channel:%d",
|
||||
channel_switch->channel);
|
||||
} break;
|
||||
case MESH_EVENT_SCAN_DONE: {
|
||||
mesh_event_scan_done_t *scan_done = (mesh_event_scan_done_t *)event_data;
|
||||
ESP_LOGI(MESH_TAG, "<MESH_EVENT_SCAN_DONE>number:%d", scan_done->number);
|
||||
} break;
|
||||
case MESH_EVENT_NETWORK_STATE: {
|
||||
mesh_event_network_state_t *network_state =
|
||||
(mesh_event_network_state_t *)event_data;
|
||||
ESP_LOGI(MESH_TAG, "<MESH_EVENT_NETWORK_STATE>is_rootless:%d",
|
||||
network_state->is_rootless);
|
||||
} break;
|
||||
case MESH_EVENT_STOP_RECONNECTION: {
|
||||
ESP_LOGI(MESH_TAG, "<MESH_EVENT_STOP_RECONNECTION>");
|
||||
} break;
|
||||
case MESH_EVENT_FIND_NETWORK: {
|
||||
mesh_event_find_network_t *find_network =
|
||||
(mesh_event_find_network_t *)event_data;
|
||||
ESP_LOGI(MESH_TAG,
|
||||
"<MESH_EVENT_FIND_NETWORK>new channel:%d, router BSSID:" MACSTR "",
|
||||
find_network->channel, MAC2STR(find_network->router_bssid));
|
||||
} break;
|
||||
case MESH_EVENT_ROUTER_SWITCH: {
|
||||
mesh_event_router_switch_t *router_switch =
|
||||
(mesh_event_router_switch_t *)event_data;
|
||||
ESP_LOGI(MESH_TAG,
|
||||
"<MESH_EVENT_ROUTER_SWITCH>new router:%s, channel:%d, " MACSTR "",
|
||||
router_switch->ssid, router_switch->channel,
|
||||
MAC2STR(router_switch->bssid));
|
||||
} break;
|
||||
default:
|
||||
ESP_LOGI(MESH_TAG, "unknown id:%" PRId32 "", event_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id,
|
||||
void *event_data) {
|
||||
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
|
||||
ESP_LOGI(MESH_TAG, "<IP_EVENT_STA_GOT_IP>IP:" IPSTR,
|
||||
IP2STR(&event->ip_info.ip));
|
||||
s_current_ip.addr = event->ip_info.ip.addr;
|
||||
#if !CONFIG_MESH_USE_GLOBAL_DNS_IP
|
||||
esp_netif_t *netif = event->esp_netif;
|
||||
esp_netif_dns_info_t dns;
|
||||
ESP_ERROR_CHECK(esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns));
|
||||
mesh_netif_start_root_ap(esp_mesh_is_root(), dns.ip.u_addr.ip4.addr);
|
||||
#if IP_NAPT
|
||||
#include "lwip/lwip_napt.h"
|
||||
#endif
|
||||
// esp_mesh_comm_mqtt_task_start();
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sys.h"
|
||||
|
||||
static const char *TAG_AP = "WiFi SoftAP";
|
||||
static const char *TAG_STA = "WiFi Sta";
|
||||
|
||||
static int s_retry_num = 0;
|
||||
|
||||
/* FreeRTOS event group to signal when we are connected/disconnected */
|
||||
EventGroupHandle_t s_wifi_event_group;
|
||||
|
||||
static void wifi_event_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data) {
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STACONNECTED) {
|
||||
wifi_event_ap_staconnected_t *event =
|
||||
(wifi_event_ap_staconnected_t *)event_data;
|
||||
ESP_LOGI(TAG_AP, "Station " MACSTR " joined, AID=%d", MAC2STR(event->mac),
|
||||
event->aid);
|
||||
} else if (event_base == WIFI_EVENT &&
|
||||
event_id == WIFI_EVENT_AP_STADISCONNECTED) {
|
||||
wifi_event_ap_stadisconnected_t *event =
|
||||
(wifi_event_ap_stadisconnected_t *)event_data;
|
||||
ESP_LOGI(TAG_AP, "Station " MACSTR " left, AID=%d, reason:%d",
|
||||
MAC2STR(event->mac), event->aid, event->reason);
|
||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||
esp_wifi_connect();
|
||||
ESP_LOGI(TAG_STA, "Station started");
|
||||
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
|
||||
ESP_LOGI(TAG_STA, "Got IP:" IPSTR, IP2STR(&event->ip_info.ip));
|
||||
s_retry_num = 0;
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize soft AP */
|
||||
esp_netif_t *wifi_init_softap(void) {
|
||||
esp_netif_t *esp_netif_ap = esp_netif_create_default_wifi_ap();
|
||||
|
||||
wifi_config_t wifi_ap_config = {
|
||||
.ap =
|
||||
{
|
||||
.ssid = EXAMPLE_ESP_WIFI_AP_SSID,
|
||||
.ssid_len = strlen(EXAMPLE_ESP_WIFI_AP_SSID),
|
||||
.channel = EXAMPLE_ESP_WIFI_CHANNEL,
|
||||
.password = EXAMPLE_ESP_WIFI_AP_PASSWD,
|
||||
.max_connection = EXAMPLE_MAX_STA_CONN,
|
||||
.authmode = WIFI_AUTH_WPA2_PSK,
|
||||
.pmf_cfg =
|
||||
{
|
||||
.required = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (strlen(EXAMPLE_ESP_WIFI_AP_PASSWD) == 0) {
|
||||
wifi_ap_config.ap.authmode = WIFI_AUTH_OPEN;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_ap_config));
|
||||
|
||||
esp_netif_dhcps_stop(esp_netif_ap);
|
||||
|
||||
ESP_LOGI(TAG_AP, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
|
||||
EXAMPLE_ESP_WIFI_AP_SSID, EXAMPLE_ESP_WIFI_AP_PASSWD,
|
||||
EXAMPLE_ESP_WIFI_CHANNEL);
|
||||
|
||||
esp_netif_ip_info_t ipinfo;
|
||||
IP4_ADDR(&ipinfo.ip, 172, 16, 0, 1);
|
||||
IP4_ADDR(&ipinfo.gw, 172, 16, 0, 1);
|
||||
IP4_ADDR(&ipinfo.netmask, 255, 255, 255, 0);
|
||||
esp_netif_set_ip_info(esp_netif_ap, &ipinfo);
|
||||
esp_netif_dhcps_start(esp_netif_ap);
|
||||
|
||||
return esp_netif_ap;
|
||||
}
|
||||
|
||||
/* Initialize wifi station */
|
||||
esp_netif_t *wifi_init_sta(void) {
|
||||
esp_netif_t *esp_netif_sta = esp_netif_create_default_wifi_sta();
|
||||
|
||||
wifi_config_t wifi_sta_config = {
|
||||
.sta =
|
||||
{
|
||||
.ssid = EXAMPLE_ESP_WIFI_STA_SSID,
|
||||
.password = EXAMPLE_ESP_WIFI_STA_PASSWD,
|
||||
.scan_method = WIFI_ALL_CHANNEL_SCAN,
|
||||
.failure_retry_cnt = EXAMPLE_ESP_MAXIMUM_RETRY,
|
||||
/* Authmode threshold resets to WPA2 as default if password
|
||||
* matches WPA2 standards (password len => 8). If you want to
|
||||
* connect the device to deprecated WEP/WPA networks, Please set
|
||||
* the threshold value to WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK and set
|
||||
* the password with length and format matching to
|
||||
* WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK standards.
|
||||
*/
|
||||
.threshold.authmode = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD,
|
||||
.sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
|
||||
},
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_sta_config));
|
||||
|
||||
ESP_LOGI(TAG_STA, "wifi_init_sta finished.");
|
||||
|
||||
return esp_netif_sta;
|
||||
}
|
||||
|
||||
void softap_set_dns_addr(esp_netif_t *esp_netif_ap,
|
||||
esp_netif_t *esp_netif_sta) {
|
||||
esp_netif_dns_info_t dns;
|
||||
esp_netif_get_dns_info(esp_netif_sta, ESP_NETIF_DNS_MAIN, &dns);
|
||||
uint8_t dhcps_offer_option = DHCPS_OFFER_DNS;
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_stop(esp_netif_ap));
|
||||
ESP_ERROR_CHECK(esp_netif_dhcps_option(
|
||||
esp_netif_ap, ESP_NETIF_OP_SET, ESP_NETIF_DOMAIN_NAME_SERVER,
|
||||
&dhcps_offer_option, sizeof(dhcps_offer_option)));
|
||||
ESP_ERROR_CHECK(
|
||||
esp_netif_set_dns_info(esp_netif_ap, ESP_NETIF_DNS_MAIN, &dns));
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_start(esp_netif_ap));
|
||||
}
|
||||
|
||||
void netif_start(void) {
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
/* tcpip initialization */
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
/* event initialization */
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
/* crete network interfaces for mesh (only station instance saved for further
|
||||
* manipulation, soft AP instance ignored */
|
||||
ESP_ERROR_CHECK(mesh_netifs_init(recv_cb));
|
||||
|
||||
/* wifi initialization */
|
||||
wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&config));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
|
||||
&ip_event_handler, NULL));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_FLASH));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
/* mesh initialization */
|
||||
ESP_ERROR_CHECK(esp_mesh_init());
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(MESH_EVENT, ESP_EVENT_ANY_ID,
|
||||
&mesh_event_handler, NULL));
|
||||
ESP_ERROR_CHECK(esp_mesh_set_max_layer(CONFIG_MESH_MAX_LAYER));
|
||||
ESP_ERROR_CHECK(esp_mesh_set_vote_percentage(1));
|
||||
ESP_ERROR_CHECK(esp_mesh_set_ap_assoc_expire(10));
|
||||
/* set blocking time of esp_mesh_send() to 30s, to prevent the esp_mesh_send()
|
||||
* from permanently for some reason */
|
||||
ESP_ERROR_CHECK(esp_mesh_send_block_time(30000));
|
||||
mesh_cfg_t cfg = MESH_INIT_CONFIG_DEFAULT();
|
||||
#if !MESH_IE_ENCRYPTED
|
||||
cfg.crypto_funcs = NULL;
|
||||
#endif
|
||||
/* mesh ID */
|
||||
memcpy((uint8_t *)&cfg.mesh_id, MESH_ID, 6);
|
||||
/* router */
|
||||
cfg.channel = CONFIG_MESH_CHANNEL;
|
||||
cfg.router.ssid_len = strlen(CONFIG_MESH_ROUTER_SSID);
|
||||
memcpy((uint8_t *)&cfg.router.ssid, CONFIG_MESH_ROUTER_SSID,
|
||||
cfg.router.ssid_len);
|
||||
memcpy((uint8_t *)&cfg.router.password, CONFIG_MESH_ROUTER_PASSWD,
|
||||
strlen(CONFIG_MESH_ROUTER_PASSWD));
|
||||
/* mesh softAP */
|
||||
ESP_ERROR_CHECK(esp_mesh_set_ap_authmode(CONFIG_MESH_AP_AUTHMODE));
|
||||
cfg.mesh_ap.max_connection = CONFIG_MESH_AP_CONNECTIONS;
|
||||
cfg.mesh_ap.nonmesh_max_connection = CONFIG_MESH_NON_MESH_AP_CONNECTIONS;
|
||||
memcpy((uint8_t *)&cfg.mesh_ap.password, CONFIG_MESH_AP_PASSWD,
|
||||
strlen(CONFIG_MESH_AP_PASSWD));
|
||||
ESP_ERROR_CHECK(esp_mesh_set_config(&cfg));
|
||||
// fixed root node
|
||||
// wifi_config_t wifi_cfg;
|
||||
|
||||
// ESP_ERROR_CHECK(esp_wifi_get_config(WIFI_IF_AP, &wifi_cfg));
|
||||
// ESP_ERROR_CHECK(
|
||||
// esp_mesh_set_parent(&wifi_cfg, &cfg.mesh_id, MESH_ROOT,
|
||||
// MESH_ROOT_LAYER));
|
||||
//
|
||||
/* mesh start */
|
||||
ESP_ERROR_CHECK(esp_mesh_start());
|
||||
ESP_LOGI(MESH_TAG, "mesh starts successfully, heap:%" PRId32 ", %s",
|
||||
esp_get_free_heap_size(),
|
||||
esp_mesh_is_root_fixed() ? "root fixed" : "root not fixed");
|
||||
// Initialize NVS
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
|
||||
ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
/* Initialize event group */
|
||||
s_wifi_event_group = xEventGroupCreate();
|
||||
|
||||
/* Register Event handler */
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(
|
||||
WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(
|
||||
IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, NULL));
|
||||
|
||||
/*Initialize WiFi */
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));
|
||||
|
||||
/* Initialize AP */
|
||||
ESP_LOGI(TAG_AP, "ESP_WIFI_MODE_AP");
|
||||
esp_netif_t *esp_netif_ap = wifi_init_softap();
|
||||
|
||||
/* Initialize STA */
|
||||
ESP_LOGI(TAG_STA, "ESP_WIFI_MODE_STA");
|
||||
esp_netif_t *esp_netif_sta = wifi_init_sta();
|
||||
|
||||
/* Start WiFi */
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
|
||||
/*
|
||||
* Wait until either the connection is established (WIFI_CONNECTED_BIT) or
|
||||
* connection failed for the maximum number of re-tries (WIFI_FAIL_BIT).
|
||||
* The bits are set by event_handler() (see above)
|
||||
*/
|
||||
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
|
||||
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
|
||||
pdFALSE, pdFALSE, portMAX_DELAY);
|
||||
|
||||
/* xEventGroupWaitBits() returns the bits before the call returned,
|
||||
* hence we can test which event actually happened. */
|
||||
if (bits & WIFI_CONNECTED_BIT) {
|
||||
ESP_LOGI(TAG_STA, "connected to ap SSID:%s password:%s",
|
||||
EXAMPLE_ESP_WIFI_STA_SSID, EXAMPLE_ESP_WIFI_STA_PASSWD);
|
||||
softap_set_dns_addr(esp_netif_ap, esp_netif_sta);
|
||||
} else if (bits & WIFI_FAIL_BIT) {
|
||||
ESP_LOGI(TAG_STA, "Failed to connect to SSID:%s, password:%s",
|
||||
EXAMPLE_ESP_WIFI_STA_SSID, EXAMPLE_ESP_WIFI_STA_PASSWD);
|
||||
} else {
|
||||
ESP_LOGE(TAG_STA, "UNEXPECTED EVENT");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set sta as the default interface */
|
||||
esp_netif_set_default_netif(esp_netif_sta);
|
||||
|
||||
/* Enable napt on the AP netif */
|
||||
if (esp_netif_napt_enable(esp_netif_ap) != ESP_OK) {
|
||||
ESP_LOGE(TAG_STA, "NAPT not enabled on the netif: %p", esp_netif_ap);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
/* The examples use WiFi configuration that you can set via project
|
||||
configuration menu.
|
||||
|
||||
If you'd rather not, just change the below entries to strings with
|
||||
the config you want - ie #define EXAMPLE_ESP_WIFI_STA_SSID "mywifissid"
|
||||
*/
|
||||
|
||||
/* STA Configuration */
|
||||
#define EXAMPLE_ESP_WIFI_STA_SSID CONFIG_ESP_WIFI_REMOTE_AP_SSID
|
||||
#define EXAMPLE_ESP_WIFI_STA_PASSWD CONFIG_ESP_WIFI_REMOTE_AP_PASSWORD
|
||||
#define EXAMPLE_ESP_MAXIMUM_RETRY CONFIG_ESP_MAXIMUM_STA_RETRY
|
||||
|
||||
#if CONFIG_ESP_WIFI_AUTH_OPEN
|
||||
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_OPEN
|
||||
#elif CONFIG_ESP_WIFI_AUTH_WEP
|
||||
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WEP
|
||||
#elif CONFIG_ESP_WIFI_AUTH_WPA_PSK
|
||||
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_PSK
|
||||
#elif CONFIG_ESP_WIFI_AUTH_WPA2_PSK
|
||||
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_PSK
|
||||
#elif CONFIG_ESP_WIFI_AUTH_WPA_WPA2_PSK
|
||||
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_WPA2_PSK
|
||||
#elif CONFIG_ESP_WIFI_AUTH_WPA3_PSK
|
||||
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA3_PSK
|
||||
#elif CONFIG_ESP_WIFI_AUTH_WPA2_WPA3_PSK
|
||||
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_WPA3_PSK
|
||||
#elif CONFIG_ESP_WIFI_AUTH_WAPI_PSK
|
||||
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WAPI_PSK
|
||||
#endif
|
||||
|
||||
/* AP Configuration */
|
||||
#define EXAMPLE_ESP_WIFI_AP_SSID CONFIG_ESP_WIFI_AP_SSID
|
||||
#define EXAMPLE_ESP_WIFI_AP_PASSWD CONFIG_ESP_WIFI_AP_PASSWORD
|
||||
#define EXAMPLE_ESP_WIFI_CHANNEL CONFIG_ESP_WIFI_AP_CHANNEL
|
||||
#define EXAMPLE_MAX_STA_CONN CONFIG_ESP_MAX_STA_CONN_AP
|
||||
|
||||
/* The event group allows multiple bits for each event, but we only care about
|
||||
* two events:
|
||||
* - we are connected to the AP with an IP
|
||||
* - we failed to connect after the maximum amount of retries */
|
||||
#define WIFI_CONNECTED_BIT BIT0
|
||||
#define WIFI_FAIL_BIT BIT1
|
||||
|
||||
/*DHCP server option*/
|
||||
#define DHCPS_OFFER_DNS 0x02
|
||||
|
||||
void netif_start();
|
||||
extern EventGroupHandle_t s_wifi_event_group;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue