From 3863952f7e74b8658ca454f7a97acbd21d96cddd Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Mon, 12 Jul 2021 21:57:36 +0200 Subject: [PATCH] Add an example chat application. --- CMakeLists.txt | 7 + README.ESP32 | 16 +++ config-esp32.h | 188 ++++++++++++++++++++++++++ main | 1 + src/CMakeLists.txt | 46 +++++++ src/Kconfig.projbuild | 20 +++ src/mlt_main.c | 301 ++++++++++++++++++++++++++++++++++++++++++ src/system.h | 2 +- 8 files changed, 580 insertions(+), 1 deletion(-) create mode 100644 CMakeLists.txt create mode 100644 README.ESP32 create mode 100644 config-esp32.h create mode 120000 main create mode 100644 src/CMakeLists.txt create mode 100644 src/Kconfig.projbuild create mode 100644 src/mlt_main.c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..28af8d7 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,7 @@ +# The following five 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) +set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/system/console/components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(meshlink_tiny) diff --git a/README.ESP32 b/README.ESP32 new file mode 100644 index 0000000..2387441 --- /dev/null +++ b/README.ESP32 @@ -0,0 +1,16 @@ +To build, you must have the Espressif IoT Development Framework (ESP-IDF) +installed. You can find it here: + +https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/ + +Make sure you have sourced export.sh so the idf.py command runs correctly. Then +run these commands: + + idf.py set-target esp32 + idf.py menuconfig + +Go to the "WiFi Configuration" menu and fill in the details matching your WiFi +network. Then build and flash with: + + idf.py build + idf.py -p flash diff --git a/config-esp32.h b/config-esp32.h new file mode 100644 index 0000000..aabb266 --- /dev/null +++ b/config-esp32.h @@ -0,0 +1,188 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ARPA_INET_H 1 + +/* Define to 1 if you have the `asprintf' function. */ +#define HAVE_ASPRINTF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_CURSES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DIRENT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the `fchmod' function. */ +#define HAVE_FCHMOD 1 + +/* Define to 1 if you have the `fork' function. */ +#define HAVE_FORK 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `socket' library (-lsocket). */ +/* #undef HAVE_LIBSOCKET */ + +/* Linux */ +#define HAVE_LINUX 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* MinGW */ +/* #undef HAVE_MINGW */ + +/* Define to 1 if you have the header file. */ +#define HAVE_NETDB_H 1 + +/* Define if you have POSIX threads libraries and header files. */ +#define HAVE_PTHREAD 1 + +/* Have PTHREAD_PRIO_INHERIT. */ +#define HAVE_PTHREAD_PRIO_INHERIT 1 + +/* Define to 1 if you have the `random' function. */ +#define HAVE_RANDOM 1 + +/* Define to 1 if you have the `select' function. */ +#define HAVE_SELECT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDATOMIC_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_FILE_H 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_RESOURCE_H 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_UN_H 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_WAIT_H 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `usleep' function. */ +#define HAVE_USLEEP 1 + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "meshlink" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "MeshLink" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "MeshLink 0.1" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "meshlink" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.1" + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef PTHREAD_CREATE_JOINABLE */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + + +/* Version number of package */ +#define VERSION "0.1" + +/* Enable Darwin features */ +#define _DARWIN_C_SOURCE 1 + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Enable POSIX features */ +#define _POSIX_C_SOURCE 200809L + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Enable BSD extensions */ +#define __USE_BSD 1 + +/* Defined if the __malloc__ attribute is not supported. */ +/* #undef __malloc__ */ + +/* Defined if the __warn_unused_result__ attribute is not supported. */ +/* #undef __warn_unused_result__ */ diff --git a/main b/main new file mode 120000 index 0000000..e831038 --- /dev/null +++ b/main @@ -0,0 +1 @@ +src \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..3346908 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,46 @@ +idf_component_register(SRCS + "buffer.c" + "conf.c" + "connection.c" + "crypto.c" + "devtools.c" + "dropin.c" + "event.c" + "hash.c" + "list.c" + "logger.c" + "meshlink.c" + "meta.c" + "net.c" + "net_setup.c" + "net_socket.c" + "netutl.c" + "node.c" + "prf.c" + "protocol.c" + "protocol_auth.c" + "protocol_edge.c" + "protocol_key.c" + "protocol_misc.c" + "splay_tree.c" + "sptps.c" + "utils.c" + "xoshiro.c" + "ed25519/add_scalar.c" + "ed25519/ecdh.c" + "ed25519/ecdsa.c" + "ed25519/ecdsagen.c" + "ed25519/fe.c" + "ed25519/ge.c" + "ed25519/key_exchange.c" + "ed25519/keypair.c" + "ed25519/sc.c" + "ed25519/seed.c" + "ed25519/sha512.c" + "ed25519/sign.c" + "ed25519/verify.c" + "chacha-poly1305/chacha-poly1305.c" + "chacha-poly1305/chacha.c" + "chacha-poly1305/poly1305.c" + "mlt_main.c" + INCLUDE_DIRS ".") diff --git a/src/Kconfig.projbuild b/src/Kconfig.projbuild new file mode 100644 index 0000000..e4fd563 --- /dev/null +++ b/src/Kconfig.projbuild @@ -0,0 +1,20 @@ +menu "WiFi Configuration" + + config ESP_WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + + config ESP_WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + + config ESP_MAXIMUM_RETRY + int "Maximum retry" + default 5 + help + Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent. +endmenu diff --git a/src/mlt_main.c b/src/mlt_main.c new file mode 100644 index 0000000..977b9c3 --- /dev/null +++ b/src/mlt_main.c @@ -0,0 +1,301 @@ +/* MeshLink-tiny Example */ + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_console.h" +#include "esp_vfs_dev.h" +#include "driver/uart.h" +#include "linenoise/linenoise.h" +#include "argtable3/argtable3.h" +#include "nvs_flash.h" + +#include "lwip/err.h" +#include "lwip/sys.h" + +#include "meshlink-tiny.h" + +/* The examples use WiFi configuration that you can set via project configuration menu + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID +#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD +#define EXAMPLE_ESP_MAXIMUM_RETRY CONFIG_ESP_MAXIMUM_RETRY + +/* FreeRTOS event group to signal when we are connected*/ +static EventGroupHandle_t s_wifi_event_group; + +/* The event group allows multiple bits for each event, but we only care about two events: + * - we are connected to the AP with an IP + * - we failed to connect after the maximum amount of retries */ +#define WIFI_CONNECTED_BIT BIT0 +#define WIFI_FAIL_BIT BIT1 + +static const char *TAG = "wifi station"; + +static int s_retry_num = 0; + +static void event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) { + if(event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { + esp_wifi_connect(); + } else if(event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + if(s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) { + esp_wifi_connect(); + s_retry_num++; + ESP_LOGI(TAG, "retry to connect to the AP"); + } else { + xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); + } + + ESP_LOGI(TAG, "connect to the AP fail"); + } else if(event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data; + ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); + s_retry_num = 0; + xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); + } +} + +void wifi_init_sta(void) { + s_wifi_event_group = xEventGroupCreate(); + + ESP_ERROR_CHECK(esp_netif_init()); + + ESP_ERROR_CHECK(esp_event_loop_create_default()); + esp_netif_create_default_wifi_sta(); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + esp_event_handler_instance_t instance_any_id; + esp_event_handler_instance_t instance_got_ip; + ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, + ESP_EVENT_ANY_ID, + &event_handler, + NULL, + &instance_any_id)); + ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, + IP_EVENT_STA_GOT_IP, + &event_handler, + NULL, + &instance_got_ip)); + + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_ESP_WIFI_SSID, + .password = EXAMPLE_ESP_WIFI_PASS, + /* Setting a password implies station will connect to all security modes including WEP/WPA. + * However these modes are deprecated and not advisable to be used. Incase your Access point + * doesn't support WPA2, these mode can be enabled by commenting below line */ + .threshold.authmode = WIFI_AUTH_WPA2_PSK, + + .pmf_cfg = { + .capable = true, + .required = false + }, + }, + }; + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + + ESP_LOGI(TAG, "wifi_init_sta finished."); + + /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum + * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ + EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, + WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, + pdFALSE, + pdFALSE, + portMAX_DELAY); + + /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually + * happened. */ + if(bits & WIFI_CONNECTED_BIT) { + ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", + EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS); + } else if(bits & WIFI_FAIL_BIT) { + ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", + EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS); + } else { + ESP_LOGE(TAG, "UNEXPECTED EVENT"); + } + + /* The event will not be processed after unregister */ + ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip)); + ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); + vEventGroupDelete(s_wifi_event_group); +} + +static void mlt_log(meshlink_handle_t *mesh, meshlink_log_level_t leve, const char *text) { + ESP_LOGI(TAG, "Log: %s", text); +} + +meshlink_handle_t *mesh = NULL; + +static void receive(meshlink_handle_t *mesh, meshlink_node_t *from, const void *data, size_t len) { + char *str = (char *)data; + str[len] = 0; + ESP_LOGI(TAG, "%s says: %s", from->name, str); +} + +static int join_func(int argc, char **argv) { + if(argc < 2) { + return 1; + } + + if(!meshlink_join(mesh, argv[1])) { + ESP_LOGE(TAG, "Join failed"); + return 1; + } + + ESP_LOGI(TAG, "Join completed"); + + meshlink_set_receive_cb(mesh, receive); + + if(!meshlink_start(mesh)) { + ESP_LOGE(TAG, "Could not start mesh!"); + return 1; + } + + return 0; +} + +static int say_func(int argc, char **argv) { + if(argc < 3) { + return 1; + } + + meshlink_node_t *peer = meshlink_get_node(mesh, argv[1]); + + if(!peer) { + ESP_LOGE(TAG, "Peer not found"); + return 1; + } + + if(!meshlink_send(mesh, peer, argv[2], strlen(argv[2]))) { + ESP_LOGE(TAG, "Send failed"); + return 1; + } + + return 0; +} + +static int quit_func(int argc, char **argv) { + meshlink_close(mesh); + mesh = NULL; + ESP_LOGI(TAG, "Closed mesh"); + return 0; +} + +static void mlt_main(void) { + meshlink_set_log_cb(NULL, MESHLINK_DEBUG, mlt_log); + + ESP_LOGI(TAG, "Starting MeshLink-tiny instance..."); + mesh = meshlink_open_ephemeral("esp32", "chat", DEV_CLASS_PORTABLE); + + if(!mesh) { + ESP_LOGE(TAG, "Open failed!"); + return; + } + + fflush(stdout); + fsync(fileno(stdout)); + setvbuf(stdin, NULL, _IONBF, 0); + + esp_vfs_dev_uart_port_set_rx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CR); + esp_vfs_dev_uart_port_set_tx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF); + + const uart_config_t uart_config = { + .baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 + .source_clk = UART_SCLK_REF_TICK, +#else + .source_clk = UART_SCLK_XTAL, +#endif + }; + /* Install UART driver for interrupt-driven reads and writes */ + ESP_ERROR_CHECK(uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM, + 256, 0, 0, NULL, 0)); + ESP_ERROR_CHECK(uart_param_config(CONFIG_ESP_CONSOLE_UART_NUM, &uart_config)); + + /* Tell VFS to use UART driver */ + esp_vfs_dev_uart_use_driver(CONFIG_ESP_CONSOLE_UART_NUM); + + esp_console_config_t console_config = { + .max_cmdline_args = 8, + .max_cmdline_length = 256, + }; + + ESP_ERROR_CHECK(esp_console_init(&console_config)); + + linenoiseSetMultiLine(1); + linenoiseHistorySetMaxLen(1); + linenoiseAllowEmpty(false); + + esp_console_cmd_t join_cmd = { + .command = "join", + .help = "Join a mesh", + .func = join_func, + }; + + esp_console_cmd_t say_cmd = { + .command = "say", + .help = "Say something", + .func = say_func, + }; + + esp_console_cmd_t quit_cmd = { + .command = "quit", + .help = "Quit", + .func = quit_func, + }; + + esp_console_cmd_register(&join_cmd); + esp_console_cmd_register(&say_cmd); + esp_console_cmd_register(&quit_cmd); + esp_console_register_help_command(); + + while(mesh) { + char *line = linenoise("> "); + + if(!line) { + continue; + } + + linenoiseHistoryAdd(line); + int ret; + esp_console_run(line, &ret); + linenoiseFree(line); + } + + ESP_LOGI(TAG, "App quit"); + esp_console_deinit(); +} + +void app_main(void) { + //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); + + ESP_LOGI(TAG, "ESP_WIFI_MODE_STA"); + wifi_init_sta(); + mlt_main(); +} diff --git a/src/system.h b/src/system.h index a31ba7a..1f6f0cd 100644 --- a/src/system.h +++ b/src/system.h @@ -20,7 +20,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "../config.h" +#include "../config-esp32.h" #include "have.h" -- 2.39.5