diff --git a/README.md b/README.md index fb55fa20..bf6f4a6f 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ - [atclient_esp32_static_components](./examples/atclient_esp32_static_components/README.md) is an example of how to use atclient in your ESP-IDF project with static libraries in separated components built from [atclient_espidf](./packages/atclient_espidf/README.md). - [atclient_esp32_static_no_components](./examples/atclient_esp32_static_no_components/) is an example of how to use atclient in your ESP-IDF project with static libraries without components built from [atclient_espidf](./packages/atclient_espidf/README.md). - [repl](./examples/repl/README.md) is a command line interface for interacting with the atProtocol. Works on Desktop Linux/MacOS. +- [repl_esp32](./examples/repl_esp32/README.md) is a command line interface for interacting with the atProtocol. Works on ESP32, not tested on other devices that can run the IDF. ## Contributing diff --git a/examples/repl/CMakeLists.txt b/examples/repl/CMakeLists.txt index 977c8770..14c22236 100644 --- a/examples/repl/CMakeLists.txt +++ b/examples/repl/CMakeLists.txt @@ -8,13 +8,7 @@ project( ) include(GNUInstallDirs) -include(FetchContent) -# FetchContent_Declare( -# atclient -# SOURCE_DIR ${CMAKE_SOURCE_DIR}/../../atclient -# ) -# FetchContent_MakeAvailable(atclient) find_package(atclient REQUIRED CONFIG) message(STATUS "atclient was found") diff --git a/examples/repl_esp32/.gitignore b/examples/repl_esp32/.gitignore new file mode 100644 index 00000000..7686741a --- /dev/null +++ b/examples/repl_esp32/.gitignore @@ -0,0 +1,2 @@ +include/ +lib/ \ No newline at end of file diff --git a/examples/repl_esp32/CMakeLists.txt b/examples/repl_esp32/CMakeLists.txt new file mode 100644 index 00000000..18ff0c85 --- /dev/null +++ b/examples/repl_esp32/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.19) + +set(EXTRA_COMPONENT_DIRS + ${CMAKE_CURRENT_LIST_DIR}/../../packages/atchops + ${CMAKE_CURRENT_LIST_DIR}/../../packages/atclient +) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(repl_esp32) \ No newline at end of file diff --git a/examples/repl_esp32/README.md b/examples/repl_esp32/README.md new file mode 100644 index 00000000..3aa46e08 --- /dev/null +++ b/examples/repl_esp32/README.md @@ -0,0 +1,31 @@ +# repl_esp32 + +This example is a command line interface for interacting with the atProtocol. Works on ESP32, not tested on other devices that can run the IDF. You will be able to interact with your atServer through the command-line which will run on your ESP32. + +## Running the REPL + +You will need the [IDF toolchain](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html#idf-py) to build and flash to your device. + +1. Get IDF + +``` +get_idf +``` + +2. Build + +``` +idf.py build +``` + +3. Menuconfig, set your SSID and Password in "repl_esp32 WiFi Configuration" + +``` +idf.py menuconfig +``` + +4. Build, Flash and Monitor + +``` +idf.py build && idf.py flash monitor +``` diff --git a/examples/repl_esp32/main/CMakeLists.txt b/examples/repl_esp32/main/CMakeLists.txt new file mode 100644 index 00000000..5a86a35e --- /dev/null +++ b/examples/repl_esp32/main/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "main.c" "event_handlers.c" "wifi.c" + INCLUDE_DIRS "." + REQUIRES atclient atchops mbedtls esp_wifi esp_event nvs_flash freertos +) diff --git a/examples/repl_esp32/main/Kconfig.projbuild b/examples/repl_esp32/main/Kconfig.projbuild new file mode 100644 index 00000000..4936d799 --- /dev/null +++ b/examples/repl_esp32/main/Kconfig.projbuild @@ -0,0 +1,13 @@ +menu "repl_esp32 WiFi Configuration" + config ESP_WIFI_SSID + string "WiFi SSID" + default "ssid" + help + SSID (network name) to connect to. + + config ESP_WIFI_PASSWORD + string "WiFi Password" + default "pswd" + help + WiFi password (WPA or WPA2) to use. +endmenu diff --git a/examples/repl_esp32/main/event_handlers.c b/examples/repl_esp32/main/event_handlers.c new file mode 100644 index 00000000..b50d1eec --- /dev/null +++ b/examples/repl_esp32/main/event_handlers.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include +#include + +#define WIFI_CONNECTED_BIT BIT0 +#define WIFI_FAIL_BIT BIT1 + +const char *TAG_WIFI_EVENT_HANDLER = "repl_esp32 Wifi Event Handler"; +const char *TAG_IP_EVENT_HANDLER = "repl_esp32 IP Event Handler"; + +// arguments should be identical to esp_event_handler_t +void event_handler_wifi(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + // event_base should always be WIFI_EVENT + if(event_base != WIFI_EVENT) + { + return; + } + + EventGroupHandle_t wifi_event_group = *((EventGroupHandle_t *) event_handler_arg); + + if(event_id == WIFI_EVENT_STA_START) + { + ESP_LOGI(TAG_WIFI_EVENT_HANDLER, "Connecting to WiFi..."); + } else if(event_id == WIFI_EVENT_STA_CONNECTED) + { + ESP_LOGI(TAG_WIFI_EVENT_HANDLER, "Connected to WiFi ! ... SSID:%s", ((wifi_event_sta_connected_t *) event_data)->ssid); + xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT); // set connected bit + } else if(event_id == WIFI_EVENT_STA_DISCONNECTED) + { + ESP_LOGI(TAG_WIFI_EVENT_HANDLER, "Disconnected from WiFi..."); + xEventGroupSetBits(wifi_event_group, WIFI_FAIL_BIT); // set fail bit + } +} + +// arguments should be identical to esp_event_handler_t +void event_handler_ip(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + // event_base should always be IP_EVENT, otherwise this function is being used incorrectly. + if(event_base != IP_EVENT) + { + return; + } + + if(event_id == IP_EVENT_STA_GOT_IP) + { + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + ESP_LOGI(TAG_IP_EVENT_HANDLER, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); + } else if(event_id == IP_EVENT_STA_LOST_IP) + { + ESP_LOGI(TAG_IP_EVENT_HANDLER, "Lost IP..."); + } +} \ No newline at end of file diff --git a/examples/repl_esp32/main/main.c b/examples/repl_esp32/main/main.c new file mode 100644 index 00000000..8d1c1226 --- /dev/null +++ b/examples/repl_esp32/main/main.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include + +static const char *TAG = "repl_esp32 main"; + +extern int wifi_connect(const char *ssid, const char *password); + +void app_main(void) +{ + int ret = 1; + + ret = wifi_connect(CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD); + if (ret != 0) + { + ESP_LOGE(TAG, "Failed to connect to WiFi."); + goto exit; + } + ESP_LOGI(TAG, "Connected to WiFi."); + + ESP_LOGI(TAG, "Connecting to root.atsign.org:64..."); + atclient_connection_ctx root_connection; + atclient_connection_init(&root_connection); + ret = atclient_connection_connect(&root_connection, "root.atsign.org", 64); + if (ret != 0) + { + ESP_LOGE(TAG, "Failed to connect to root.atsign.org. at_client_connection_connect: %d", ret); + + ESP_LOGI(TAG, "Trying again..."); + ret = atclient_connection_connect(&root_connection, "root.atsign.org", 64); + if (ret != 0) + { + ESP_LOGE(TAG, "Failed to connect to root.atsign.org. at_client_connection_connect: %d", ret); + goto exit; + } + } + ESP_LOGI(TAG, "Connected to root.atsign.org"); + + ESP_LOGI(TAG, "Sending data..."); + const unsigned long recvlen = 4096; + unsigned char *recv = malloc(sizeof(unsigned char) * recvlen); + unsigned long olen = 0; + const char *src = "colin\r\n"; + const unsigned long srclen = strlen(src); + + ret = atclient_connection_send(&root_connection, recv, recvlen, &olen, (const unsigned char *) src, srclen); + if (ret != 0) + { + ESP_LOGE(TAG, "Failed to send data. at_client_connection_send: %d", ret); + goto exit; + } + + ESP_LOGI(TAG, "Receiving data..."); + ESP_LOGI(TAG, "olen: %lu", olen); + ESP_LOGI(TAG, "recv: \"%s\"", recv); + + free(recv); + goto exit; +exit: +{ + return; +} +} \ No newline at end of file diff --git a/examples/repl_esp32/main/wifi.c b/examples/repl_esp32/main/wifi.c new file mode 100644 index 00000000..13d7d13b --- /dev/null +++ b/examples/repl_esp32/main/wifi.c @@ -0,0 +1,175 @@ +#include +#include // +#include +#include +#include // ESP_LOGI and ESP +#include +#include // NVS "non-volatile storage" (ROM) +#include +#include + +#define WIFI_CONNECTED_BIT BIT0 +#define WIFI_FAIL_BIT BIT1 + +static const char *TAG = "repl_esp32 WiFi"; // for logging + +extern void event_handler_wifi(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data); + +extern void event_handler_ip(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data); + +static int init_nvs() +{ + int ret = 1; + // intialize NVS "non-volatile storage" (ROM) + ret = nvs_flash_init(); // from nvs_flash.h + + // if NVS is not initialized, erase it and try again + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) + { + // erase NVS + ESP_ERROR_CHECK(nvs_flash_erase()); + // re-initialize NVS + ret = nvs_flash_init(); + } + + return ret; +} + +static int init_net() +{ + int ret = 1; + + // initialize underlying TCP/IP stack, esp_netif.h + ESP_LOGI(TAG, "Initializing TCP/IP stack..."); + ret = esp_netif_init(); + ESP_ERROR_CHECK(ret); + + return ret; +} + +static int init_event_handlers(EventGroupHandle_t *wifi_event_group) +{ + int ret = 1; + + // create default event loop, esp_event.h + ESP_LOGI(TAG, "Creating default event loop..."); + ret = esp_event_loop_create_default(); + ESP_ERROR_CHECK(ret); + + // create default network interface, esp_wifi.h + esp_netif_create_default_wifi_sta(); + + // register wifi event handler, esp_event.h + esp_event_base_t wifi_event_base = WIFI_EVENT; + int32_t any_event_id = ESP_EVENT_ANY_ID; + ret = esp_event_handler_instance_register( + wifi_event_base, // esp_event_base_t + any_event_id, // int32_t + &event_handler_wifi, // esp_event_handler_t + wifi_event_group, // void * (event_handler_arg) + NULL // esp_event_handler_instance_t * (instance) + ); + ESP_ERROR_CHECK(ret); + + // register ip event handler, esp_event.h + esp_event_base_t ip_event_base = IP_EVENT; + ret = esp_event_handler_instance_register( + ip_event_base, // esp_event_base_t + any_event_id, // int32_t + &event_handler_ip, // esp_event_handler_t + NULL, // void * (event_handler_arg) + NULL // esp_event_handler_instance_t * (instance) + ); + + return ret; +} + +static int init_wifi_sta(const unsigned char *wifi_ssid, const unsigned long wifi_ssidlen, const unsigned char *wifi_password, const unsigned long wifi_passwordlen) +{ + int ret = 1; + + wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT(); + + wifi_sta_config_t wifi_sta_config; + memset(&wifi_sta_config, 0, sizeof(wifi_sta_config)); + memcpy(wifi_sta_config.ssid, wifi_ssid, wifi_ssidlen); + memcpy(wifi_sta_config.password, wifi_password, wifi_passwordlen); + + wifi_config_t wifi_config = { + .sta = wifi_sta_config}; + + // init wifi + ret = esp_wifi_init(&wifi_init_config); + ESP_ERROR_CHECK(ret); + + // set mode to station + ret = esp_wifi_set_mode(WIFI_MODE_STA); + ESP_ERROR_CHECK(ret); + + // configure wifi + ret = esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config); + ESP_ERROR_CHECK(ret); + + // start wifi + ret = esp_wifi_start(); + ESP_ERROR_CHECK(ret); + + return ret; +} + +int wifi_connect(const char *ssid, const char *password) +{ + int ret = 1; + + // initialize NVS "non-volatile storage" (ROM), nvs_flash.h + ESP_LOGI(TAG, "Initializing NVS..."); + ret = init_nvs(); + ESP_ERROR_CHECK(ret); + + // initialie network interface, esp_netif.h + ESP_LOGI(TAG, "Initializing network interface..."); + ret = init_net(); + ESP_ERROR_CHECK(ret); + + // use free rtos to create event group, freertos/FreeRTOS.h + EventGroupHandle_t wifi_event_group = xEventGroupCreate(); + + // initialize event handlers, esp_event.h + ESP_LOGI(TAG, "Initializing event handlers..."); + ret = init_event_handlers(&wifi_event_group); + ESP_ERROR_CHECK(ret); + + // initialize wifi, esp_wifi.h + ESP_LOGI(TAG, "Initializing wifi..."); + ret = init_wifi_sta((const unsigned char *)ssid, strlen(ssid), (const unsigned char *)password, strlen(password)); + ESP_ERROR_CHECK(ret); + + // event handler sets bits in event group, attempt connect using esp_wifi_connect() + EventBits_t bits; + do + { + ESP_LOGI(TAG, "Attempting to connect to WiFi..."); + ret = esp_wifi_connect(); + ESP_LOGI(TAG, "esp_wifi_connect() returned %d", (int)ret); + vTaskDelay(1000 / portTICK_PERIOD_MS); + + bits = xEventGroupWaitBits( + wifi_event_group, // EventGroupHandle_t + WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, // bits to wait for + pdFALSE, // clear bits on exit + pdFALSE, // wait for all bits + portMAX_DELAY // wait forever + ); + if (bits & WIFI_CONNECTED_BIT) + { + ESP_LOGI(TAG, "Connected to WiFi !"); + ret = 0; + } + else if (bits & WIFI_FAIL_BIT) + { + ESP_LOGI(TAG, "Failed to connect to WiFi."); + } + } while (bits & WIFI_FAIL_BIT); + + return ret; +} \ No newline at end of file diff --git a/packages/atclient/src/connection.c b/packages/atclient/src/connection.c index 95047c3e..c2073582 100644 --- a/packages/atclient/src/connection.c +++ b/packages/atclient/src/connection.c @@ -17,6 +17,7 @@ static void my_debug(void *ctx, int level, const char *file, int line, const cha void atclient_connection_init(atclient_connection_ctx *ctx) { + memset(ctx, 0, sizeof(atclient_connection_ctx)); ctx->host = NULL; ctx->port = NULL; ctx->cert_pem = ROOT_CERT;