socket ver

This commit is contained in:
jasinco 2025-12-31 14:53:05 +08:00
parent 9c5f05027a
commit 4a0d3af4c1
15 changed files with 3753 additions and 30527 deletions

View file

@ -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)

View file

@ -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

View file

@ -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");
} }

View file

@ -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);
}
} }

View file

@ -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);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

738
main/lib/picohttpparser.c Normal file
View 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
View 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

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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;
}

View file

@ -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);

View file

@ -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);
}
} }

View file

@ -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;