diff --git a/examples/protocols/asio/chat_client/CMakeLists.txt b/examples/protocols/asio/asio_chat/CMakeLists.txt similarity index 94% rename from examples/protocols/asio/chat_client/CMakeLists.txt rename to examples/protocols/asio/asio_chat/CMakeLists.txt index ddcc068ba8..483b43d703 100644 --- a/examples/protocols/asio/chat_client/CMakeLists.txt +++ b/examples/protocols/asio/asio_chat/CMakeLists.txt @@ -7,4 +7,4 @@ cmake_minimum_required(VERSION 3.5) set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(asio_chat_client) +project(asio_chat) diff --git a/examples/protocols/asio/chat_server/Makefile b/examples/protocols/asio/asio_chat/Makefile similarity index 87% rename from examples/protocols/asio/chat_server/Makefile rename to examples/protocols/asio/asio_chat/Makefile index a7656889fd..93950cb9a9 100644 --- a/examples/protocols/asio/chat_server/Makefile +++ b/examples/protocols/asio/asio_chat/Makefile @@ -2,7 +2,7 @@ # This is a project Makefile. It is assumed the directory this Makefile resides in is a # project subdirectory. # -PROJECT_NAME := asio_chat_server +PROJECT_NAME := asio_chat EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/common_components/protocol_examples_common diff --git a/examples/protocols/asio/asio_chat/README.md b/examples/protocols/asio/asio_chat/README.md new file mode 100644 index 0000000000..4848b457b8 --- /dev/null +++ b/examples/protocols/asio/asio_chat/README.md @@ -0,0 +1,62 @@ + +# Asio chat client and server examples + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +The application aims to demonstrate a simple use of Asio library in different modes. +In project settings it could be configured to run either a Asio chat server, a Asio chat client, or both. + +## How to use example + +The example is configured by default as an Asio chat client. + +Note that the example uses string representation of IP addresses and ports. + +You can find the upstream asio chat implementation [here] https://github.com/chriskohlhoff/asio/tree/master/asio/src/examples/cpp11/chat + +### Asio Client + +In the client mode, the example connects to the configured address, sends the message, which was inserted as an input in the terminal, and receives a response. + +### Asio Server + +In the server mode, Asio chat server with a specified port number is created and being polled till a connection request from the client arrives. +Chat server echoes a message (received from any client) to all connected clients. + +## Configure the project + +``` +idf.py menuconfig +``` + +Set following parameters under Example Configuration Options: + +* Set `EXAMPLE_CHAT_SERVER` to use the example as an ASIO chat server + * Configure `EXAMPLE_CHAT_SERVER_BIND_PORT` to the port number. + +* Set `EXAMPLE_CHAT_CLIENT` to use the example as an ASIO chat client + * Configure `EXAMPLE_CHAT_CLIENT_CONNECT_ADDRESS` to a string representation of the address to connect the client to. + * Configure `EXAMPLE_CHAT_CLIENT_CONNECT_PORT` to the port number. + +* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more d etails. + +## Running the example in server mode + +- Configure the example according "Configure the project" section. +- Run `idf.py -p PORT flash monitor` to build and upload the example to your board and connect to it's serial terminal. +- Wait for the board to connect to WiFi or Ethernet (note the IP address). +- Connect to the server using multiple clients, for example using any option below. + - build and run asio chat client on your host machine + - run chat_client asio example on ESP platform + - since chat messages consists of ASCII size and message, it is possible to + netcat `nc IP PORT` and type for example ` 4ABC` to transmit 'ABC\n' + +## Running the example in client mode + +- Configure the example according "Configure the project" section. +- Start chat server either on host machine or as another ESP device running chat_server example. +- Run `idf.py -p PORT flash monitor` to build and upload the example to your board and connect to it's serial terminal. +- Wait for the board to connect to WiFi or Ethernet. +- Receive and send messages to/from other clients on stdin/stdout via serial terminal. + +See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/protocols/asio/asio_chat/example_test.py b/examples/protocols/asio/asio_chat/example_test.py new file mode 100644 index 0000000000..7dc5c7c2b7 --- /dev/null +++ b/examples/protocols/asio/asio_chat/example_test.py @@ -0,0 +1,29 @@ +# 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. + +# -*- coding: utf-8 -*- + +from __future__ import print_function, unicode_literals + +import re + +import ttfw_idf + + +@ttfw_idf.idf_example_test(env_tag='Example_GENERIC') +def test_examples_asio_chat(env, _): # type: (ttfw_idf.TinyFW.Env, None) -> None + msg = 'asio-chat: received hi' + dut = env.get_dut('asio_chat', 'examples/protocols/asio/asio_chat') + # start the test and expect the client to receive back it's original data + dut.start_app() + dut.expect(re.compile(r'{}'.format('Waiting for input')), timeout=30) + dut.write(msg) + dut.write('exit') + dut.expect(re.compile(r'{}'.format(msg)), timeout=30) + + +if __name__ == '__main__': + test_examples_asio_chat() diff --git a/examples/protocols/asio/asio_chat/main/CMakeLists.txt b/examples/protocols/asio/asio_chat/main/CMakeLists.txt new file mode 100644 index 0000000000..8f19544237 --- /dev/null +++ b/examples/protocols/asio/asio_chat/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "asio_chat.cpp" + INCLUDE_DIRS ".") diff --git a/examples/protocols/asio/asio_chat/main/Kconfig.projbuild b/examples/protocols/asio/asio_chat/main/Kconfig.projbuild new file mode 100644 index 0000000000..99f08cdbd3 --- /dev/null +++ b/examples/protocols/asio/asio_chat/main/Kconfig.projbuild @@ -0,0 +1,39 @@ +menu "Example Configuration" + + config EXAMPLE_CHAT_SERVER + bool "Asio example chat server" + default n + help + This example will setup a chat server, binds it to the specified address + and starts listening. + + if EXAMPLE_CHAT_SERVER + config EXAMPLE_CHAT_SERVER_BIND_PORT + string "Asio example server bind port" + default "3344" + help + Server listener's socket would be bound to this port. + endif + + config EXAMPLE_CHAT_CLIENT + bool "Asio example chat client" + default y + help + This example will setup an asio chat client. + and sends the data. + + if EXAMPLE_CHAT_CLIENT + config EXAMPLE_CHAT_CLIENT_CONNECT_ADDRESS + string "Client connection address" + default "192.168.0.1" + help + Client's socket would connect to this address/host. + + config EXAMPLE_CHAT_CLIENT_CONNECT_PORT + string "Client connection port" + default "3344" + help + Client's connection port. + endif + +endmenu diff --git a/examples/protocols/asio/asio_chat/main/asio_chat.cpp b/examples/protocols/asio/asio_chat/main/asio_chat.cpp new file mode 100644 index 0000000000..be2329d151 --- /dev/null +++ b/examples/protocols/asio/asio_chat/main/asio_chat.cpp @@ -0,0 +1,119 @@ +/* ASIO chat server client 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 "protocol_examples_common.h" +#include "esp_log.h" +#include "esp_event.h" +#include "nvs_flash.h" +#include "server.hpp" +#include "client.hpp" +#include +#include + +using asio::ip::tcp; + +static const char *TAG = "asio-chat"; + +// This variable is necessary for `python test` execution, it provides synchronisation between server/client(as server should be started before client) +std::mutex server_ready; + +#ifdef CONFIG_EXAMPLE_CHAT_CLIENT +static void get_string(char *line, size_t size) +{ + int count = 0; + while (count < size) { + int c = fgetc(stdin); + if (c == '\n') { + line[count] = '\0'; + break; + } else if (c > 0 && c < 127) { + line[count] = c; + ++count; + } + vTaskDelay(10 / portTICK_PERIOD_MS); + } +} + +void start_client(void) +{ + const std::string port(CONFIG_EXAMPLE_CHAT_CLIENT_CONNECT_PORT); + const std::string name(CONFIG_EXAMPLE_CHAT_CLIENT_CONNECT_ADDRESS); + asio::io_context io_context; + char line[128]; + + tcp::resolver resolver(io_context); + auto endpoints = resolver.resolve(name, port); + chat_client c(io_context, endpoints); +#ifdef CONFIG_EXAMPLE_CHAT_SERVER + std::lock_guard guard(server_ready); +#endif + std::thread t([&io_context]() { try { + io_context.run(); + } catch (const std::exception &e) { + ESP_LOGE(TAG, "Exception occured during client thread execution %s", e.what()); + } + catch (...) { + ESP_LOGE(TAG, "Unknown exception"); + }}); + do { + ESP_LOGI(TAG, "CLIENT: Waiting for input"); + get_string(line, sizeof(line)); + + chat_message msg; + msg.body_length(std::strlen(line)); + std::memcpy(msg.body(), line, msg.body_length()); + msg.encode_header(); + c.write(msg); + sleep(1); + } while (strcmp(line, "exit") != 0); + + c.close(); + t.join(); +} +#endif // CONFIG_EXAMPLE_CHAT_CLIENT + +extern "C" void app_main(void) +{ + ESP_ERROR_CHECK(nvs_flash_init()); + esp_netif_init(); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. + * Read "Establishing Wi-Fi or Ethernet Connection" section in + * examples/protocols/README.md for more information about this function. + */ + ESP_ERROR_CHECK(example_connect()); + + try { +#ifdef CONFIG_EXAMPLE_CHAT_SERVER + asio::io_context io_context; + chat_server server(io_context, tcp::endpoint(tcp::v4(), std::atoi(CONFIG_EXAMPLE_CHAT_SERVER_BIND_PORT))); + std::thread t = std::thread([&io_context]() { // Chat server starting here + try { + io_context.run(); + } catch (const std::exception &e) { + ESP_LOGE(TAG, "Exception occured during server thread execution %s", e.what()); + } + catch (...) { + ESP_LOGE(TAG, "Unknown exception"); + }});; +#endif +#ifdef CONFIG_EXAMPLE_CHAT_CLIENT + start_client(); +#endif +#ifdef CONFIG_EXAMPLE_CHAT_SERVER + t.join(); +#endif + } catch (const std::exception &e) { + ESP_LOGE(TAG, "Exception occured during run %s", e.what()); + } catch (...) { + ESP_LOGE(TAG, "Unknown exception"); + } + ESP_ERROR_CHECK(example_disconnect()); +} diff --git a/examples/protocols/asio/chat_client/main/chat_message.hpp b/examples/protocols/asio/asio_chat/main/chat_message.hpp similarity index 100% rename from examples/protocols/asio/chat_client/main/chat_message.hpp rename to examples/protocols/asio/asio_chat/main/chat_message.hpp diff --git a/examples/protocols/asio/asio_chat/main/client.hpp b/examples/protocols/asio/asio_chat/main/client.hpp new file mode 100644 index 0000000000..09baece986 --- /dev/null +++ b/examples/protocols/asio/asio_chat/main/client.hpp @@ -0,0 +1,126 @@ +// +// client.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef CHAT_CLIENT_HPP +#define CHAT_CLIENT_HPP + +#include +#include "asio.hpp" +#include "chat_message.hpp" + +typedef std::deque chat_message_queue; + +class chat_client +{ +public: + chat_client(asio::io_context& io_context, + const asio::ip::tcp::resolver::results_type& endpoints) + : io_context_(io_context), + socket_(io_context) + { + do_connect(endpoints); + } + + void write(const chat_message& msg) + { + asio::post(io_context_, + [this, msg]() + { + bool write_in_progress = !write_msgs_.empty(); + write_msgs_.push_back(msg); + if (!write_in_progress) + { + do_write(); + } + }); + } + + void close() + { + asio::post(io_context_, [this]() { socket_.close(); }); + } + +private: +void do_connect(const asio::ip::tcp::resolver::results_type& endpoints) + { + asio::async_connect(socket_, endpoints, + [this](std::error_code ec, asio::ip::tcp::endpoint) + { + if (!ec) + { + do_read_header(); + } + }); + } + + void do_read_header() + { + asio::async_read(socket_, + asio::buffer(read_msg_.data(), chat_message::header_length), + [this](std::error_code ec, std::size_t /*length*/) + { + if (!ec && read_msg_.decode_header()) + { + do_read_body(); + } + else + { + socket_.close(); + } + }); + } + + void do_read_body() + { + asio::async_read(socket_, + asio::buffer(read_msg_.body(), read_msg_.body_length()), + [this](std::error_code ec, std::size_t /*length*/) + { + if (!ec) + { + do_read_header(); + } + else + { + socket_.close(); + } + }); + } + + void do_write() + { + asio::async_write(socket_, + asio::buffer(write_msgs_.front().data(), + write_msgs_.front().length()), + [this](std::error_code ec, std::size_t /*length*/) + { + if (!ec) + { + write_msgs_.pop_front(); + if (!write_msgs_.empty()) + { + do_write(); + } + } + else + { + socket_.close(); + } + }); + } + +private: + asio::io_context& io_context_; + asio::ip::tcp::socket socket_; + chat_message read_msg_; + chat_message_queue write_msgs_; +}; + +#endif // CHAT_CLIENT_HPP diff --git a/examples/protocols/asio/chat_client/main/component.mk b/examples/protocols/asio/asio_chat/main/component.mk similarity index 100% rename from examples/protocols/asio/chat_client/main/component.mk rename to examples/protocols/asio/asio_chat/main/component.mk diff --git a/examples/protocols/asio/chat_server/main/chat_server.cpp b/examples/protocols/asio/asio_chat/main/server.hpp similarity index 52% rename from examples/protocols/asio/chat_server/main/chat_server.cpp rename to examples/protocols/asio/asio_chat/main/server.hpp index 6ea7c7be47..450e255a47 100644 --- a/examples/protocols/asio/chat_server/main/chat_server.cpp +++ b/examples/protocols/asio/asio_chat/main/server.hpp @@ -1,5 +1,5 @@ // -// chat_server.cpp +// server.hpp // ~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) @@ -8,28 +8,25 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include -#include -#include +#ifndef CHAT_SERVER_HPP +#define CHAT_SERVER_HPP + #include -#include #include +#include #include #include "asio.hpp" #include "chat_message.hpp" -#include "protocol_examples_common.h" -#include "esp_event.h" -#include "nvs_flash.h" - - -using asio::ip::tcp; //---------------------------------------------------------------------- typedef std::deque chat_message_queue; +extern std::mutex server_ready; + //---------------------------------------------------------------------- + class chat_participant { public: @@ -74,12 +71,13 @@ private: //---------------------------------------------------------------------- + class chat_session : public chat_participant, public std::enable_shared_from_this { public: - chat_session(tcp::socket socket, chat_room& room) + chat_session(asio::ip::tcp::socket socket, chat_room& room) : socket_(std::move(socket)), room_(room) { @@ -117,56 +115,57 @@ private: { room_.leave(shared_from_this()); } - }); - } + }); + } - void do_read_body() - { - auto self(shared_from_this()); - asio::async_read(socket_, - asio::buffer(read_msg_.body(), read_msg_.body_length()), - [this, self](std::error_code ec, std::size_t /*length*/) - { - if (!ec) - { - room_.deliver(read_msg_); - do_read_header(); - } - else - { - room_.leave(shared_from_this()); - } - }); - } + void do_read_body() + { + auto self(shared_from_this()); + asio::async_read(socket_, + asio::buffer(read_msg_.body(), read_msg_.body_length()), + [this, self](std::error_code ec, std::size_t /*length*/) + { + if (!ec) + { + ESP_LOGD("asio-chat:", "%s", read_msg_.body()); + room_.deliver(read_msg_); + do_read_header(); + } + else + { + room_.leave(shared_from_this()); + } + }); + } - void do_write() - { - auto self(shared_from_this()); - asio::async_write(socket_, - asio::buffer(write_msgs_.front().data(), - write_msgs_.front().length()), - [this, self](std::error_code ec, std::size_t /*length*/) - { - if (!ec) - { - write_msgs_.pop_front(); - if (!write_msgs_.empty()) - { - do_write(); - } - } - else - { - room_.leave(shared_from_this()); - } - }); - } + void do_write() + { + auto self(shared_from_this()); + asio::async_write(socket_, + asio::buffer(write_msgs_.front().data(), + write_msgs_.front().length()), + [this, self](std::error_code ec, std::size_t /*length*/) + { + if (!ec) + { + write_msgs_.pop_front(); + if (!write_msgs_.empty()) + { + do_write(); + } + } + else + { + room_.leave(shared_from_this()); + } + }); + } - tcp::socket socket_; - chat_room& room_; - chat_message read_msg_; - chat_message_queue write_msgs_; -}; + asio::ip::tcp::socket socket_; + chat_room& room_; + chat_message read_msg_; + chat_message_queue write_msgs_; + }; //---------------------------------------------------------------------- @@ -174,7 +173,7 @@ class chat_server { public: chat_server(asio::io_context& io_context, - const tcp::endpoint& endpoint) + const asio::ip::tcp::endpoint& endpoint) : acceptor_(io_context, endpoint) { do_accept(); @@ -183,49 +182,21 @@ public: private: void do_accept() { + std::lock_guard guard(server_ready); acceptor_.async_accept( - [this](std::error_code ec, tcp::socket socket) - { - if (!ec) - { - std::make_shared(std::move(socket), room_)->start(); - } + [this](std::error_code ec, asio::ip::tcp::socket socket) + { + if (!ec) + { + std::make_shared(std::move(socket), room_)->start(); + } - do_accept(); - }); + do_accept(); + }); } - tcp::acceptor acceptor_; + asio::ip::tcp::acceptor acceptor_; chat_room room_; }; -//---------------------------------------------------------------------- - -extern "C" void app_main(void) -{ - ESP_ERROR_CHECK(nvs_flash_init()); - esp_netif_init(); - ESP_ERROR_CHECK(esp_event_loop_create_default()); - - /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. - * Read "Establishing Wi-Fi or Ethernet Connection" section in - * examples/protocols/README.md for more information about this function. - */ - ESP_ERROR_CHECK(example_connect()); - - /* This helper function configures blocking UART I/O */ - ESP_ERROR_CHECK(example_configure_stdin_stdout()); - - asio::io_context io_context; - - std::list servers; - - { - tcp::endpoint endpoint(tcp::v4(), std::atoi(CONFIG_EXAMPLE_PORT)); - servers.emplace_back(io_context, endpoint); - } - - std::cout << "ASIO engine is up and running" << std::endl; - - io_context.run(); -} +#endif // CHAT_SERVER_HPP diff --git a/examples/protocols/asio/asio_chat/sdkconfig.ci b/examples/protocols/asio/asio_chat/sdkconfig.ci new file mode 100644 index 0000000000..4829992d03 --- /dev/null +++ b/examples/protocols/asio/asio_chat/sdkconfig.ci @@ -0,0 +1,6 @@ +CONFIG_EXAMPLE_CONNECT_WIFI=n +CONFIG_EXAMPLE_CONNECT_ETHERNET=n +CONFIG_EXAMPLE_CHAT_CLIENT=y +CONFIG_EXAMPLE_CHAT_SERVER=y +CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y +CONFIG_EXAMPLE_CHAT_CLIENT_CONNECT_ADDRESS="localhost" diff --git a/examples/protocols/asio/asio_chat/sdkconfig.defaults b/examples/protocols/asio/asio_chat/sdkconfig.defaults new file mode 100644 index 0000000000..3a66e33fd0 --- /dev/null +++ b/examples/protocols/asio/asio_chat/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192 +CONFIG_COMPILER_CXX_EXCEPTIONS=y diff --git a/examples/protocols/asio/chat_client/Makefile b/examples/protocols/asio/chat_client/Makefile deleted file mode 100644 index 90197676aa..0000000000 --- a/examples/protocols/asio/chat_client/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# -# This is a project Makefile. It is assumed the directory this Makefile resides in is a -# project subdirectory. -# -PROJECT_NAME := asio_chat_client - -EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/common_components/protocol_examples_common - -include $(IDF_PATH)/make/project.mk diff --git a/examples/protocols/asio/chat_client/README.md b/examples/protocols/asio/chat_client/README.md deleted file mode 100644 index bcc2bd4d5e..0000000000 --- a/examples/protocols/asio/chat_client/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Asio chat client example - -Simple Asio chat client using WiFi STA or Ethernet. - -## Example workflow - -- Wi-Fi or Ethernet connection is established, and IP address is obtained. -- Asio chat client connects to the corresponding server whose port number and IP are defined through the project configuration menu. -- Chat client receives all messages from other chat clients, also it sends message received from stdin using `idf.py -p PORT monitor`. - -## Running the example - -- Open the project configuration menu (`idf.py menuconfig`) to configure Wi-Fi or Ethernet. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details. -- Set server IP address and port number in menuconfig, "Example configuration". -- Start chat server either on host machine or as another ESP device running chat_server example. -- Run `idf.py -p PORT flash monitor` to build and upload the example to your board and connect to it's serial terminal. -- Wait for the board to connect to WiFi or Ethernet. -- Receive and send messages to/from other clients on stdin/stdout via serial terminal. - -See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/protocols/asio/chat_client/asio_chat_client_test.py b/examples/protocols/asio/chat_client/asio_chat_client_test.py deleted file mode 100644 index eab7254332..0000000000 --- a/examples/protocols/asio/chat_client/asio_chat_client_test.py +++ /dev/null @@ -1,94 +0,0 @@ -import os -import re -import socket -import time -from threading import Thread - -import ttfw_idf - -global g_client_response -global g_msg_to_client - -g_client_response = b'' -g_msg_to_client = b' 3XYZ' - - -def get_my_ip(): - s1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - s1.connect(('8.8.8.8', 80)) - my_ip = s1.getsockname()[0] - s1.close() - return my_ip - - -def chat_server_sketch(my_ip): - global g_client_response - print('Starting the server on {}'.format(my_ip)) - port = 2222 - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.settimeout(600) - s.bind((my_ip, port)) - s.listen(1) - q,addr = s.accept() - print('connection accepted') - q.settimeout(30) - q.send(g_msg_to_client) - data = q.recv(1024) - # check if received initial empty message - if (len(data) > 4): - g_client_response = data - else: - g_client_response = q.recv(1024) - print('received from client {}'.format(g_client_response)) - s.close() - print('server closed') - - -@ttfw_idf.idf_example_test(env_tag='Example_WIFI_Protocols') -def test_examples_protocol_asio_chat_client(env, extra_data): - """ - steps: | - 1. Test to start simple tcp server - 2. `dut1` joins AP - 3. Test injects server IP to `dut1`via stdin - 4. Test evaluates `dut1` receives a message server placed - 5. Test injects a message to `dut1` to be sent as chat_client message - 6. Test evaluates received test message in host server - """ - global g_client_response - global g_msg_to_client - test_msg = 'ABC' - dut1 = env.get_dut('chat_client', 'examples/protocols/asio/chat_client', dut_class=ttfw_idf.ESP32DUT) - # check and log bin size - binary_file = os.path.join(dut1.app.binary_path, 'asio_chat_client.bin') - bin_size = os.path.getsize(binary_file) - ttfw_idf.log_performance('asio_chat_client_size', '{}KB'.format(bin_size // 1024)) - # 1. start a tcp server on the host - host_ip = get_my_ip() - thread1 = Thread(target=chat_server_sketch, args=(host_ip,)) - thread1.start() - # 2. start the dut test and wait till client gets IP address - dut1.start_app() - dut1.expect(re.compile(r' IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)'), timeout=30) - # 3. send host's IP to the client i.e. the `dut1` - dut1.write(host_ip) - # 4. client `dut1` should receive a message - dut1.expect(g_msg_to_client[4:].decode()) # Strip out the front 4 bytes of message len (see chat_message protocol) - # 5. write test message from `dut1` chat_client to the server - dut1.write(test_msg) - while len(g_client_response) == 0: - time.sleep(1) - g_client_response = g_client_response.decode() - print(g_client_response) - # 6. evaluate host_server received this message - if (g_client_response[4:7] == test_msg): - print('PASS: Received correct message') - pass - else: - print('Failure!') - raise ValueError('Wrong data received from asi tcp server: {} (expected:{})'.format(g_client_response[4:7], test_msg)) - thread1.join() - - -if __name__ == '__main__': - test_examples_protocol_asio_chat_client() diff --git a/examples/protocols/asio/chat_client/main/CMakeLists.txt b/examples/protocols/asio/chat_client/main/CMakeLists.txt deleted file mode 100644 index 02529cb541..0000000000 --- a/examples/protocols/asio/chat_client/main/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -idf_component_register(SRCS "chat_client.cpp" - INCLUDE_DIRS ".") diff --git a/examples/protocols/asio/chat_client/main/Kconfig.projbuild b/examples/protocols/asio/chat_client/main/Kconfig.projbuild deleted file mode 100644 index 34d9e93cfa..0000000000 --- a/examples/protocols/asio/chat_client/main/Kconfig.projbuild +++ /dev/null @@ -1,16 +0,0 @@ -menu "Example Configuration" - - config EXAMPLE_PORT - string "Asio example server port number" - default "2222" - help - Port number used by Asio example. - - config EXAMPLE_SERVER_IP - string "Asio example server ip" - default "FROM_STDIN" - help - Asio example server ip for this client to connect to. - Leave default "FROM_STDIN" to enter the server address via serial terminal. - -endmenu diff --git a/examples/protocols/asio/chat_client/main/chat_client.cpp b/examples/protocols/asio/chat_client/main/chat_client.cpp deleted file mode 100644 index 10ab860c9d..0000000000 --- a/examples/protocols/asio/chat_client/main/chat_client.cpp +++ /dev/null @@ -1,183 +0,0 @@ -// -// chat_client.cpp -// ~~~~~~~~~~~~~~~ -// -// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#include -#include -#include -#include -#include "asio.hpp" -#include "chat_message.hpp" -#include "protocol_examples_common.h" -#include "esp_event.h" -#include "nvs_flash.h" - -using asio::ip::tcp; - -typedef std::deque chat_message_queue; - -class chat_client -{ -public: - chat_client(asio::io_context& io_context, - const tcp::resolver::results_type& endpoints) - : io_context_(io_context), - socket_(io_context) - { - do_connect(endpoints); - } - - void write(const chat_message& msg) - { - asio::post(io_context_, - [this, msg]() - { - bool write_in_progress = !write_msgs_.empty(); - write_msgs_.push_back(msg); - if (!write_in_progress) - { - do_write(); - } - }); - } - - void close() - { - asio::post(io_context_, [this]() { socket_.close(); }); - } - -private: - void do_connect(const tcp::resolver::results_type& endpoints) - { - asio::async_connect(socket_, endpoints, - [this](std::error_code ec, tcp::endpoint) - { - if (!ec) - { - do_read_header(); - } - }); - } - - void do_read_header() - { - asio::async_read(socket_, - asio::buffer(read_msg_.data(), chat_message::header_length), - [this](std::error_code ec, std::size_t /*length*/) - { - if (!ec && read_msg_.decode_header()) - { - do_read_body(); - } - else - { - socket_.close(); - } - }); - } - - void do_read_body() - { - asio::async_read(socket_, - asio::buffer(read_msg_.body(), read_msg_.body_length()), - [this](std::error_code ec, std::size_t /*length*/) - { - if (!ec) - { - std::cout.write(read_msg_.body(), read_msg_.body_length()); - std::cout << "\n"; - do_read_header(); - } - else - { - socket_.close(); - } - }); - } - - void do_write() - { - asio::async_write(socket_, - asio::buffer(write_msgs_.front().data(), - write_msgs_.front().length()), - [this](std::error_code ec, std::size_t /*length*/) - { - if (!ec) - { - write_msgs_.pop_front(); - if (!write_msgs_.empty()) - { - do_write(); - } - } - else - { - socket_.close(); - } - }); - } - -private: - asio::io_context& io_context_; - tcp::socket socket_; - chat_message read_msg_; - chat_message_queue write_msgs_; -}; - -void read_line(char * line, int max_chars); - - -extern "C" void app_main(void) -{ - ESP_ERROR_CHECK(nvs_flash_init()); - esp_netif_init(); - ESP_ERROR_CHECK(esp_event_loop_create_default()); - - /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. - * Read "Establishing Wi-Fi or Ethernet Connection" section in - * examples/protocols/README.md for more information about this function. - */ - ESP_ERROR_CHECK(example_connect()); - - /* This helper function configures blocking UART I/O */ - ESP_ERROR_CHECK(example_configure_stdin_stdout()); - - std::string name(CONFIG_EXAMPLE_SERVER_IP); - std::string port(CONFIG_EXAMPLE_PORT); - char line[chat_message::max_body_length + 1] = { 0 }; - - if (name == "FROM_STDIN") { - std::cout << "Please enter ip address of chat server" << std::endl; - if (std::cin.getline(line, chat_message::max_body_length + 1)) { - name = line; - std::cout << "Chat server IP:" << name << std::endl; - } - } - - asio::io_context io_context; - tcp::resolver resolver(io_context); - auto endpoints = resolver.resolve(name, port); - - chat_client c(io_context, endpoints); - - std::thread t([&io_context](){ io_context.run(); }); - - while (std::cin.getline(line, chat_message::max_body_length + 1) && std::string(line) != "exit") { - chat_message msg; - msg.body_length(std::strlen(line)); - std::memcpy(msg.body(), line, msg.body_length()); - msg.encode_header(); - c.write(msg); - } - - c.close(); - t.join(); - - ESP_ERROR_CHECK(example_disconnect()); -} diff --git a/examples/protocols/asio/chat_client/sdkconfig.defaults b/examples/protocols/asio/chat_client/sdkconfig.defaults deleted file mode 100644 index b02a3a3ef6..0000000000 --- a/examples/protocols/asio/chat_client/sdkconfig.defaults +++ /dev/null @@ -1,7 +0,0 @@ -CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192 - -# -# Partition Table -# -# Leave some room for larger apps without needing to reduce other features -CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y diff --git a/examples/protocols/asio/chat_server/CMakeLists.txt b/examples/protocols/asio/chat_server/CMakeLists.txt deleted file mode 100644 index 182f8d4024..0000000000 --- a/examples/protocols/asio/chat_server/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# The following lines of boilerplate have to be in your project's CMakeLists -# in this exact order for cmake to work correctly -cmake_minimum_required(VERSION 3.5) - -# (Not part of the boilerplate) -# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection. -set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common) - -include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(asio_chat_server) diff --git a/examples/protocols/asio/chat_server/README.md b/examples/protocols/asio/chat_server/README.md deleted file mode 100644 index ec8b3e76e3..0000000000 --- a/examples/protocols/asio/chat_server/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Asio chat server example - -Simple Asio chat server using WiFi STA or Ethernet. - -## Example workflow - -- Wi-Fi or Ethernet connection is established, and IP address is obtained. -- Asio chat server is started on port number defined through the project configuration. -- Chat server echoes a message (received from any client) to all connected clients. - -## Running the example - -- Open project configuration menu (`idf.py menuconfig`) to configure Wi-Fi or Ethernet. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details. -- Set server port number in menuconfig, "Example configuration". -- Run `idf.py -p PORT flash monitor` to build and upload the example to your board and connect to it's serial terminal. -- Wait for the board to connect to WiFi or Ethernet (note the IP address). -- Connect to the server using multiple clients, for example using any option below. - - build and run asi chat client on your host machine - - run chat_client asio example on ESP platform - - since chat message consist of ascii size and message, it is possible to - netcat `nc IP PORT` and type for example ` 4ABC` to transmit 'ABC\n' - -See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/protocols/asio/chat_server/asio_chat_server_test.py b/examples/protocols/asio/chat_server/asio_chat_server_test.py deleted file mode 100644 index 5e92ea00eb..0000000000 --- a/examples/protocols/asio/chat_server/asio_chat_server_test.py +++ /dev/null @@ -1,44 +0,0 @@ -import os -import re -import socket - -import ttfw_idf - - -@ttfw_idf.idf_example_test(env_tag='Example_WIFI_Protocols') -def test_examples_protocol_asio_chat_server(env, extra_data): - """ - steps: | - 1. join AP - 2. Start server - 3. Test connects to server and sends a test message - 4. Test evaluates received test message from server - """ - test_msg = b' 4ABC\n' - dut1 = env.get_dut('chat_server', 'examples/protocols/asio/chat_server', dut_class=ttfw_idf.ESP32DUT) - # check and log bin size - binary_file = os.path.join(dut1.app.binary_path, 'asio_chat_server.bin') - bin_size = os.path.getsize(binary_file) - ttfw_idf.log_performance('asio_chat_server_bin_size', '{}KB'.format(bin_size // 1024)) - # 1. start test - dut1.start_app() - # 2. get the server IP address - data = dut1.expect(re.compile(r' IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)'), timeout=30) - # 3. create tcp client and connect to server - dut1.expect('ASIO engine is up and running', timeout=1) - cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - cli.settimeout(30) - cli.connect((data[0], 2222)) - cli.send(test_msg) - data = cli.recv(1024) - # 4. check the message received back from the server - if (data == test_msg): - print('PASS: Received correct message {}'.format(data)) - pass - else: - print('Failure!') - raise ValueError('Wrong data received from asi tcp server: {} (expoected:{})'.format(data, test_msg)) - - -if __name__ == '__main__': - test_examples_protocol_asio_chat_server() diff --git a/examples/protocols/asio/chat_server/main/CMakeLists.txt b/examples/protocols/asio/chat_server/main/CMakeLists.txt deleted file mode 100644 index c16daff3da..0000000000 --- a/examples/protocols/asio/chat_server/main/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -idf_component_register(SRCS "chat_server.cpp" - INCLUDE_DIRS ".") diff --git a/examples/protocols/asio/chat_server/main/Kconfig.projbuild b/examples/protocols/asio/chat_server/main/Kconfig.projbuild deleted file mode 100644 index c1f397b9cd..0000000000 --- a/examples/protocols/asio/chat_server/main/Kconfig.projbuild +++ /dev/null @@ -1,9 +0,0 @@ -menu "Example Configuration" - - config EXAMPLE_PORT - string "Asio example server port number" - default "2222" - help - Port number used by Asio example - -endmenu diff --git a/examples/protocols/asio/chat_server/main/chat_message.hpp b/examples/protocols/asio/chat_server/main/chat_message.hpp deleted file mode 100644 index 629105b05b..0000000000 --- a/examples/protocols/asio/chat_server/main/chat_message.hpp +++ /dev/null @@ -1,91 +0,0 @@ -// -// chat_message.hpp -// ~~~~~~~~~~~~~~~~ -// -// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef CHAT_MESSAGE_HPP -#define CHAT_MESSAGE_HPP - -#include -#include -#include - -class chat_message -{ -public: - enum { header_length = 4 }; - enum { max_body_length = 512 }; - - chat_message() - : body_length_(0) - { - } - - const char* data() const - { - return data_; - } - - char* data() - { - return data_; - } - - std::size_t length() const - { - return header_length + body_length_; - } - - const char* body() const - { - return data_ + header_length; - } - - char* body() - { - return data_ + header_length; - } - - std::size_t body_length() const - { - return body_length_; - } - - void body_length(std::size_t new_length) - { - body_length_ = new_length; - if (body_length_ > max_body_length) - body_length_ = max_body_length; - } - - bool decode_header() - { - char header[header_length + 1] = ""; - std::strncat(header, data_, header_length); - body_length_ = std::atoi(header); - if (body_length_ > max_body_length) - { - body_length_ = 0; - return false; - } - return true; - } - - void encode_header() - { - char header[header_length + 1] = ""; - std::sprintf(header, "%4d", static_cast(body_length_)); - std::memcpy(data_, header, header_length); - } - -private: - char data_[header_length + max_body_length]; - std::size_t body_length_; -}; - -#endif // CHAT_MESSAGE_HPP diff --git a/examples/protocols/asio/chat_server/main/component.mk b/examples/protocols/asio/chat_server/main/component.mk deleted file mode 100644 index 0adf45649a..0000000000 --- a/examples/protocols/asio/chat_server/main/component.mk +++ /dev/null @@ -1,8 +0,0 @@ -# -# Main component makefile. -# -# This Makefile can be left empty. By default, it will take the sources in the -# src/ directory, compile them and link them into lib(subdirectory_name).a -# in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# diff --git a/examples/protocols/asio/chat_server/sdkconfig.defaults b/examples/protocols/asio/chat_server/sdkconfig.defaults deleted file mode 100644 index b02a3a3ef6..0000000000 --- a/examples/protocols/asio/chat_server/sdkconfig.defaults +++ /dev/null @@ -1,7 +0,0 @@ -CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192 - -# -# Partition Table -# -# Leave some room for larger apps without needing to reduce other features -CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y