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"
|
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
|
PRIV_REQUIRES esp_wifi esp_timer mpack lwip esp_partition nvs_flash joltwallet__littlefs esp-tls app_update
|
||||||
INCLUDE_DIRS ".")
|
INCLUDE_DIRS ".")
|
||||||
|
|
||||||
idf_component_set_property(main MINIMAL_BUILD ON)
|
idf_component_set_property(main MINIMAL_BUILD ON)
|
||||||
|
|
||||||
target_compile_options(${COMPONENT_LIB} PRIVATE -std=gnu23)
|
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"
|
menu "SoftAP Configuration"
|
||||||
range 0 14
|
comment "SoftAP Configuration"
|
||||||
default 0
|
|
||||||
|
config ESP_WIFI_AP_SSID
|
||||||
|
string "WiFi AP SSID"
|
||||||
|
default "myssid"
|
||||||
help
|
help
|
||||||
mesh network channel.
|
SSID (network name) of the AP for the example to connect to.
|
||||||
|
|
||||||
config MESH_ROUTER_SSID
|
config ESP_WIFI_AP_PASSWORD
|
||||||
string "Router SSID"
|
string "WiFi AP Password"
|
||||||
default "ROUTER_SSID"
|
default "mypassword"
|
||||||
help
|
help
|
||||||
Router SSID.
|
WiFi password of the AP for the example to use.
|
||||||
|
|
||||||
config MESH_ROUTER_PASSWD
|
config ESP_WIFI_AP_CHANNEL
|
||||||
string "Router password"
|
int "WiFi AP Channel"
|
||||||
default "ROUTER_PASSWD"
|
range 1 14
|
||||||
|
default 1
|
||||||
help
|
help
|
||||||
Router password.
|
WiFi channel (network channel) of the AP for the example to use.
|
||||||
|
|
||||||
choice
|
config ESP_MAX_STA_CONN_AP
|
||||||
bool "Mesh AP Authentication Mode"
|
int "Maximal STA connections"
|
||||||
default WIFI_AUTH_WPA2_PSK
|
default 4
|
||||||
help
|
help
|
||||||
Authentication mode.
|
Max number of the STA connects to AP.
|
||||||
|
endmenu
|
||||||
|
|
||||||
config WIFI_AUTH_OPEN
|
menu "STA Configuration"
|
||||||
bool "WIFI_AUTH_OPEN"
|
comment "STA Configuration"
|
||||||
config WIFI_AUTH_WPA_PSK
|
|
||||||
bool "WIFI_AUTH_WPA_PSK"
|
config ESP_WIFI_REMOTE_AP_SSID
|
||||||
config WIFI_AUTH_WPA2_PSK
|
string "WiFi Remote AP SSID"
|
||||||
bool "WIFI_AUTH_WPA2_PSK"
|
default "otherapssid"
|
||||||
config WIFI_AUTH_WPA_WPA2_PSK
|
help
|
||||||
bool "WIFI_AUTH_WPA_WPA2_PSK"
|
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
|
endchoice
|
||||||
|
|
||||||
config MESH_AP_AUTHMODE
|
endmenu
|
||||||
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_littlefs.h>
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
#include <nvs_flash.h>
|
#include <nvs_flash.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#define BLOCKSIZE 4096
|
#define BLOCKSIZE 4096
|
||||||
|
|
||||||
const char *TAG = "main";
|
const char *TAG = "main";
|
||||||
|
|
||||||
void fatal_err(int code) {
|
|
||||||
while (1) {
|
|
||||||
ESP_LOGE(TAG, "fatal err: %d", code);
|
|
||||||
sleep(5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void app_main(void) {
|
void app_main(void) {
|
||||||
|
|
||||||
/* event initialization */
|
/* event initialization */
|
||||||
|
|
@ -40,9 +34,16 @@ void app_main(void) {
|
||||||
return;
|
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();
|
struct Meshtalos *control = mtsh_init();
|
||||||
if (control == NULL) {
|
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 "meshtalos.h"
|
||||||
#include "freertos/idf_additions.h"
|
#include "./mpack.h"
|
||||||
#include "lib/mongoose.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 <string.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
uint8_t image_buf[IMAGE_SIZE] = {0};
|
#define STB_DS_IMPLEMENTATION
|
||||||
bool flag_refresh = true;
|
#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 conn {
|
||||||
struct mg_http_message *hm =
|
int rootfd;
|
||||||
(struct mg_http_message *)ev_data; // Parsed HTTP request
|
fd_set master, recv;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tag"), NULL)) { // REST API call?
|
struct client *c;
|
||||||
if (hm->body.len == sizeof(image_buf)) {
|
int max_client_fd;
|
||||||
memcpy(image_buf, hm->body.buf, sizeof(image_buf));
|
struct timeval interval;
|
||||||
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 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 {
|
} 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_init() {
|
||||||
struct Meshtalos *mtsh = calloc(1, sizeof(struct Meshtalos));
|
struct Meshtalos *m = calloc(sizeof(struct Meshtalos), 1);
|
||||||
if (mtsh == NULL) {
|
return m;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,32 +8,29 @@
|
||||||
* mpack formatted: [ uint8_t id = 1, char topic[TOPIC_LEN] ]
|
* mpack formatted: [ uint8_t id = 1, char topic[TOPIC_LEN] ]
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "./mongoose.h"
|
|
||||||
#include "./storage.h"
|
#include "./storage.h"
|
||||||
|
|
||||||
#define TOPIC_LEN 20
|
#define TOPIC_LEN 20
|
||||||
#define IMAGE_SIZE 152 * 296
|
#define IMG_W_SIZE 152
|
||||||
#define IMAGE_BLK_SIZE 1024
|
#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 Meshtalos {
|
||||||
struct Storage *storage;
|
struct Storage *storage;
|
||||||
struct mg_mgr mg;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sync_jobs {
|
// struct sync_jobs {
|
||||||
// char *tag_name;
|
// // char *tag_name;
|
||||||
char topic[TOPIC_LEN];
|
// char topic[TOPIC_LEN];
|
||||||
};
|
// };
|
||||||
struct sub {
|
// struct sub {
|
||||||
struct mg_connection *c;
|
// char topic[TOPIC_LEN];
|
||||||
char topic[TOPIC_LEN];
|
// struct sub *next;
|
||||||
struct sub *next;
|
// };
|
||||||
};
|
// extern struct sub *sub_list;
|
||||||
extern struct sub *sub_list;
|
|
||||||
extern QueueHandle_t sub_jobs;
|
|
||||||
extern SemaphoreHandle_t sub_mutex;
|
|
||||||
extern uint8_t image_buf[IMAGE_SIZE];
|
|
||||||
|
|
||||||
struct Meshtalos *mtsh_init();
|
struct Meshtalos *mtsh_init();
|
||||||
void mtsh_listen(struct Meshtalos *mtsh, const char *http_endpoint);
|
void mtsh_listen(struct Meshtalos *mtsh, const int worker_port, int http_port);
|
||||||
void mtsh_tcp_sender(struct mg_mgr *mgr);
|
|
||||||
|
|
|
||||||
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.)
|
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
|
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
CONDITIONS OF ANY KIND, either express or implied.
|
CONDITIONS OF ANY KIND, either express or implied.
|
||||||
*/
|
*/
|
||||||
#include "esp_err.h"
|
|
||||||
#include "esp_event.h"
|
#include "esp_event.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_mac.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.h"
|
||||||
#include "esp_wifi_types_generic.h"
|
#include "lwip/inet.h"
|
||||||
#include "freertos/semphr.h"
|
#include "lwip/ip4_addr.h"
|
||||||
#include "mesh_netif.h"
|
#include "lwip/netdb.h"
|
||||||
|
#include "lwip/sockets.h"
|
||||||
#include "nvs_flash.h"
|
#include "nvs_flash.h"
|
||||||
#include <inttypes.h>
|
#include <network.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#if IP_NAPT
|
||||||
/*******************************************************
|
#include "lwip/lwip_napt.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);
|
|
||||||
#endif
|
#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) {
|
void netif_start(void) {
|
||||||
ESP_ERROR_CHECK(nvs_flash_init());
|
|
||||||
/* tcpip initialization */
|
|
||||||
ESP_ERROR_CHECK(esp_netif_init());
|
ESP_ERROR_CHECK(esp_netif_init());
|
||||||
/* event initialization */
|
|
||||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
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 */
|
// Initialize NVS
|
||||||
wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT();
|
esp_err_t ret = nvs_flash_init();
|
||||||
ESP_ERROR_CHECK(esp_wifi_init(&config));
|
if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
|
||||||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
|
ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||||
&ip_event_handler, NULL));
|
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_FLASH));
|
ret = nvs_flash_init();
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
|
}
|
||||||
ESP_ERROR_CHECK(esp_wifi_start());
|
ESP_ERROR_CHECK(ret);
|
||||||
/* mesh initialization */
|
|
||||||
ESP_ERROR_CHECK(esp_mesh_init());
|
/* Initialize event group */
|
||||||
ESP_ERROR_CHECK(esp_event_handler_register(MESH_EVENT, ESP_EVENT_ANY_ID,
|
s_wifi_event_group = xEventGroupCreate();
|
||||||
&mesh_event_handler, NULL));
|
|
||||||
ESP_ERROR_CHECK(esp_mesh_set_max_layer(CONFIG_MESH_MAX_LAYER));
|
/* Register Event handler */
|
||||||
ESP_ERROR_CHECK(esp_mesh_set_vote_percentage(1));
|
ESP_ERROR_CHECK(esp_event_handler_instance_register(
|
||||||
ESP_ERROR_CHECK(esp_mesh_set_ap_assoc_expire(10));
|
WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL));
|
||||||
/* set blocking time of esp_mesh_send() to 30s, to prevent the esp_mesh_send()
|
ESP_ERROR_CHECK(esp_event_handler_instance_register(
|
||||||
* from permanently for some reason */
|
IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, NULL));
|
||||||
ESP_ERROR_CHECK(esp_mesh_send_block_time(30000));
|
|
||||||
mesh_cfg_t cfg = MESH_INIT_CONFIG_DEFAULT();
|
/*Initialize WiFi */
|
||||||
#if !MESH_IE_ENCRYPTED
|
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||||
cfg.crypto_funcs = NULL;
|
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||||
#endif
|
|
||||||
/* mesh ID */
|
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));
|
||||||
memcpy((uint8_t *)&cfg.mesh_id, MESH_ID, 6);
|
|
||||||
/* router */
|
/* Initialize AP */
|
||||||
cfg.channel = CONFIG_MESH_CHANNEL;
|
ESP_LOGI(TAG_AP, "ESP_WIFI_MODE_AP");
|
||||||
cfg.router.ssid_len = strlen(CONFIG_MESH_ROUTER_SSID);
|
esp_netif_t *esp_netif_ap = wifi_init_softap();
|
||||||
memcpy((uint8_t *)&cfg.router.ssid, CONFIG_MESH_ROUTER_SSID,
|
|
||||||
cfg.router.ssid_len);
|
/* Initialize STA */
|
||||||
memcpy((uint8_t *)&cfg.router.password, CONFIG_MESH_ROUTER_PASSWD,
|
ESP_LOGI(TAG_STA, "ESP_WIFI_MODE_STA");
|
||||||
strlen(CONFIG_MESH_ROUTER_PASSWD));
|
esp_netif_t *esp_netif_sta = wifi_init_sta();
|
||||||
/* mesh softAP */
|
|
||||||
ESP_ERROR_CHECK(esp_mesh_set_ap_authmode(CONFIG_MESH_AP_AUTHMODE));
|
/* Start WiFi */
|
||||||
cfg.mesh_ap.max_connection = CONFIG_MESH_AP_CONNECTIONS;
|
ESP_ERROR_CHECK(esp_wifi_start());
|
||||||
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));
|
* Wait until either the connection is established (WIFI_CONNECTED_BIT) or
|
||||||
ESP_ERROR_CHECK(esp_mesh_set_config(&cfg));
|
* connection failed for the maximum number of re-tries (WIFI_FAIL_BIT).
|
||||||
// fixed root node
|
* The bits are set by event_handler() (see above)
|
||||||
// wifi_config_t wifi_cfg;
|
*/
|
||||||
|
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
|
||||||
// ESP_ERROR_CHECK(esp_wifi_get_config(WIFI_IF_AP, &wifi_cfg));
|
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
|
||||||
// ESP_ERROR_CHECK(
|
pdFALSE, pdFALSE, portMAX_DELAY);
|
||||||
// esp_mesh_set_parent(&wifi_cfg, &cfg.mesh_id, MESH_ROOT,
|
|
||||||
// MESH_ROOT_LAYER));
|
/* xEventGroupWaitBits() returns the bits before the call returned,
|
||||||
//
|
* hence we can test which event actually happened. */
|
||||||
/* mesh start */
|
if (bits & WIFI_CONNECTED_BIT) {
|
||||||
ESP_ERROR_CHECK(esp_mesh_start());
|
ESP_LOGI(TAG_STA, "connected to ap SSID:%s password:%s",
|
||||||
ESP_LOGI(MESH_TAG, "mesh starts successfully, heap:%" PRId32 ", %s",
|
EXAMPLE_ESP_WIFI_STA_SSID, EXAMPLE_ESP_WIFI_STA_PASSWD);
|
||||||
esp_get_free_heap_size(),
|
softap_set_dns_addr(esp_netif_ap, esp_netif_sta);
|
||||||
esp_mesh_is_root_fixed() ? "root fixed" : "root not fixed");
|
} 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
|
#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();
|
void netif_start();
|
||||||
|
extern EventGroupHandle_t s_wifi_event_group;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue