diff --git a/README.md b/README.md index dae61dde..26ffe0b0 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ DHYARA ====== +[GitHub Repository](https://github.com/neel/dhyara) | [Doxygen Documentation](http://neel.github.io/dhyara) + Dhyara is a ESP-IDF component for Mobile Ad-Hoc Networks on the top of ESP NOW (Layer 2). Dhyara is Open Source under BSD 2 Clause License. See [LICENSE](docs/LICENSE.md) -[Doxygen Documentation](http://neel.github.io/dhyara) +[TOC] Basic Usage @@ -51,6 +53,8 @@ dhyara_receive_data([](const dhyara::peer::address& source, const dhyara::packet In the above mentioned C++ example, source represents the immediate neighbour of the desination, and `data.source()` is the originator of the message. +[Read More](\ref dhyara) + Compilation & Integration -------------------------- @@ -69,54 +73,112 @@ To build and flash the application use `idf.py build` and `idf.py flash` as usua See [Building the examples](docs/examples.md). -Guide ------- - -ESP wifi subsystem has to be started before starting the dhyara network. -`dhyara::link` provides the underlying communication layer over ESP NOW. -The registered received callbacks should forward the received data to the `dhyara::link::_esp_rcvd_cb`. -Similarly the registered sent callback should forward the arguments to the `dhyara::link::_esp_sent_cb`. -A `dhyara::network` instance is created by using a reference to the `link`. -The network is started using nonblocking `dhyara::network::start` method, which creates multiple tasks to maintain the underlying network. -For rest of the application using dhyara, a reference to the `dhyara::network` is required. -So after starting the network, the application code should pass a reference to the network instance appopriately. -All nodes running dhyara will communicate with each other and transmit various packets to maintain the dynamic neighbourhood. - -### Design - -Dhyara uses many different packets for communication. -The packets are defined in the \ref packets module. -Type of a packet is identified using `dhyara::packets::type` enum. -Once a packet is received it is read and enqueued to a queue for further processing. -Different actions have been defined to process different packets. -The actions are defined in the \ref actions module. -The actions are installed to the `dhyara::link` by passing the packet type and a reference to the action to `dhyara::link::operator[]()` overload. -While dequeuing a packet it finds the appropriate action through the packet type and invokes the action. -The action may need to send some other packet in response. -For such circumstances the action may take a reference to the `dhyara::link` in its constructor. -Depending of the type of packet received, and the action installed, the received packet may be enqueued into another queue for further processing. -Each action provides a \ref dhyara::actions::action::connect "connect" method to bind a callback which will be called after the action have processed the incomming packet. -The `dhyara::network` provides functions to get reference to all installed actions. - -### Usage - -Being a network over L2 all nodes are identified through their MAC address. `dhyara::peer_address` abstracts a MAC address representation. -ESP Now uses Wifi Action Frames to communicate, which limits maximuum size of a single packet to 250 bytes. -So if the application want to send variable sized messages then dhyara may split that message into multiple chunks and send those packets to the desired destination through the best route over the ad hoc network. -To send a message, use `dhyara::network::send`, which takes a destination address and `begin` and `end` iterators to the data that has to be sent. - -Protocol Overview ------------------- - -Dhyara does not depend in IP layer. It uses MAC address to identify the nodes in the network. -To establish an ad hoc network each nodes broadcasts beacon periodically which is acknowledged by its neighbourhood. -The multihop network is established by sharing the routes discoverd by each of these nodes. See [Protocol](docs/protocol.md) for details. - -Additionally ICMP is implemented on the top of L2 to have a ping and traceroute like functionality. - -Example --------- +C Example +---------- + +The examples are provided in the examples directory. See [Building the examples](docs/examples.md) for instructions on building the examples. + +Following is a C example demonstrating basic usage of dhyara +~~~{.c} + +void data_received(const unsigned char* source, const void* data, unsigned long len){ + ESP_LOGI("hello-c", "data received \"%s\" (length %lu)", (const char*)data, len); +} + +void app_main(){ + 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_ERROR_CHECK(dhyara_init(WIFI_MODE_AP)); + dhyara_start_default_network(); + + dhyara_receivef(&data_received); + + uint8_t self[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; + dhyara_local(self); + + ESP_LOGI("hello-c", "Local MAC address %x:%x:%x:%x:%x:%x", self[0], self[1], self[2], self[3], self[4], self[5]); + + uint8_t source[] = {0x4c, 0x11, 0xae, 0x71, 0x0f, 0x4d}; // TODO change the source address + uint8_t sink[] = {0x4c, 0x11, 0xae, 0x9c, 0xa6, 0x85}; // TODO change the sink address + + const uint8_t* other = 0x0; + + if(memcmp(self, source, 6) == 0) other = sink; + if(memcmp(self, sink, 6) == 0) other = source; + + while(1){ + if(other){ + dhyara_ping(other, .count = 1, .batch = 10); + dhyara_traceroute(other); + dhyara_send(other, "Hello World"); + dhyara_send(other, "Hello World", 5); + } + vTaskDelay(pdMS_TO_TICKS(2000)); + } +} +~~~ + +C++ Example +----------- + +Dhyara is primarily intended to be used for C++ application. +To build a C++ application with ESP-IDF we first start from a `main.c` which calls a C++ function `mainx()`. +Following is the `main.c` from the example `hello-cxx` + +~~~{.c} + +void app_main(){ + 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_ERROR_CHECK(dhyara_init(WIFI_MODE_AP)); + dhyara_start_default_network(); + + mainx(); // Enter C++ +} + +~~~ + +The `mainx()` is declared and gaurded by `extern C{}` block in the `mainx.h`, so that it can be included from c as well as C++. +Following is the definition of mainx() which can be found in the `mainx.cpp` in the `hello-cxx` example project. + +~~~{.cpp} +void mainx(){ + dhyara::network network(dhyara_link()); + dhyara_set_default_network(&network); + + dhyara::peer_address sink("4c:11:ae:9c:a6:85"), source("4c:11:ae:71:0f:4d"); + + dhyara_start_default_network(); + + dhyara::peer_address local = dhyara_local(); + dhyara::peer_address other = (local == source) ? sink : ((local == sink) ? source : dhyara::peer_address::null()); + + // The anonymous function will be called once all chunks of a data packet is received + dhyara_receive_data([](const dhyara::peer::address& source, const dhyara::packets::data& data){ + std::cout << "received data " << " originating from " << data.source() << " via " << source << " of size " << data.length() << std::endl; + }); + + while(1){ + if(!other.is_null()){ + dhyara_ping(other, 1, 10); + dhyara_traceroute(other); + std::string buffer = "Hello World"; + dhyara_send(other, buffer.begin(), buffer.end()); + } + vTaskDelay(pdMS_TO_TICKS(5000)); + } +} +~~~ -See [Building the examples](docs/examples.md). diff --git a/docs/examples.md b/docs/examples.md index 98685b53..e2326587 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -2,57 +2,26 @@ Building Examples {#examples} ================== The examples are stored in the examples directory in the source. -To build an example (e.g. dhyara-example-basic) +To build an example (e.g. hello-cxx) + ```bash -cd examples/dhyara-example-basic +cd examples/hello-cxx idf.py build idf.py flash ``` -Structure ----------- - -All examples puts the application code inside `application` class which has a `main()` method that runs an infinitite loop. -In `main.c` the `app_main()` function is defined which initializes NVS and WiFi. -In the end it calls the `mainx()` function which is declared in `mainx.h` and defined in `mainx.cpp`. -In `mainx()` first ESP Now is initialized, then `application::main` is invoked. -Dhyara requires an instance of `dhyara::link` to maintain the ad hoc network. -It also requires the ESP Now send receive callbacks to be connected to the link. -So a global instance of `dhyara::link` is created. - -```cpp - -dhyara::link g_link; - -void _sent(const uint8_t* target, esp_now_send_status_t status){ - g_link._esp_sent_cb(target, status); -} - -void _rcvd(const uint8_t* source, const uint8_t* data, int len){ - g_link._esp_rcvd_cb(source, data, len); -} - -``` -The two global functions `_sent` and `_rcvd` are used as send and receive callbacks that forward the arguments to the `link` instance. - -```cpp - -void mainx(){ - ESP_ERROR_CHECK(esp_now_init()); - ESP_ERROR_CHECK(esp_now_register_send_cb(_sent)); - ESP_ERROR_CHECK(esp_now_register_recv_cb(_rcvd)); - - dhyara::network network(g_link); - network.start(); - - esp_log_level_set("dhyara", ESP_LOG_VERBOSE); - - application app(network); - app.main(); -} -``` -In the `mainx()` first a network object is created. -The `dhyara::network::start` method does not block. -It creates the necessary FreeRTOS tasks to initiate and maintain the network and returns. -Then the `application` class is instantiated using a reference to that network, and then its `main()` method is called. -The application code can be found in the `application::main` method defined in `application.cpp`. +The `hello-c` and `hello-cxx` examples are respectively C an C++ applications using dhyara. +Botyh of these applications assumes there is a source and a sink node. + +In the C example (hello-c) the source and sink are defined as 6 byte arrays +~~~{.c} +uint8_t source[] = {0x4c, 0x11, 0xae, 0x71, 0x0f, 0x4d}; +uint8_t sink[] = {0x4c, 0x11, 0xae, 0x9c, 0xa6, 0x85}; +~~~ +In the C++ example the `dhyara::peer_address` class is used. +~~~{.cpp} +dhyara::peer_address sink("4c:11:ae:9c:a6:85"), source("4c:11:ae:71:0f:4d"); +~~~ + +While running this examples, usercode *MUST* change the mac address and replace it with the mac address of the ESP32 node. +Both of these examples are proggrammed in a way so that the source and the sink repeatedly pings each other and performs traceroute operation. diff --git a/docs/protocol.md b/docs/protocol.md index e27318c3..cf4e937a 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -3,6 +3,8 @@ Protocol {#protocol} Dhyara Commnunication Protocol +[TOC] + Routing Table -------------- diff --git a/docs/qtify.css b/docs/qtify.css index f4f4f625..a38a85d1 100644 --- a/docs/qtify.css +++ b/docs/qtify.css @@ -4,6 +4,11 @@ * Frank Enderle */ +:root { + --fg: #00732f; + --bg: #f6f6f6; +} + body, table, div, p, dl { font: 400 13px/1.3 Verdana,DejaVu Sans,Geneva,sans-serif; } @@ -850,12 +855,14 @@ dl.citelist dd { } div.toc { + background-color: #f6f6f6 !important; } div.toc li { } div.toc h3 { + color: #00732f !important; } div.toc ul { diff --git a/examples/hello-c/main/main.c b/examples/hello-c/main/main.c index 82963b53..6943fb18 100644 --- a/examples/hello-c/main/main.c +++ b/examples/hello-c/main/main.c @@ -2,13 +2,7 @@ #include "esp_system.h" #include "esp_wifi.h" #include "esp_log.h" -#include "esp_event.h" #include "nvs_flash.h" -#include "lwip/err.h" -#include "lwip/sys.h" -#include "freertos/FreeRTOSConfig.h" -#include "esp_timer.h" -#include "esp_private/wifi.h" #include #include