diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 4370099..0118665 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,12 +1,10 @@ file(GLOB_RECURSE MY_LIB_SOURCES "ESP32epdx/src/*.cpp" "ESP32epdx/src/GUI/*.cpp" - "pubsubclient/src/*.cpp", - "mpack/src/mpack/*.c", - "sha256/*.c" + "mpack/src/mpack/*.c" ) idf_component_register( - SRCS "processor.c" "main.cpp" "EPD.cpp" "mesh_main.c" "mesh_netif.c" "mesh_msg.c" ${MY_LIB_SOURCES} - INCLUDE_DIRS "" "ESP32epdx/src" "ESP32epdx/src/GUI" "mpack/src/mpack" "sha256" + SRCS "main.cpp" "EPD.cpp" "mesh_main.c" "mesh_netif.c" "tcphelper.c" "process.c" ${MY_LIB_SOURCES} + INCLUDE_DIRS "" "ESP32epdx/src" "ESP32epdx/src/GUI" "mpack/src/mpack" ) diff --git a/main/main.cpp b/main/main.cpp index 9b54c2c..c340796 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1,5 +1,6 @@ #include "stdlib.h" +#include "esp_log.h" #include "Arduino.h" #include "nvs_flash.h" #include @@ -8,11 +9,9 @@ #include "ESP32epdx.h" #include "EPD.h" #include "mesh_netif.h" -#include "esp_mesh.h" +#include "tcphelper.h" #include "main.h" -#include "sha256.h" - std::mutex epd_mtx; /* @@ -22,25 +21,7 @@ EPD: RES = 22 ; DC = 23 ; CS = 1 ; BUSY = 0 uint8_t *image_buf = nullptr; -#if CONFIG_MESH_ROOT == 0 -extern "C" void req_release(request_t *req) { - free(req->command); - req->command = 0; - free(req->data); - req->data = 0; - - free(req); - - return; -} - extern "C" int command_update_image(uint64_t index, char *buf, size_t len) { - // check - if (!buf) { - return -2; - } - - // main if (!epd_mtx.try_lock()) { return -3; // locked } @@ -62,11 +43,6 @@ extern "C" int command_update_image(uint64_t index, char *buf, size_t len) { } extern "C" int command_push_image() { -#if DEBUG == 1 - printf("sha256 of image_buf: "); - show_sha256((char *)image_buf, CONFIG_IMAGE_BUF_SIZE); -#endif - if (epd_mtx.try_lock()) { EPD_HW_Init_Fast(); EPD_Display((unsigned char *)(image_buf)); @@ -79,149 +55,47 @@ extern "C" int command_push_image() { } } -extern "C" int mesh_client_callback(char *buf, size_t len) { +extern "C" int tcp_client_callback(char *buf, size_t len) { + for (size_t i = 0; i < len; i++) { + printf("%02x ", buf[i]); + } + printf("\n"); + request_t *req = client_parse_data(buf, len); if (!req) { // error -> invalid data return -2; } - int ret = 0; - // execute command if (strcmp(req->command, "ping") == 0) { - goto mesh_client_callback_ret; + return 0; } else if (strcmp(req->command, "update_image") == 0) { - ret = command_update_image(req->index, req->data, req->data_len); - goto mesh_client_callback_ret; + return command_update_image(req->index, req->data, req->data_len); } else if (strcmp(req->command, "push_image") == 0) { - ret = command_push_image(); - goto mesh_client_callback_ret; + return command_push_image(); } else { - ret = -2; - goto mesh_client_callback_ret; + return -2; } - -mesh_client_callback_ret: - req_release(req); - return ret; } -#else // root -> 送測試資料 -#define PAYLOAD_SIZE 1024 -#include "IMAGE.h" -void mesh_server_send_test_data() { - /* - mpack_writer_t *writer = (mpack_writer_t *)malloc(sizeof(mpack_writer_t)); - char *payload = (char *)malloc(PAYLOAD_SIZE); - if (!writer || !payload) { - printf("failed to allocate memory\n"); - return; - } - - const unsigned char *image_start = &gImage_1[0]; - const unsigned char *image_end = image_start + sizeof(gImage_1); - - printf("sha256 of gImage_1: "); - show_sha256((char *)image_start, sizeof(gImage_1)); - - uint32_t counter = 0; - for (const unsigned char *image = image_start; image < image_end; image += 950) { - vTaskDelay(2000 / portTICK_PERIOD_MS); - - memset(writer, 0, sizeof(mpack_writer_t)); - memset(payload, 0, PAYLOAD_SIZE); - - mpack_writer_init(writer, payload, PAYLOAD_SIZE); - mpack_start_map(writer, 3); - - mpack_write_cstr(writer,"command"); - mpack_write_cstr(writer,"update_image"); - mpack_write_cstr(writer,"index"); - printf("index -> %d\n", image - image_start); - mpack_write_u64(writer, image - image_start); - - //mpack_write_cstr(writer,"data"); - //size_t count = 950; - //if (image + 950 > image_end) - // count = image_end - image; - //mpack_start_bin(writer, count); - //mpack_write_bin(writer, (const char *)image, count); - //mpack_finish_bin(writer); - - char testdata[64]; - memset(testdata, 0, sizeof(testdata)); - snprintf(testdata, sizeof(testdata), "Hello, World! -> %lu", counter++); - mpack_start_bin(writer, sizeof(testdata)); - mpack_write_bin(writer, testdata, sizeof(testdata)); - mpack_finish_bin(writer); - - mpack_finish_map(writer); - - if (mpack_writer_destroy(writer) != mpack_ok) { - printf("error!\n"); - continue; - } - - //fwrite("payload: ", 1, 9, stdout); - //fwrite(payload, 1, PAYLOAD_SIZE, stdout); - //fwrite("\n", 1, 1, stdout); - - esp_err_t err = esp_mesh_boardcast_to_nodes((uint8_t *)payload, PAYLOAD_SIZE); - printf("mesh_server_send_test_data result -> %d\n", err); - } - - - vTaskDelay(1500 / portTICK_PERIOD_MS); - - memset(writer, 0, sizeof(mpack_writer_t)); - memset(payload, 0, PAYLOAD_SIZE); - - mpack_writer_init(writer, payload, PAYLOAD_SIZE); - mpack_start_map(writer, 1); - mpack_write_cstr(writer,"command"); - mpack_write_cstr(writer,"push_image"); - mpack_finish_map(writer); - - if (mpack_writer_destroy(writer) != mpack_ok) { - printf("error!\n"); - } else { - esp_err_t err = esp_mesh_boardcast_to_nodes((uint8_t *)payload, PAYLOAD_SIZE); - printf("mesh_server_send_test_data result -> %d\n", err); - } - - free(writer); - free(payload); - */ - - char payload[64] = {0}; - - for (int i = 0; i < 6; i++) { - memset(payload, 0, sizeof(payload)); - snprintf(payload, sizeof(payload), "Hello, World! -> %d", i); - esp_err_t err = esp_mesh_boardcast_to_nodes((uint8_t *)payload, sizeof(payload)); - printf("[i=%d] result -> %d\n", i, err); - vTaskDelay(1000 / portTICK_PERIOD_MS); - } - - return; -} -#endif extern "C" void app_main() { initArduino(); +#if CONFIG_TCP_ROOT == 0 // init image_buf image_buf = (uint8_t *)malloc(CONFIG_IMAGE_BUF_SIZE); if (!image_buf) { return; } memset(image_buf, 0, CONFIG_IMAGE_BUF_SIZE); +#endif // initialize NVS esp_err_t ret = nvs_flash_init(); @@ -234,26 +108,18 @@ extern "C" void app_main() { // Initialize EPD EPD_Pin_Init(6, 2, 7, 16, 22, 23, 1, 0); - - // start mesh network + mesh_main(); - xTaskCreate(esp_mesh_p2p_rx_main, "MPRX", 3072, NULL, 5, NULL); + + // tcp client + tcp_initialize(); // Arduino-like setup() // Arduino-like loop() - int32_t counter = 6; while (true) - { -#if CONFIG_MESH_ROOT == 1 - printf("counter -> %ld\n", counter); - if (counter == 0) - mesh_server_send_test_data(); - if (counter >= 0) - counter--; -#endif - printf("Am I root? -> %d\n", esp_mesh_is_root()); - vTaskDelay(5 * 1000 / portTICK_PERIOD_MS); + { + vTaskDelay(500); } // WARNING: if program reaches end of function app_main() the MCU will restart. diff --git a/main/main.h b/main/main.h index bef53d1..28f321a 100644 --- a/main/main.h +++ b/main/main.h @@ -1,4 +1,6 @@ -#pragma once +#ifndef MAIN_H +#define MAIN_H + #include /******************************************************* @@ -13,16 +15,19 @@ static const uint8_t MESH_ID[6] = { 0x77, 0x77, 0x77, 0x77, 0x77, 0x76 }; #define CONFIG_MESH_ROUTER_SSID "FBK_the_cutest_fox" #define CONFIG_MESH_ROUTER_PASSWD "zsfv3210" -//#define CONFIG_MESH_ROUTER_SSID "FastCyberRoom" -//#define CONFIG_MESH_ROUTER_PASSWD "dyes1107@" +//#define CONFIG_MESH_ROUTER_SSID "testesp" +//#define CONFIG_MESH_ROUTER_PASSWD "RABCRABC" #define CONFIG_MESH_AP_AUTHMODE WIFI_AUTH_WPA2_PSK #define CONFIG_MESH_AP_PASSWD "RASRASRAS" #define CONFIG_MESH_AP_CONNECTIONS 6 // number of nodes #define CONFIG_MESH_NON_MESH_AP_CONNECTIONS 0 // number of non-node devices -#define DEBUG 1 -#define CONFIG_MESH_ROOT 1 -#define CONFIG_IMAGE_BUF_SLICE_SIZE 950 // map["data"] max size +#define CONFIG_TCP_ROOT 0 +//#define CONFIG_TCP_SERVER_IP "172.16.0.1" +#define CONFIG_TCP_SERVER_IP "10.189.34.172" +#define CONFIG_TCP_SERVER_PORT "3030" +#define CONFIG_TCP_RXBUFFER_SIZE 4096 // tcp raw data max size +#define CONFIG_IMAGE_BUF_SLICE_SIZE 2048 // map["data"] max size #define CONFIG_IMAGE_BUF_SIZE 5624 /******************************************************* @@ -43,10 +48,16 @@ typedef struct request_t { size_t data_len; } request_t; -request_t *client_parse_data(char *buf, size_t len); +#if CONFIG_TCP_ROOT == 1 +int tcp_server_callback(const char *TAG, int sock); +#else +int tcp_client_callback(char *buf, size_t len); +#endif -int mesh_client_callback(char *buf, size_t len); +request_t *client_parse_data(char *buf, size_t len); #ifdef __cplusplus } +#endif + #endif \ No newline at end of file diff --git a/main/mesh_main.c b/main/mesh_main.c index a1bcce5..ef07ea2 100644 --- a/main/mesh_main.c +++ b/main/mesh_main.c @@ -19,7 +19,7 @@ #include "freertos/semphr.h" #include "main.h" -//#include "tcphelper.h" +#include "tcphelper.h" /******************************************************* * Macros @@ -35,7 +35,7 @@ /******************************************************* * Constants *******************************************************/ -const char *MESH_TAG = "mesh_main"; +static const char *MESH_TAG = "mesh_main"; /******************************************************* * Variable Definitions @@ -306,6 +306,13 @@ void ip_event_handler(void *arg, esp_event_base_t event_base, 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 + + // tcp client +#if CONFIG_TCP_ROOT == 1 + start_tcp_server(); +#else + start_tcp_client(); +#endif } @@ -349,12 +356,8 @@ void mesh_main(void) cfg.channel = CONFIG_MESH_CHANNEL; cfg.router.ssid_len = strlen(CONFIG_MESH_ROUTER_SSID); memcpy((uint8_t *) &cfg.router.ssid, CONFIG_MESH_ROUTER_SSID, cfg.router.ssid_len); - #if CONFIG_MESH_ROOT == 0 - memset((uint8_t *)&cfg.router.password, 0, 64); - #else memcpy((uint8_t *) &cfg.router.password, CONFIG_MESH_ROUTER_PASSWD, strlen(CONFIG_MESH_ROUTER_PASSWD)); - #endif /* mesh softAP */ ESP_ERROR_CHECK(esp_mesh_set_ap_authmode(CONFIG_MESH_AP_AUTHMODE)); diff --git a/main/mesh_msg.c b/main/mesh_msg.c deleted file mode 100644 index ba99347..0000000 --- a/main/mesh_msg.c +++ /dev/null @@ -1,186 +0,0 @@ -/* Mesh 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 -#include -#include "esp_wifi.h" -#include "esp_mac.h" -#include "esp_event.h" -#include "esp_log.h" -#include "esp_mesh.h" -#include "esp_mesh_internal.h" -#include "nvs_flash.h" - -#include "mesh_netif.h" -#include "main.h" - -/******************************************************* - * Macros - *******************************************************/ - -/******************************************************* - * Constants - *******************************************************/ -#define RX_SIZE (1500) -#define TX_SIZE (1460) - -/******************************************************* - * Variable Definitions - *******************************************************/ -static uint8_t tx_buf[TX_SIZE] = { 0, }; -static uint8_t rx_buf[RX_SIZE] = { 0, }; -static bool is_running = true; -static bool is_mesh_connected = false; -static mesh_addr_t mesh_parent_addr; -static int mesh_layer = -1; -static esp_netif_t *netif_sta = NULL; - -//mesh_light_ctl_t light_on = { -// .cmd = MESH_CONTROL_CMD, -// .on = 1, -// .token_id = MESH_TOKEN_ID, -// .token_value = MESH_TOKEN_VALUE, -//}; -// -//mesh_light_ctl_t light_off = { -// .cmd = MESH_CONTROL_CMD, -// .on = 0, -// .token_id = MESH_TOKEN_ID, -// .token_value = MESH_TOKEN_VALUE, -//}; - -/******************************************************* - * Function Declarations - *******************************************************/ - -/******************************************************* - * Function Definitions - *******************************************************/ -#if CONFIG_MESH_ROOT == 1 -esp_err_t esp_mesh_boardcast_to_nodes(uint8_t *payload, uint16_t payload_len) -{ - int i; - esp_err_t err; - mesh_addr_t route_table[CONFIG_MESH_ROUTE_TABLE_SIZE]; - int route_table_size = 0; - mesh_data_t data; - data.data = payload; - data.size = payload_len; - data.proto = MESH_PROTO_BIN; - data.tos = MESH_TOS_P2P; - - esp_mesh_get_routing_table((mesh_addr_t *) &route_table, - CONFIG_MESH_ROUTE_TABLE_SIZE * 6, &route_table_size); - - printf("route table size -> %d\n", route_table_size); - for (i = 0; i < route_table_size; i++) { - err = esp_mesh_send(&route_table[i], &data, MESH_DATA_P2P, NULL, 0); - if (err) { - ESP_LOGE(MESH_TAG, - "[ROOT-2-UNICAST][L:%d]parent:"MACSTR" to "MACSTR", heap:%" PRId32 "[err:0x%x, proto:%d, tos:%d]", - mesh_layer, MAC2STR(mesh_parent_addr.addr), - MAC2STR(route_table[i].addr), esp_get_minimum_free_heap_size(), - err, data.proto, data.tos); - return err; - } - } - return ESP_OK; -} -#else -esp_err_t esp_mesh_send_to_root(uint8_t *payload, uint16_t payload_len) -{ - esp_err_t err; - mesh_data_t data; - data.data = payload; - data.size = payload_len; - data.proto = MESH_PROTO_BIN; - data.tos = MESH_TOS_P2P; - - err = esp_mesh_send(NULL, &data, MESH_DATA_P2P, NULL, 0); - if (err) { - //ESP_LOGE(MESH_TAG, - // "[ROOT-2-UNICAST][L:%d]parent:"MACSTR" to "MACSTR", heap:%" PRId32 "[err:0x%x, proto:%d, tos:%d]", - // mesh_layer, MAC2STR(mesh_parent_addr.addr), - // MAC2STR(route_table[i].addr), esp_get_minimum_free_heap_size(), - // err, data.proto, data.tos); - ESP_LOGE(MESH_TAG, "Error while sending message to root, heap:%" PRId32 "[err:0x%x, proto:%d, tos:%d]", - esp_get_minimum_free_heap_size(), err, - data.proto, data.tos); - return err; - } - - return ESP_OK; -} -#endif - -void esp_mesh_p2p_rx_main(void *arg) -{ - esp_err_t err; - mesh_addr_t from; - mesh_data_t data; - int flag = 0; - data.data = rx_buf; - data.size = RX_SIZE; - is_running = true; - - while (is_running) { - data.size = RX_SIZE; - memset(rx_buf, 0, sizeof(rx_buf)); - err = esp_mesh_recv(&from, &data, portMAX_DELAY, &flag, NULL, 0); - if (err != ESP_OK || !data.size) { - ESP_LOGE(MESH_TAG, "err:0x%x, size:%d", err, data.size); - continue; - } - //if (data.size > sizeof(packet_t)) { - // ESP_LOGE(MESH_TAG, "err:data.size > sizeof(packet_t), size:%d", data.size); - // continue; - //} - // extract data and process - //packet_t *packet = calloc(1, sizeof(packet_t)); - //if (!packet) { - // ESP_LOGE(MESH_TAG, "failed to allocate memory"); - // continue; - //} - //memcpy(packet, data.data, data.size); -#if CONFIG_MESH_ROOT == 1 - printf("[root] received:%s\n", data.data); -#else - printf("recvived aaa\n"); - printf("[root] received:%s\n", data.data); - char result_str[128] = {0}; - int result = mesh_client_callback((char *)data.data, data.size); - switch (result) { - case 0: - strcpy(result_str, "ok\n"); - break; - case -2: - strcpy(result_str, "invalid request\n"); - break; - case -3: - strcpy(result_str, "locked\n"); - break; - default: - strcpy(result_str, "unknown error\n"); - break; - } - printf("returned -> %s\n", result_str); - esp_mesh_send_to_root((uint8_t *)result_str, strlen(result_str)+1); -#endif - //free(packet); - //packet = NULL; - - // log - ESP_LOGW(MESH_TAG, - "[#RX][L:%d] parent:"MACSTR", receive from "MACSTR", size:%d, heap:%" PRId32 ", flag:%d[err:0x%x, proto:%d, tos:%d]", - mesh_layer, - MAC2STR(mesh_parent_addr.addr), MAC2STR(from.addr), - data.size, esp_get_minimum_free_heap_size(), flag, err, data.proto, - data.tos); - } - vTaskDelete(NULL); -} \ No newline at end of file diff --git a/main/mesh_netif.h b/main/mesh_netif.h index e9d684a..aee06a7 100644 --- a/main/mesh_netif.h +++ b/main/mesh_netif.h @@ -13,10 +13,6 @@ #include "esp_mesh.h" #include "esp_mac.h" -#include "main.h" - -extern const char *MESH_TAG; - /******************************************************* * Macros *******************************************************/ @@ -94,20 +90,6 @@ uint8_t* mesh_netif_get_station_mac(void); // mesh main function void mesh_main(void); -// mesh_msg.c -void esp_mesh_p2p_rx_main(void *arg); - -#if CONFIG_MESH_ROOT == 1 -esp_err_t esp_mesh_boardcast_to_nodes(uint8_t *payload, uint16_t payload_len); -#else - -#endif - -typedef struct packet_t { - uint32_t packet_id; - uint8_t data[1024]; -} packet_t; - #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/main/note.txt b/main/note.txt index c1742b2..f6385e0 100644 --- a/main/note.txt +++ b/main/note.txt @@ -1,3 +1 @@ -must enable "use external log wrapper" - -我是寫 client 的,所以我這裡的「模擬 master」採用直接 unicast 的方式 \ No newline at end of file +must enable "use external log wrapper" \ No newline at end of file diff --git a/main/processor.c b/main/process.c similarity index 69% rename from main/processor.c rename to main/process.c index 996618d..829cd2a 100644 --- a/main/processor.c +++ b/main/process.c @@ -1,24 +1,27 @@ +#pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #include #include +#include -#include "mpack/src/mpack/mpack.h" +#include "mpack.h" #include "main.h" +#if CONFIG_TCP_ROOT == 0 request_t *client_parse_data(char *buf, size_t len) { mpack_reader_t reader; // initialize mpack_reader_init_data(&reader, buf, len); if (mpack_reader_error(&reader) != mpack_ok) { - goto parse_data_finish; + goto client_parse_data_finish; } size_t count = mpack_expect_map_max(&reader, 100); if (mpack_reader_error(&reader) != mpack_ok) { - goto parse_data_finish; + goto client_parse_data_finish; } // variables @@ -32,31 +35,40 @@ request_t *client_parse_data(char *buf, size_t len) { char key[32] = {0}; mpack_expect_cstr(&reader, key, sizeof(key)); if (mpack_reader_error(&reader) != mpack_ok) { - goto parse_data_finish; + goto client_parse_data_finish; } + printf("key -> %s\n", key); if (strcmp(key, "command") == 0) { command = mpack_expect_cstr_alloc(&reader, 32); } else if (strcmp(key, "index") == 0) { index = mpack_expect_u64(&reader); } else if (strcmp(key, "data") == 0) { - mpack_expect_bin(&reader); + //mpack_expect_bin(&reader); data = mpack_expect_bin_alloc(&reader, CONFIG_IMAGE_BUF_SLICE_SIZE, &data_len); - } else { + } + else { mpack_discard(&reader); } + + if (mpack_reader_error(&reader) != mpack_ok) { + puts("broken"); + } } -parse_data_finish: - if (mpack_reader_destroy(&reader) != mpack_ok) +client_parse_data_finish: + if (mpack_reader_destroy(&reader) != mpack_ok) { return NULL; + } - if (!command) + if (!command) { return NULL; + } - request_t *req = malloc(sizeof(request_t)); - if (!req) + request_t *req = (request_t *)malloc(sizeof(request_t)); + if (!req) { return NULL; + } memset(req, 0, sizeof(request_t)); req->command = command; @@ -65,4 +77,5 @@ parse_data_finish: req->data_len = data_len; return req; -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/main/sha256/sha256.c b/main/sha256/sha256.c deleted file mode 100644 index 0042864..0000000 --- a/main/sha256/sha256.c +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include - -#include - -void show_sha256(char *buf, size_t len) { - uint8_t output[32]; - memset(output, 0, sizeof(output)); - - mbedtls_md_context_t ctx; - mbedtls_md_init(&ctx); - mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 0); - mbedtls_md_starts(&ctx); - mbedtls_md_update(&ctx, (const unsigned char *)buf, len); - mbedtls_md_finish(&ctx, output); - mbedtls_md_free(&ctx); - - for (int i = 0; i < sizeof(output); i++) { - printf("%02x", output[i]); - } - printf("\n"); - - return; -} \ No newline at end of file diff --git a/main/sha256/sha256.h b/main/sha256/sha256.h deleted file mode 100644 index ce46faf..0000000 --- a/main/sha256/sha256.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -void show_sha256(char *buf, size_t len); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/main/tcphelper.c b/main/tcphelper.c new file mode 100644 index 0000000..4cbe277 --- /dev/null +++ b/main/tcphelper.c @@ -0,0 +1,443 @@ +/* BSD non-blocking socket 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 +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "sys/socket.h" +#include "netdb.h" +#include "errno.h" +#include "esp_system.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "esp_wifi.h" + +#include "main.h" +#include "mesh_netif.h" + +/** + * @brief Indicates that the file descriptor represents an invalid (uninitialized or closed) socket + * + * Used in the TCP server structure `sock[]` which holds list of active clients we serve. + */ +#define INVALID_SOCK (-1) + +/** + * @brief Time in ms to yield to all tasks when a non-blocking socket would block + * + * Non-blocking socket operations are typically executed in a separate task validating + * the socket status. Whenever the socket returns `EAGAIN` (idle status, i.e. would block) + * we have to yield to all tasks to prevent lower priority tasks from starving. + */ +#define YIELD_TO_ALL_MS 50 + +/** + * @brief Utility to log socket errors + * + * @param[in] tag Logging tag + * @param[in] sock Socket number + * @param[in] err Socket errno + * @param[in] message Message to print + */ +static void log_socket_error(const char *tag, const int sock, const int err, const char *message) +{ + ESP_LOGE(tag, "[sock=%d]: %s\n" + "error=%d: %s", sock, message, err, strerror(err)); +} + +/** + * @brief Tries to receive data from specified sockets in a non-blocking way, + * i.e. returns immediately if no data. + * + * @param[in] tag Logging tag + * @param[in] sock Socket for reception + * @param[out] data Data pointer to write the received data + * @param[in] max_len Maximum size of the allocated space for receiving data + * @return + * >0 : Size of received data + * =0 : No data available + * -1 : Error occurred during socket read operation + * -2 : Socket is not connected, to distinguish between an actual socket error and active disconnection + */ +int try_receive(const char *tag, const int sock, char * data, size_t max_len) +{ + int len = recv(sock, data, max_len, 0); + if (len < 0) { + if (errno == EINPROGRESS || errno == EAGAIN || errno == EWOULDBLOCK) { + return 0; // Not an error + } + if (errno == ENOTCONN) { + ESP_LOGW(tag, "[sock=%d]: Connection closed", sock); + return -2; // Socket has been disconnected + } + log_socket_error(tag, sock, errno, "Error occurred during receiving"); + return -1; + } + + return len; +} + +/** + * @brief Sends the specified data to the socket. This function blocks until all bytes got sent. + * + * @param[in] tag Logging tag + * @param[in] sock Socket to write data + * @param[in] data Data to be written + * @param[in] len Length of the data + * @return + * >0 : Size the written data + * -1 : Error occurred during socket write operation + */ +int socket_send(const char *tag, const int sock, const char * data, const size_t len) +{ + int to_write = len; + while (to_write > 0) { + int written = send(sock, data + (len - to_write), to_write, 0); + if (written < 0 && errno != EINPROGRESS && errno != EAGAIN && errno != EWOULDBLOCK) { + log_socket_error(tag, sock, errno, "Error occurred during sending"); + return -1; + } + to_write -= written; + } + return len; +} + +// variables +SemaphoreHandle_t lock = NULL; +TaskHandle_t *task_handle = NULL; + +void tcp_initialize() { + lock = xSemaphoreCreateMutex(); + + return; +} + +#if CONFIG_TCP_ROOT == 1 +static const char *TAG = "nonblocking-socket-server"; + +/** + * @brief Returns the string representation of client's address (accepted on this server) + */ +static inline char* get_clients_address(struct sockaddr_storage *source_addr) +{ + static char address_str[128]; + char *res = NULL; + // Convert ip address to string + if (source_addr->ss_family == PF_INET) { + res = inet_ntoa_r(((struct sockaddr_in *)source_addr)->sin_addr, address_str, sizeof(address_str) - 1); + } +#ifdef CONFIG_LWIP_IPV6 + else if (source_addr->ss_family == PF_INET6) { + res = inet6_ntoa_r(((struct sockaddr_in6 *)source_addr)->sin6_addr, address_str, sizeof(address_str) - 1); + } +#endif + if (!res) { + address_str[0] = '\0'; // Returns empty string if conversion didn't succeed + } + return address_str; +} + +static void tcp_server_task(void *pvParameters) +{ + static char rx_buffer[128]; + struct addrinfo hints = { .ai_socktype = SOCK_STREAM }; + struct addrinfo *address_info; + int listen_sock = INVALID_SOCK; + const size_t max_socks = CONFIG_LWIP_MAX_SOCKETS - 1; + static int sock[CONFIG_LWIP_MAX_SOCKETS - 1]; + + // Prepare a list of file descriptors to hold client's sockets, mark all of them as invalid, i.e. available + for (int i=0; iai_family, address_info->ai_socktype, address_info->ai_protocol); + + if (listen_sock < 0) { + log_socket_error(TAG, listen_sock, errno, "Unable to create socket"); + goto error; + } + ESP_LOGI(TAG, "Listener socket created"); + + // Marking the socket as non-blocking + int flags = fcntl(listen_sock, F_GETFL); + if (fcntl(listen_sock, F_SETFL, flags | O_NONBLOCK) == -1) { + log_socket_error(TAG, listen_sock, errno, "Unable to set socket non blocking"); + goto error; + } + ESP_LOGI(TAG, "Socket marked as non blocking"); + + // Binding socket to the given address + int err = bind(listen_sock, address_info->ai_addr, address_info->ai_addrlen); + if (err != 0) { + log_socket_error(TAG, listen_sock, errno, "Socket unable to bind"); + goto error; + } + ESP_LOGI(TAG, "Socket bound on %s:%s", "0.0.0.0", CONFIG_TCP_SERVER_PORT); + + // Set queue (backlog) of pending connections to one (can be more) + err = listen(listen_sock, 1); + if (err != 0) { + log_socket_error(TAG, listen_sock, errno, "Error occurred during listen"); + goto error; + } + ESP_LOGI(TAG, "Socket listening"); + + // Main loop for accepting new connections and serving all connected clients + while (1) { + struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6 + socklen_t addr_len = sizeof(source_addr); + + // Find a free socket + int new_sock_index = 0; + for (new_sock_index=0; new_sock_index print it's address + ESP_LOGI(TAG, "[sock=%d]: Connection accepted from IP:%s", sock[new_sock_index], get_clients_address(&source_addr)); + + // ...and set the client's socket non-blocking + flags = fcntl(sock[new_sock_index], F_GETFL); + if (fcntl(sock[new_sock_index], F_SETFL, flags | O_NONBLOCK) == -1) { + log_socket_error(TAG, sock[new_sock_index], errno, "Unable to set socket non blocking"); + goto error; + } + ESP_LOGI(TAG, "[sock=%d]: Socket marked as non blocking", sock[new_sock_index]); + } + } + + // We serve all the connected clients in this loop + for (int i=0; i closing the socket", sock[i], result); + close(sock[i]); + sock[i] = INVALID_SOCK; + } + } // one client's socket + } // for all sockets + + // Yield to other tasks + vTaskDelay(pdMS_TO_TICKS(YIELD_TO_ALL_MS)); + } + +error: + if (listen_sock != INVALID_SOCK) { + close(listen_sock); + } + + for (int i=0; iai_family, address_info->ai_socktype, address_info->ai_protocol); + if (sock < 0) { + log_socket_error(TAG, sock, errno, "Unable to create socket"); + goto error; + } + ESP_LOGI(TAG, "Socket created, connecting to %s:%s", CONFIG_TCP_SERVER_IP, CONFIG_TCP_SERVER_PORT); + + // Marking the socket as non-blocking + int flags = fcntl(sock, F_GETFL); + if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) { + log_socket_error(TAG, sock, errno, "Unable to set socket non blocking"); + } + + if (connect(sock, address_info->ai_addr, address_info->ai_addrlen) != 0) { + if (errno == EINPROGRESS) { + ESP_LOGD(TAG, "connection in progress"); + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(sock, &fdset); + + // Connection in progress -> have to wait until the connecting socket is marked as writable, i.e. connection completes + res = select(sock+1, NULL, &fdset, NULL, NULL); + if (res < 0) { + log_socket_error(TAG, sock, errno, "Error during connection: select for socket to be writable"); + goto error; + } else if (res == 0) { + log_socket_error(TAG, sock, errno, "Connection timeout: select for socket to be writable"); + goto error; + } else { + int sockerr; + socklen_t len = (socklen_t)sizeof(int); + + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)(&sockerr), &len) < 0) { + log_socket_error(TAG, sock, errno, "Error when getting socket error using getsockopt()"); + goto error; + } + if (sockerr) { + log_socket_error(TAG, sock, sockerr, "Connection error"); + goto error; + } + } + } else { + log_socket_error(TAG, sock, errno, "Socket is unable to connect"); + goto error; + } + } + + ESP_LOGI(TAG, "Connected to %s:%s", CONFIG_TCP_SERVER_IP, CONFIG_TCP_SERVER_PORT); + // Keep receiving message + do { + // receive + int len = 0; + memset(rx_buffer, 0, sizeof(rx_buffer)); + do { + len = try_receive(TAG, sock, rx_buffer, sizeof(rx_buffer)); + if (len < 0) { + ESP_LOGE(TAG, "Error occurred during try_receive"); + goto error; + } + vTaskDelay(pdMS_TO_TICKS(YIELD_TO_ALL_MS)); + } while (len == 0); + ESP_LOGI(TAG, "Received message (len=%d)", len); + + // execute command + char tx_buffer[128] = {0}; + int result = tcp_client_callback(rx_buffer, len); + switch (result) { + case 0: + strcpy(tx_buffer, "ok\n"); + break; + case -2: + strcpy(tx_buffer, "invalid request\n"); + break; + case -3: + strcpy(tx_buffer, "locked\n"); + break; + default: + strcpy(tx_buffer, "unknown error\n"); + break; + } + + // send result + len = socket_send(TAG, sock, tx_buffer, strlen(tx_buffer)); + if (len < 0) { + ESP_LOGE(TAG, "Error occurred during socket_send"); + goto error; + } + ESP_LOGI(TAG, "Written: %.*s", len, tx_buffer); + } while (true); + +error: + if (sock != INVALID_SOCK) { + close(sock); + } + free(address_info); + + free(task_handle); + task_handle = NULL; + vTaskDelete(NULL); +} + +void start_tcp_client(void) { + if (xSemaphoreTake(lock, 1000 / portTICK_PERIOD_MS) == false) + { + return; + } + + // stop old task + if (task_handle) { + ESP_LOGI(TAG, "Detected old task, deleting..."); + + vTaskDelete(*task_handle); + free(task_handle); + task_handle = NULL; + } + + // new task + task_handle = malloc(sizeof(TaskHandle_t)); + memset(task_handle, 0, sizeof(TaskHandle_t)); + + xTaskCreate(tcp_client_task, "tcp_client", 4096, NULL, 5, task_handle); + + xSemaphoreGive(lock); + return; +} +#endif \ No newline at end of file diff --git a/main/tcphelper.h b/main/tcphelper.h new file mode 100644 index 0000000..2fbbdda --- /dev/null +++ b/main/tcphelper.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "main.h" + +// functions +int socket_send(const char *tag, const int sock, const char * data, const size_t len); +int try_receive(const char *tag, const int sock, char * data, size_t max_len); + +void tcp_initialize(void); + +#if CONFIG_TCP_ROOT == 1 +void start_tcp_server(void); +#else +void start_tcp_client(void); +#endif + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/tcp_client_asyncio.py b/tcp_client_asyncio.py new file mode 100644 index 0000000..a505001 --- /dev/null +++ b/tcp_client_asyncio.py @@ -0,0 +1,28 @@ +import asyncio +import signal + +async def handle_client(reader, writer): + try: + while True: + #data = await reader.readline() + #if not data: + # break +# + #msg = data.decode().strip() + #writer.write(f"echo: {msg}\n".encode()) + await writer.drain() + except Exception as e: + print("error:", e) + finally: + writer.close() + await writer.wait_closed() + +async def main(): + server = await asyncio.start_server( + handle_client, host="0.0.0.0", port=3030 + ) + + async with server: + await server.serve_forever() + +asyncio.run(main())