Skip to content

Commit

Permalink
- docs updated
Browse files Browse the repository at this point in the history
  • Loading branch information
neel committed Apr 14, 2021
1 parent 0970d81 commit fa2ceca
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 104 deletions.
160 changes: 111 additions & 49 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
--------------------------

Expand All @@ -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<DerivedT, PacketT>::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).


67 changes: 18 additions & 49 deletions docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
2 changes: 2 additions & 0 deletions docs/protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ Protocol {#protocol}

Dhyara Commnunication Protocol

[TOC]

Routing Table
--------------

Expand Down
7 changes: 7 additions & 0 deletions docs/qtify.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
* Frank Enderle <[email protected]>
*/

:root {
--fg: #00732f;
--bg: #f6f6f6;
}

body, table, div, p, dl {
font: 400 13px/1.3 Verdana,DejaVu Sans,Geneva,sans-serif;
}
Expand Down Expand Up @@ -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 {
Expand Down
6 changes: 0 additions & 6 deletions examples/hello-c/main/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 <string.h>
#include <dhyara/dhyara.h>

Expand Down

0 comments on commit fa2ceca

Please sign in to comment.