feat(tcp_server): receive image via TCP
This commit is contained in:
parent
197e165462
commit
080a1d81e2
12 changed files with 396 additions and 182 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -7,4 +7,5 @@ __pycache__
|
|||
.devcontainer
|
||||
.vscode
|
||||
|
||||
main/old
|
||||
main/old
|
||||
main/drop_mqtt
|
||||
|
|
@ -5,6 +5,6 @@ file(GLOB_RECURSE MY_LIB_SOURCES
|
|||
)
|
||||
|
||||
idf_component_register(
|
||||
SRCS "main.cpp" "EPD.cpp" "mesh_main.c" "mesh_netif.c" "mqtt_app.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"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "ESP32epdx.h"
|
||||
#include "EPD.h"
|
||||
#include "mesh_netif.h"
|
||||
#include "tcphelper.h"
|
||||
#include "main.h"
|
||||
|
||||
std::mutex epd_mtx;
|
||||
|
|
@ -15,30 +16,34 @@ SPI: SCK = 6 ; MISO = 2 ; MOSI = 7 ; SS = 16
|
|||
EPD: RES = 22 ; DC = 23 ; CS = 1 ; BUSY = 0
|
||||
*/
|
||||
|
||||
extern "C" void mqtt_event_data_callback_func(char* topic, int topic_len, char* data, int data_len)
|
||||
{
|
||||
if (strncmp(topic, APP_MQTT_TOPIC, topic_len))
|
||||
return;
|
||||
|
||||
// process
|
||||
if (data_len == 5624)
|
||||
{
|
||||
printf("TOPIC (len: %d) -> %.*s\nMESSAGE (len: %d)\n", topic_len, topic_len, topic, data_len);
|
||||
if (epd_mtx.try_lock())
|
||||
#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 *)data);
|
||||
EPD_Display((unsigned char *)(buf+5));
|
||||
EPD_DeepSleep();
|
||||
|
||||
//vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
|
||||
// unlock
|
||||
epd_mtx.unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// locked
|
||||
return;
|
||||
return -2;
|
||||
}
|
||||
} else { // invalid request
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -46,6 +51,10 @@ extern "C" void app_main()
|
|||
{
|
||||
initArduino();
|
||||
|
||||
// init image_buf
|
||||
image_buf = (uint8_t *)malloc(IMAGE_BUF_SIZE);
|
||||
memset(image_buf, 0, 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) {
|
||||
|
|
@ -60,11 +69,8 @@ extern "C" void app_main()
|
|||
|
||||
mesh_main();
|
||||
|
||||
// Connect to WiFi
|
||||
// connect_wifi();
|
||||
|
||||
// Connect to MQTT broker
|
||||
// connect_mqtt();
|
||||
// tcp server
|
||||
start_tcp_server();
|
||||
|
||||
// Arduino-like setup()
|
||||
|
||||
|
|
|
|||
15
main/main.h
15
main/main.h
|
|
@ -12,16 +12,19 @@
|
|||
#define CONFIG_MESH_CHANNEL 0
|
||||
|
||||
// change these
|
||||
static const uint8_t MESH_ID[6] = { 0x77, 0x77, 0x77, 0x77, 0x77, 0x77 };
|
||||
#define CONFIG_MESH_ROUTER_SSID "FBK_the_cutest_fox"
|
||||
#define CONFIG_MESH_ROUTER_PASSWD "zsfv3210"
|
||||
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_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 APP_MQTT_URI "mqtt://10.0.0.1:1883"
|
||||
#define APP_MQTT_TOPIC "adboard"
|
||||
#define CONFIG_TCP_SERVER_BIND_ADDRESS "0.0.0.0"
|
||||
#define CONFIG_TCP_SERVER_BIND_PORT "8888"
|
||||
#define CONFIG_TCP_SERVER_RXBUFFER_SIZE 8192
|
||||
|
||||
/*******************************************************
|
||||
* Function Declarations
|
||||
|
|
@ -31,7 +34,7 @@ static const uint8_t MESH_ID[6] = { 0x77, 0x77, 0x77, 0x77, 0x77, 0x77 };
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
void mqtt_event_data_callback_func(char* topic, int topic_len, char* data, int data_len);
|
||||
int tcp_server_callback(char *buf, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,9 +52,6 @@ static uint8_t s_mesh_tx_payload[CONFIG_MESH_ROUTE_TABLE_SIZE*6+1];
|
|||
/*******************************************************
|
||||
* Function Declarations
|
||||
*******************************************************/
|
||||
// interaction with public mqtt broker
|
||||
void mqtt_app_start(void);
|
||||
void mqtt_app_publish(char* topic, char *publish_string);
|
||||
|
||||
/*******************************************************
|
||||
* Function Definitions
|
||||
|
|
@ -88,52 +85,6 @@ void static recv_cb(mesh_addr_t *from, mesh_data_t *data)
|
|||
}
|
||||
}
|
||||
|
||||
void esp_mesh_mqtt_task(void *arg)
|
||||
{
|
||||
is_running = true;
|
||||
char *print;
|
||||
mesh_data_t data;
|
||||
esp_err_t err;
|
||||
mqtt_app_start();
|
||||
while (is_running) {
|
||||
//asprintf(&print, "layer:%d IP:" IPSTR, esp_mesh_get_layer(), IP2STR(&s_current_ip));
|
||||
//ESP_LOGI(MESH_TAG, "Tried to publish %s", print);
|
||||
//mqtt_app_publish("/topic/ip_mesh", print);
|
||||
//free(print);
|
||||
//if (esp_mesh_is_root()) {
|
||||
// esp_mesh_get_routing_table((mesh_addr_t *) &s_route_table,
|
||||
// CONFIG_MESH_ROUTE_TABLE_SIZE * 6, &s_route_table_size);
|
||||
// data.size = s_route_table_size * 6 + 1;
|
||||
// data.proto = MESH_PROTO_BIN;
|
||||
// data.tos = MESH_TOS_P2P;
|
||||
// s_mesh_tx_payload[0] = CMD_ROUTE_TABLE;
|
||||
// memcpy(s_mesh_tx_payload + 1, s_route_table, s_route_table_size*6);
|
||||
// data.data = s_mesh_tx_payload;
|
||||
// for (int i = 0; i < s_route_table_size; i++) {
|
||||
// err = esp_mesh_send(&s_route_table[i], &data, MESH_DATA_P2P, NULL, 0);
|
||||
// ESP_LOGI(MESH_TAG, "Sending routing table to [%d] "
|
||||
// MACSTR ": sent with err code: %d", i, MAC2STR(s_route_table[i].addr), err);
|
||||
// }
|
||||
//}
|
||||
vTaskDelay(2 * 1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
esp_err_t esp_mesh_comm_mqtt_task_start(void)
|
||||
{
|
||||
static bool is_comm_mqtt_task_started = false;
|
||||
|
||||
s_route_table_lock = xSemaphoreCreateMutex();
|
||||
|
||||
if (!is_comm_mqtt_task_started) {
|
||||
xTaskCreate(esp_mesh_mqtt_task, "mqtt task", 4096, NULL, 5, NULL);
|
||||
//xTaskCreate(check_button, "check button task", 3072, NULL, 5, NULL);
|
||||
is_comm_mqtt_task_started = true;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void mesh_event_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
|
|
@ -325,7 +276,6 @@ 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
|
||||
esp_mesh_comm_mqtt_task_start();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -369,11 +319,11 @@ 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);
|
||||
|
||||
memcpy((uint8_t *) &cfg.router.password, CONFIG_MESH_ROUTER_PASSWD,
|
||||
strlen(CONFIG_MESH_ROUTER_PASSWD));
|
||||
//memset((uint8_t *) &cfg.router.ssid, 0, 32);
|
||||
//memset((uint8_t *) &cfg.router.password, 0, 64);
|
||||
//cfg.router.ssid_len = 0;
|
||||
|
||||
/* mesh softAP */
|
||||
ESP_ERROR_CHECK(esp_mesh_set_ap_authmode(CONFIG_MESH_AP_AUTHMODE));
|
||||
cfg.mesh_ap.max_connection = CONFIG_MESH_AP_CONNECTIONS;
|
||||
|
|
|
|||
|
|
@ -1,83 +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.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_tls.h"
|
||||
|
||||
#include "mqtt_client.h"
|
||||
#include "main.h"
|
||||
|
||||
static const char *TAG = "mesh_mqtt";
|
||||
static esp_mqtt_client_handle_t s_client = NULL;
|
||||
|
||||
static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
|
||||
{
|
||||
switch (event->event_id) {
|
||||
case MQTT_EVENT_CONNECTED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
|
||||
if (esp_mqtt_client_subscribe(s_client, APP_MQTT_TOPIC, 0) < 0) {
|
||||
// Disconnect to retry the subscribe after auto-reconnect timeout
|
||||
esp_mqtt_client_disconnect(s_client);
|
||||
}
|
||||
break;
|
||||
case MQTT_EVENT_DISCONNECTED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_UNSUBSCRIBED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_PUBLISHED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_DATA:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
|
||||
mqtt_event_data_callback_func(event->topic, event->topic_len, event->data, event->data_len);
|
||||
//ESP_LOGI(TAG, "TOPIC=%.*s", event->topic_len, event->topic);
|
||||
//ESP_LOGI(TAG, "DATA=%.*s", event->data_len, event->data);
|
||||
break;
|
||||
case MQTT_EVENT_ERROR:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) {
|
||||
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRId32 "", base, event_id);
|
||||
mqtt_event_handler_cb(event_data);
|
||||
}
|
||||
|
||||
void mqtt_app_publish(char* topic, char *publish_string)
|
||||
{
|
||||
if (s_client) {
|
||||
int msg_id = esp_mqtt_client_publish(s_client, topic, publish_string, 0, 1, 0);
|
||||
ESP_LOGI(TAG, "sent publish returned msg_id=%d", msg_id);
|
||||
}
|
||||
}
|
||||
|
||||
void mqtt_app_start(void)
|
||||
{
|
||||
esp_mqtt_client_config_t mqtt_cfg = {
|
||||
.broker.address.uri = APP_MQTT_URI,
|
||||
.buffer.size = 8192
|
||||
};
|
||||
|
||||
s_client = esp_mqtt_client_init(&mqtt_cfg);
|
||||
esp_mqtt_client_register_event(s_client, ESP_EVENT_ANY_ID, mqtt_event_handler, s_client);
|
||||
esp_mqtt_client_start(s_client);
|
||||
}
|
||||
308
main/tcphelper.c
Normal file
308
main/tcphelper.c
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
/* 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 <string.h>
|
||||
#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 "tcphelper.h"
|
||||
#include "main.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
|
||||
*/
|
||||
static 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
|
||||
*/
|
||||
static 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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[CONFIG_TCP_SERVER_RXBUFFER_SIZE];
|
||||
static const char *TAG = "nonblocking-socket-server";
|
||||
SemaphoreHandle_t *server_ready = pvParameters;
|
||||
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; i<max_socks; ++i) {
|
||||
sock[i] = INVALID_SOCK;
|
||||
}
|
||||
|
||||
// Translating the hostname or a string representation of an IP to address_info
|
||||
int res = getaddrinfo(CONFIG_TCP_SERVER_BIND_ADDRESS, CONFIG_TCP_SERVER_BIND_PORT, &hints, &address_info);
|
||||
if (res != 0 || address_info == NULL) {
|
||||
ESP_LOGE(TAG, "couldn't get hostname for `%s` "
|
||||
"getaddrinfo() returns %d, addrinfo=%p", CONFIG_TCP_SERVER_BIND_ADDRESS, res, address_info);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Creating a listener socket
|
||||
listen_sock = socket(address_info->ai_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", CONFIG_TCP_SERVER_BIND_ADDRESS, CONFIG_TCP_SERVER_BIND_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");
|
||||
xSemaphoreGive(*server_ready);
|
||||
|
||||
// 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<max_socks; ++new_sock_index) {
|
||||
if (sock[new_sock_index] == INVALID_SOCK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We accept a new connection only if we have a free socket
|
||||
if (new_sock_index < max_socks) {
|
||||
// Try to accept a new connections
|
||||
sock[new_sock_index] = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
|
||||
|
||||
if (sock[new_sock_index] < 0) {
|
||||
if (errno == EWOULDBLOCK) { // The listener socket did not accepts any connection
|
||||
// continue to serve open connections and try to accept again upon the next iteration
|
||||
ESP_LOGV(TAG, "No pending connections...");
|
||||
} else {
|
||||
log_socket_error(TAG, listen_sock, errno, "Error when accepting connection");
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
// We have a new client connected -> 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<max_socks; ++i) {
|
||||
if (sock[i] != INVALID_SOCK) {
|
||||
|
||||
// This is an open socket -> try to serve it
|
||||
memset(rx_buffer, 0, sizeof(rx_buffer));
|
||||
int len = try_receive(TAG, sock[i], rx_buffer, sizeof(rx_buffer));
|
||||
if (len < 0) {
|
||||
// Error occurred within this client's socket -> close and mark invalid
|
||||
ESP_LOGI(TAG, "[sock=%d]: try_receive() returned %d -> closing the socket", sock[i], len);
|
||||
close(sock[i]);
|
||||
sock[i] = INVALID_SOCK;
|
||||
} else if (len > 0) {
|
||||
// Received some data -> echo back
|
||||
//ESP_LOGI(TAG, "[sock=%d]: Received %.*s", sock[i], len, rx_buffer);
|
||||
ESP_LOGI(TAG, "[sock=%d]: Received message (len=%d)", sock[i], len);
|
||||
|
||||
char tx_buffer[128] = {0};
|
||||
int result = tcp_server_callback(rx_buffer, len);
|
||||
switch (result) {
|
||||
case 0:
|
||||
strcpy(tx_buffer, "ok\n");
|
||||
break;
|
||||
case -1:
|
||||
strcpy(tx_buffer, "invalid data\n");
|
||||
break;
|
||||
case -2:
|
||||
strcpy(tx_buffer, "locked\n");
|
||||
break;
|
||||
case -3:
|
||||
strcpy(tx_buffer, "invalid request\n");
|
||||
break;
|
||||
default:
|
||||
strcpy(tx_buffer, "unknown error\n");
|
||||
break;
|
||||
}
|
||||
|
||||
len = socket_send(TAG, sock[i], tx_buffer, strlen(tx_buffer));
|
||||
if (len < 0) {
|
||||
// Error occurred on write to this socket -> close it and mark invalid
|
||||
ESP_LOGI(TAG, "[sock=%d]: socket_send() returned %d -> closing the socket", sock[i], len);
|
||||
close(sock[i]);
|
||||
sock[i] = INVALID_SOCK;
|
||||
} else {
|
||||
// Successfully echoed to this socket
|
||||
ESP_LOGI(TAG, "[sock=%d]: Written %.*s", sock[i], strlen(tx_buffer), tx_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
} // 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; i<max_socks; ++i) {
|
||||
if (sock[i] != INVALID_SOCK) {
|
||||
close(sock[i]);
|
||||
}
|
||||
}
|
||||
|
||||
free(address_info);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
|
||||
void start_tcp_server(void) {
|
||||
SemaphoreHandle_t server_ready = xSemaphoreCreateBinary();
|
||||
assert(server_ready);
|
||||
xTaskCreate(tcp_server_task, "tcp_server", 4096, &server_ready, 5, NULL);
|
||||
xSemaphoreTake(server_ready, portMAX_DELAY);
|
||||
vSemaphoreDelete(server_ready);
|
||||
}
|
||||
12
main/tcphelper.h
Normal file
12
main/tcphelper.h
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// functions
|
||||
void start_tcp_server(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
import time
|
||||
|
||||
import paho.mqtt.client as mqtt
|
||||
|
||||
import mqtt_client_image
|
||||
|
||||
BROKER = "127.0.0.1"
|
||||
PORT = 1883
|
||||
TOPIC = "worker/01"
|
||||
|
||||
client = mqtt.Client(client_id="python-publisher")
|
||||
client.connect(BROKER, PORT, 60)
|
||||
|
||||
while True:
|
||||
choice = int(input("image > "))
|
||||
if choice == 1:
|
||||
message = bytes(mqtt_client_image.image1)
|
||||
elif choice == 2:
|
||||
message = bytes(mqtt_client_image.image2)
|
||||
client.publish(TOPIC, message)
|
||||
print(f"send")
|
||||
|
|
@ -1754,7 +1754,7 @@ CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y
|
|||
CONFIG_LWIP_TIMERS_ONDEMAND=y
|
||||
CONFIG_LWIP_ND6=y
|
||||
# CONFIG_LWIP_FORCE_ROUTER_FORWARDING is not set
|
||||
CONFIG_LWIP_MAX_SOCKETS=10
|
||||
CONFIG_LWIP_MAX_SOCKETS=32
|
||||
# CONFIG_LWIP_USE_ONLY_LWIP_SELECT is not set
|
||||
# CONFIG_LWIP_SO_LINGER is not set
|
||||
CONFIG_LWIP_SO_REUSE=y
|
||||
|
|
|
|||
38
tcp_client.py
Normal file
38
tcp_client.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import socket
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
if data:
|
||||
print(f"收到伺服器訊息: {data.decode('utf-8')}")
|
||||
else:
|
||||
print("伺服器已關閉連線。")
|
||||
break
|
||||
except ConnectionResetError:
|
||||
print("連線被伺服器重置。")
|
||||
finally:
|
||||
client_socket.close()
|
||||
print("客戶端連線已關閉。")
|
||||
Loading…
Add table
Reference in a new issue