diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 46d89fb..1600a50 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -2,9 +2,10 @@ file(GLOB_RECURSE MY_LIB_SOURCES "ESP32epdx/src/*.cpp" "ESP32epdx/src/GUI/*.cpp" "pubsubclient/src/*.cpp", + "mpack/src/mpack/*.c" ) idf_component_register( SRCS "main.cpp" "EPD.cpp" "mesh_main.c" "mesh_netif.c" "tcphelper.c" ${MY_LIB_SOURCES} - INCLUDE_DIRS "" "ESP32epdx/src" "ESP32epdx/src/GUI" + INCLUDE_DIRS "" "ESP32epdx/src" "ESP32epdx/src/GUI" "mpack/src/mpack" ) diff --git a/main/main.cpp b/main/main.cpp index 965a0fe..7e219be 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1,8 +1,10 @@ -#include "Arduino.h" +#include "stdlib.h" +#include "Arduino.h" #include "nvs_flash.h" #include +#include "mpack.h" #include "ESP32epdx.h" #include "EPD.h" #include "mesh_netif.h" @@ -16,69 +18,165 @@ SPI: SCK = 6 ; MISO = 2 ; MOSI = 7 ; SS = 16 EPD: RES = 22 ; DC = 23 ; CS = 1 ; BUSY = 0 */ -#define IMAGE_BUF_SIZE 5624 uint8_t *image_buf = nullptr; -extern "C" int tcp_server_callback(char *buf, size_t len) { - if (!strncmp(buf, "ping", 4)) { // ping - return 0; - } else if (!strncmp(buf, "image", 5) && len == 5 + 5624) { // update image - if (epd_mtx.try_lock()) - { - // work - EPD_HW_Init_Fast(); - EPD_Display((unsigned char *)(buf+5)); - EPD_DeepSleep(); +typedef struct request_t { + // command : update_image, push_image, ping + char *command; + + // arguments for update_image + uint64_t index; + char *data; + size_t data_len; +} request_t; - //vTaskDelay(pdMS_TO_TICKS(1000)); - // unlock - epd_mtx.unlock(); +extern "C" request_t* parse_data(char *buf, size_t len) { + // variables + char *command = NULL; + uint64_t index = 0; + char *data = NULL; + size_t data_len = 0; - return 0; - } - else - { - // locked - return -2; + // init reader + mpack_reader_t reader; + mpack_reader_init_data(&reader, buf, len); + + size_t count = mpack_expect_map_max(&reader, 100); + if (mpack_reader_error(&reader) != mpack_ok) { + return NULL; } - } else { // invalid request - return -3; - } + + // read data + for (size_t i = 0; i < count; i++) { + char *key = mpack_expect_cstr_alloc(&reader, 100); + + 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) { + data = mpack_expect_bin_alloc(&reader, CONFIG_IMAGE_BUF_SLICE_SIZE, &data_len); + } else { + mpack_discard(&reader); + } + + free(key); + } + + // finish + mpack_done_map(&reader); + if (mpack_reader_destroy(&reader) != mpack_ok) { + return NULL; + } + + // return + request_t *result = (request_t *)malloc(sizeof(request_t)); + if (!result) { + return NULL; + } + memset(result, 0, sizeof(request_t)); + result->command = command; + result->index = index; + result->data = data; + result->data_len = data_len; + + return result; } -extern "C" void app_main() -{ - initArduino(); +extern "C" int command_update_image(uint64_t index, char *buf, size_t len) { + if (!epd_mtx.try_lock()) { + return -3; // locked + } + + if ( + index < 0 || index >= CONFIG_IMAGE_BUF_SIZE || + len < 0 || len > CONFIG_IMAGE_BUF_SIZE || + (index+len) < 0 || (index+len) > CONFIG_IMAGE_BUF_SIZE + ) + { + epd_mtx.unlock(); + return -2; + } - // init image_buf - image_buf = (uint8_t *)malloc(IMAGE_BUF_SIZE); - memset(image_buf, 0, IMAGE_BUF_SIZE); + memcpy(image_buf + index, buf, len); - //Initialize NVS - esp_err_t ret = nvs_flash_init(); - if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { - ESP_ERROR_CHECK(nvs_flash_erase()); - ret = nvs_flash_init(); - } - ESP_ERROR_CHECK(ret); + epd_mtx.unlock(); + return 0; +} - // Initialize EPD - EPD_Pin_Init(6, 2, 7, 16, - 22, 23, 1, 0); +extern "C" int command_push_image() { + if (epd_mtx.try_lock()) { + EPD_HW_Init_Fast(); + EPD_Display((unsigned char *)(image_buf)); + EPD_DeepSleep(); - mesh_main(); + epd_mtx.unlock(); + return 0; + } else { + return -3; + } +} - // tcp server - start_tcp_server(); +extern "C" int tcp_server_callback(char *buf, size_t len) { + request_t *req = parse_data(buf, len); + if (!req) { // error -> invalid data + return -2; + } - // Arduino-like setup() + // execute command + if (strcmp(req->command, "ping") == 0) + { + return 0; + } + else if (strcmp(req->command, "update_image") == 0) + { + return command_update_image(req->index, req->data, req->data_len); + } + else if (strcmp(req->command, "push_image") == 0) + { + return command_push_image(); + } + else + { + return -2; + } +} - // Arduino-like loop() - while (true) - { - vTaskDelay(500); - } +extern "C" void app_main() { + initArduino(); - // WARNING: if program reaches end of function app_main() the MCU will restart. + // init image_buf + image_buf = (uint8_t *)malloc(CONFIG_IMAGE_BUF_SIZE); + if (!image_buf) { + return; + } + memset(image_buf, 0, CONFIG_IMAGE_BUF_SIZE); + + // initialize NVS + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + // Initialize EPD + EPD_Pin_Init(6, 2, 7, 16, + 22, 23, 1, 0); + + mesh_main(); + + // tcp server + start_tcp_server(); + + // Arduino-like setup() + + // Arduino-like loop() + while (true) + { + vTaskDelay(500); + } + + // WARNING: if program reaches end of function app_main() the MCU will restart. } \ No newline at end of file diff --git a/main/main.h b/main/main.h index f543239..b332830 100644 --- a/main/main.h +++ b/main/main.h @@ -22,9 +22,12 @@ static const uint8_t MESH_ID[6] = { 0x77, 0x77, 0x77, 0x77, 0x77, 0x76 }; #define CONFIG_MESH_AP_CONNECTIONS 6 // number of nodes #define CONFIG_MESH_NON_MESH_AP_CONNECTIONS 0 // number of non-node devices +#define CONFIG_TCP_DEBUG 0 #define CONFIG_TCP_SERVER_BIND_ADDRESS "0.0.0.0" #define CONFIG_TCP_SERVER_BIND_PORT "8888" -#define CONFIG_TCP_SERVER_RXBUFFER_SIZE 8192 +#define CONFIG_TCP_SERVER_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 /******************************************************* * Function Declarations diff --git a/main/mesh_main.c b/main/mesh_main.c index 06701f0..0a4b4bc 100644 --- a/main/mesh_main.c +++ b/main/mesh_main.c @@ -320,9 +320,12 @@ void mesh_main(void) 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_TCP_DEBUG memcpy((uint8_t *) &cfg.router.password, CONFIG_MESH_ROUTER_PASSWD, strlen(CONFIG_MESH_ROUTER_PASSWD)); - //memset((uint8_t *) &cfg.router.password, 0, 64); +#else + memset((uint8_t *) &cfg.router.password, 0, 64); +#endif /* mesh softAP */ ESP_ERROR_CHECK(esp_mesh_set_ap_authmode(CONFIG_MESH_AP_AUTHMODE)); diff --git a/main/mpack b/main/mpack new file mode 160000 index 0000000..a94ff11 --- /dev/null +++ b/main/mpack @@ -0,0 +1 @@ +Subproject commit a94ff1129d41a470ad9425ce6f730b3d8e99983f diff --git a/main/tcphelper.c b/main/tcphelper.c index c364706..2ce1285 100644 --- a/main/tcphelper.c +++ b/main/tcphelper.c @@ -250,14 +250,11 @@ static void tcp_server_task(void *pvParameters) case 0: strcpy(tx_buffer, "ok\n"); break; - case -1: - strcpy(tx_buffer, "invalid data\n"); - break; case -2: - strcpy(tx_buffer, "locked\n"); + strcpy(tx_buffer, "invalid request\n"); break; case -3: - strcpy(tx_buffer, "invalid request\n"); + strcpy(tx_buffer, "locked\n"); break; default: strcpy(tx_buffer, "unknown error\n"); diff --git a/tcp_client.py b/tcp_client.py index ded8bc5..1d285c2 100644 --- a/tcp_client.py +++ b/tcp_client.py @@ -1,38 +1,76 @@ +from typing import Dict, Any import socket +import msgpack + import tcp_client_image SERVER_ADDRESS = ('172.16.0.249', 8888) -client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -client_socket.connect(SERVER_ADDRESS) +def send_request(data:Dict[str, Any]): + client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + client_socket.connect(SERVER_ADDRESS) + + try: + message:bytes = msgpack.packb(data) -try: - while True: - message = "" - choice = int(input("image > ")) - if choice == 1: - message = b"image" + bytes(tcp_client_image.image1) - elif choice == 2: - message = b"image" + bytes(tcp_client_image.image2) - elif choice == 3: - message = b"ping" - else: - message = b"something_invalid" - - # 4. 傳送資料 (需要編碼成 bytes) client_socket.sendall(message) - # 5. 接收伺服器的回應 - # 接收最多 1024 bytes - data = client_socket.recv(1024) + rx = client_socket.recv(1024) if data: - print(f"收到伺服器訊息: {data.decode('utf-8')}") + print(f"Received message: {rx.decode('utf-8').strip()}") + else: + print("Connection closed") + return + except ConnectionResetError: + print("Connection reset") + finally: + client_socket.close() + print("Connection closed") + +def image(): + image = b"" + try: + choice = int(input("image > ")) + if choice == 1: + image = tcp_client_image.image1 + elif choice == 2: + image = tcp_client_image.image2 + else: + raise ValueError + except: + print("invalid choice") + return + + chunk_size = 2048 + for i in range(0, len(image), chunk_size): + data = { + "command": "update_image", + "index": i, + "data": bytes(image[i:i+chunk_size]) + } + send_request(data) + + send_request({"command":"push_image"}) + +def main(): + while True: + print("Command:") + print("(1) update + push image") + print("(2) ping") + print("(3) invalid request") + print("(4) exit") + + choice:int = int(input("> ")) + data = {} + if choice == 1: + image() + elif choice == 2: + send_request({"command":"ping"}) + elif choice == 3: + send_request({"command":"invalid_command"}) else: - print("伺服器已關閉連線。") break -except ConnectionResetError: - print("連線被伺服器重置。") -finally: - client_socket.close() - print("客戶端連線已關閉。") \ No newline at end of file + +if __name__ == "__main__": + main() \ No newline at end of file