feat(tcp_server): schema in message pack and more command
This commit is contained in:
parent
080a1d81e2
commit
1428ec2d11
7 changed files with 225 additions and 84 deletions
|
|
@ -2,9 +2,10 @@ file(GLOB_RECURSE MY_LIB_SOURCES
|
||||||
"ESP32epdx/src/*.cpp"
|
"ESP32epdx/src/*.cpp"
|
||||||
"ESP32epdx/src/GUI/*.cpp"
|
"ESP32epdx/src/GUI/*.cpp"
|
||||||
"pubsubclient/src/*.cpp",
|
"pubsubclient/src/*.cpp",
|
||||||
|
"mpack/src/mpack/*.c"
|
||||||
)
|
)
|
||||||
|
|
||||||
idf_component_register(
|
idf_component_register(
|
||||||
SRCS "main.cpp" "EPD.cpp" "mesh_main.c" "mesh_netif.c" "tcphelper.c" ${MY_LIB_SOURCES}
|
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"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
190
main/main.cpp
190
main/main.cpp
|
|
@ -1,8 +1,10 @@
|
||||||
#include "Arduino.h"
|
#include "stdlib.h"
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
#include "nvs_flash.h"
|
#include "nvs_flash.h"
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "mpack.h"
|
||||||
#include "ESP32epdx.h"
|
#include "ESP32epdx.h"
|
||||||
#include "EPD.h"
|
#include "EPD.h"
|
||||||
#include "mesh_netif.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
|
EPD: RES = 22 ; DC = 23 ; CS = 1 ; BUSY = 0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define IMAGE_BUF_SIZE 5624
|
|
||||||
uint8_t *image_buf = nullptr;
|
uint8_t *image_buf = nullptr;
|
||||||
|
|
||||||
extern "C" int tcp_server_callback(char *buf, size_t len) {
|
typedef struct request_t {
|
||||||
if (!strncmp(buf, "ping", 4)) { // ping
|
// command : update_image, push_image, ping
|
||||||
return 0;
|
char *command;
|
||||||
} else if (!strncmp(buf, "image", 5) && len == 5 + 5624) { // update image
|
|
||||||
if (epd_mtx.try_lock())
|
// arguments for update_image
|
||||||
|
uint64_t index;
|
||||||
|
char *data;
|
||||||
|
size_t data_len;
|
||||||
|
} request_t;
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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" 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
|
||||||
|
)
|
||||||
{
|
{
|
||||||
// work
|
epd_mtx.unlock();
|
||||||
EPD_HW_Init_Fast();
|
return -2;
|
||||||
EPD_Display((unsigned char *)(buf+5));
|
}
|
||||||
EPD_DeepSleep();
|
|
||||||
|
|
||||||
//vTaskDelay(pdMS_TO_TICKS(1000));
|
memcpy(image_buf + index, buf, len);
|
||||||
|
|
||||||
// unlock
|
epd_mtx.unlock();
|
||||||
epd_mtx.unlock();
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
extern "C" int command_push_image() {
|
||||||
|
if (epd_mtx.try_lock()) {
|
||||||
|
EPD_HW_Init_Fast();
|
||||||
|
EPD_Display((unsigned char *)(image_buf));
|
||||||
|
EPD_DeepSleep();
|
||||||
|
|
||||||
|
epd_mtx.unlock();
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
else
|
||||||
{
|
{
|
||||||
// locked
|
return -2;
|
||||||
return -2;
|
|
||||||
}
|
}
|
||||||
} else { // invalid request
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void app_main()
|
extern "C" void app_main() {
|
||||||
{
|
initArduino();
|
||||||
initArduino();
|
|
||||||
|
|
||||||
// init image_buf
|
// init image_buf
|
||||||
image_buf = (uint8_t *)malloc(IMAGE_BUF_SIZE);
|
image_buf = (uint8_t *)malloc(CONFIG_IMAGE_BUF_SIZE);
|
||||||
memset(image_buf, 0, IMAGE_BUF_SIZE);
|
if (!image_buf) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memset(image_buf, 0, CONFIG_IMAGE_BUF_SIZE);
|
||||||
|
|
||||||
//Initialize NVS
|
// initialize NVS
|
||||||
esp_err_t ret = nvs_flash_init();
|
esp_err_t ret = nvs_flash_init();
|
||||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||||
ret = nvs_flash_init();
|
ret = nvs_flash_init();
|
||||||
}
|
}
|
||||||
ESP_ERROR_CHECK(ret);
|
ESP_ERROR_CHECK(ret);
|
||||||
|
|
||||||
// Initialize EPD
|
// Initialize EPD
|
||||||
EPD_Pin_Init(6, 2, 7, 16,
|
EPD_Pin_Init(6, 2, 7, 16,
|
||||||
22, 23, 1, 0);
|
22, 23, 1, 0);
|
||||||
|
|
||||||
mesh_main();
|
mesh_main();
|
||||||
|
|
||||||
// tcp server
|
// tcp server
|
||||||
start_tcp_server();
|
start_tcp_server();
|
||||||
|
|
||||||
// Arduino-like setup()
|
// Arduino-like setup()
|
||||||
|
|
||||||
// Arduino-like loop()
|
// Arduino-like loop()
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
vTaskDelay(500);
|
vTaskDelay(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARNING: if program reaches end of function app_main() the MCU will restart.
|
// WARNING: if program reaches end of function app_main() the MCU will restart.
|
||||||
}
|
}
|
||||||
|
|
@ -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_AP_CONNECTIONS 6 // number of nodes
|
||||||
#define CONFIG_MESH_NON_MESH_AP_CONNECTIONS 0 // number of non-node devices
|
#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_ADDRESS "0.0.0.0"
|
||||||
#define CONFIG_TCP_SERVER_BIND_PORT "8888"
|
#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
|
* Function Declarations
|
||||||
|
|
|
||||||
|
|
@ -320,9 +320,12 @@ void mesh_main(void)
|
||||||
cfg.router.ssid_len = strlen(CONFIG_MESH_ROUTER_SSID);
|
cfg.router.ssid_len = strlen(CONFIG_MESH_ROUTER_SSID);
|
||||||
memcpy((uint8_t *) &cfg.router.ssid, CONFIG_MESH_ROUTER_SSID, cfg.router.ssid_len);
|
memcpy((uint8_t *) &cfg.router.ssid, CONFIG_MESH_ROUTER_SSID, cfg.router.ssid_len);
|
||||||
|
|
||||||
|
#if CONFIG_TCP_DEBUG
|
||||||
memcpy((uint8_t *) &cfg.router.password, CONFIG_MESH_ROUTER_PASSWD,
|
memcpy((uint8_t *) &cfg.router.password, CONFIG_MESH_ROUTER_PASSWD,
|
||||||
strlen(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 */
|
/* mesh softAP */
|
||||||
ESP_ERROR_CHECK(esp_mesh_set_ap_authmode(CONFIG_MESH_AP_AUTHMODE));
|
ESP_ERROR_CHECK(esp_mesh_set_ap_authmode(CONFIG_MESH_AP_AUTHMODE));
|
||||||
|
|
|
||||||
1
main/mpack
Submodule
1
main/mpack
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit a94ff1129d41a470ad9425ce6f730b3d8e99983f
|
||||||
|
|
@ -250,14 +250,11 @@ static void tcp_server_task(void *pvParameters)
|
||||||
case 0:
|
case 0:
|
||||||
strcpy(tx_buffer, "ok\n");
|
strcpy(tx_buffer, "ok\n");
|
||||||
break;
|
break;
|
||||||
case -1:
|
|
||||||
strcpy(tx_buffer, "invalid data\n");
|
|
||||||
break;
|
|
||||||
case -2:
|
case -2:
|
||||||
strcpy(tx_buffer, "locked\n");
|
strcpy(tx_buffer, "invalid request\n");
|
||||||
break;
|
break;
|
||||||
case -3:
|
case -3:
|
||||||
strcpy(tx_buffer, "invalid request\n");
|
strcpy(tx_buffer, "locked\n");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
strcpy(tx_buffer, "unknown error\n");
|
strcpy(tx_buffer, "unknown error\n");
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,76 @@
|
||||||
|
from typing import Dict, Any
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
|
import msgpack
|
||||||
|
|
||||||
import tcp_client_image
|
import tcp_client_image
|
||||||
|
|
||||||
SERVER_ADDRESS = ('172.16.0.249', 8888)
|
SERVER_ADDRESS = ('172.16.0.249', 8888)
|
||||||
|
|
||||||
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
def send_request(data:Dict[str, Any]):
|
||||||
client_socket.connect(SERVER_ADDRESS)
|
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
client_socket.connect(SERVER_ADDRESS)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while True:
|
message:bytes = msgpack.packb(data)
|
||||||
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)
|
client_socket.sendall(message)
|
||||||
|
|
||||||
# 5. 接收伺服器的回應
|
rx = client_socket.recv(1024)
|
||||||
# 接收最多 1024 bytes
|
|
||||||
data = client_socket.recv(1024)
|
|
||||||
if data:
|
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:
|
else:
|
||||||
print("伺服器已關閉連線。")
|
|
||||||
break
|
break
|
||||||
except ConnectionResetError:
|
|
||||||
print("連線被伺服器重置。")
|
if __name__ == "__main__":
|
||||||
finally:
|
main()
|
||||||
client_socket.close()
|
|
||||||
print("客戶端連線已關閉。")
|
|
||||||
Loading…
Add table
Reference in a new issue