Background Decoration

Getting Started

Integration with the Æthernet infrastructure works by linking the client libraries on connected devices. All currently supported platforms and languages are listed on this page. We plan to expand this list to support more devices.

C

The Æthernet C++ library currently supports a few platforms: Desktop (Linux, Windows, macOS) and ESP32 through Arduino IDE, PlatformIO, and ESP-IDF. We use CMake as a build system; more options will be added in the future. It is a static library, ideally integrated and built within your project. The library also provides a C API.

Follow the integration steps below to install the relevant library.

Desktop

The C API for Desktop is available through the aether-client-cpp GitHub repository.

  1. Add aether-client-cpp to your project as a submodule. Run in the console:

    git submodule add ‘https://github.com/aethernetio/aether-client-cpp.git’
  2. Add the aether library to your CMakeLists.txt and link it with your project:

    add_subdirectory(aether-client-cpp/aether aether)
    target_link_libraries(<your_target_name> PRIVATE aether)
  3. For configuration, the user-provided user_config.h is used. All options with their default values are listed in aether/config.h. Provide the path to user_config.h in the USER_CONFIG variable.

    set(USER_CONFIG "user_config.h" CACHE PATH "" FORCE)

To verify your integration, try our code example below in the tutorial.

ESP IDF

The C API for ESP-IDF uses the aether-client-cpp repository.

  1. Add aether-client-cpp to your project as a submodule. Run in the console:

    git submodule add ‘https://github.com/aethernetio/aether-client-cpp.git’
  2. Add an aether component to your ESP-IDF project CMakeLists.txt and require it for your application component:

    list(APPEND EXTRA_COMPONENT_DIRS "aether-client-cpp/aether”)
    idf_component_register(SRCS ${src_list}
          INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}
          PRIV_REQUIRES aether)
  3. For configuration, the user-provided user_config.h is used. All options with their default values are listed in aether/config.h. Provide the path to user_config.h in the USER_CONFIG variable.

    set(USER_CONFIG "user_config.h" CACHE PATH "" FORCE)

To verify your integration, try our code example below in the tutorial.

PlatformIO

PlatformIO can fetch the aether-client-cpp dependency automatically.

  1. Add the aether-client-cpp library dependency to your platformio.ini:

    lib_deps = https://github.com/aethernetio/aether-client-cpp.git
  2. Add an aether component to your PlatformIO project CMakeLists.txt and require it for your application component:

    list(APPEND EXTRA_COMPONENT_DIRS "${CMAKE_SOURCE_DIR}/.pio/libdeps/${configName}/Aether/aether")
    idf_component_register(SRCS ${src_list}
          INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}
          PRIV_REQUIRES aether)
  3. For configuration, the user-provided user_config.h is used. All options with their default values are listed in aether/config.h. Provide the path to user_config.h in the USER_CONFIG variable.

    set(USER_CONFIG "user_config.h" CACHE PATH "" FORCE)

To verify your integration, try our code example below in the tutorial.

Arduino IDE

The Arduino client has its own repository: aether-client-arduino-library.

  1. Download an Arduino repository as a ZIP file from the GitHub repository and install it to the Arduino IDE library path.

  2. In Arduino IDE, select:
    Sketch -> Include Library -> Add .ZIP Library…

  3. Make aether build in distillation mode. Read about it in Documentation. Add to build_opt.h.

    "-DAE_DISTILLATION=On"
  4. For configuration, the user-provided user_config.h is used. All options with their default values are listed in src/aether/config.h. Provide the path to user_config.h in the USER_CONFIG variable in build_opt.h.

    “-DUSER_CONFIG=user_config.h”‘

To verify your integration, try our code example below in the tutorial.

Usage

This example configures the Æthernet library with one client, waits for a message, and sends “Hello!” as a response.

  1. First you need to include the aether library header.

    Include the aether header in your code:

    #include “aether_lib.h” - for Arduino IDE
    #include "aether/capi.h" - for the rest
  2. Then provide configuration for your application. We need to configure the library and client. You may think about them like a messaging app and user account. Instead of TEST_UID, you should provide your application ID.

    ClientConfig client_config = { 
      .id = "example",
      .parent_uid = CUidFromString(TEST_UID),
      .message_received_cb = MessageReceived,
    };
    AetherConfig aether_config = {
      .default_client = &client_config,
    };

    Though this is enough for most applications. Typically you should configure a WiFi adapter for ESP32:

    AeWifiAdapterConf wifi_adapter_conf = {    
      .type = AeWifiAdapter,
      .ssid = WIFI_SSID,
      .password = WIFI_PASSWORD,
    };
    AdapterBase* adapter_confs = (AdapterBase*)&wifi_adapter_conf;
    Adapters adapters = {
      .count = 1,
      .adapters = &adapter_confs,
    };
    AetherConfig aether_config = {
      .adapters = &adapters,
      .default_client = &client_config,
    };
  3. Then call the init function:

    AetherInit(&aether_config);
  4. Notice almost all operations in the aether library are asynchronous. You need to integrate invoking the update function into your update loop.

    while (AetherExcited() == AE_NOK) {
      uint64_t time = AetherUpdate();
      AetherWait(time);
    }

    The AetherUpdate function returns the timestamp for the next update call, and AetherWait waits until the timestamp or until a new event occurs.

  5. AetherEnd returns the exit code from the application.

  6. To receive a message, a callback is used:

    void MessageReceived(AetherClient* client, CUid sender, void const* data,
                         size_t size, void* user_data) {
      printf(">>> Received message size: %zu text: %s\n", size, (char const*)data);
      // send response
      char const* message = "Hello!";
      SendStr(sender, message, MessageSentCb, NULL);
    }
  7. SendStr is an asynchronous operation. The result is provided by MessageSentCb, which is invoked when a message is written to the network interface.

    void MessageSentCb(ActionStatus status, void* user_data) {
      // close app
      AetherExit(0);
    }

For more examples, check our GitHub repository.

C++

The Æthernet C++ library currently supports Desktop (Linux, Windows, macOS) and ESP32 via Arduino IDE, PlatformIO, and ESP-IDF. It is a static library, so the recommended approach is to integrate and build it as part of your project. We currently use CMake as the build system; more options will be added later.

Follow the steps below to integrate the library for your target platform.

Desktop

The C++ Æthernet client library is available in the aether-client-cpp GitHub repository.

  1. Add aether-client-cpp to your project as a submodule. Run in your terminal:

    git submodule add ‘https://github.com/aethernetio/aether-client-cpp.git’
  2. Add the aether library to your CMakeLists.txt and link it with your target:

    add_subdirectory(aether-client-cpp/aether aether)
    target_link_libraries(<your_target_name> PRIVATE aether)
  3. Configuration is provided via user_config.h. Default options and values are listed in aether/config.h. Set the path to your user_config.h in the USER_CONFIG CMake variable:

    set(USER_CONFIG "user_config.h" CACHE PATH "" FORCE)

To verify your integration, try our code example further in the tutorial.

ESP IDF

Use the aether-client-cpp repository. You can clone it, add it as a submodule, or copy it next to your project.

  1. Add aether-client-cpp to your project as a submodule. Run in your terminal:

    git submodule add ‘https://github.com/aethernetio/aether-client-cpp.git’
  2. Add the aether component to your ESP-IDF project and require it from your application component:

    list(APPEND EXTRA_COMPONENT_DIRS "aether-client-cpp/aether”)
    idf_component_register(SRCS ${src_list}
          INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}
          PRIV_REQUIRES aether)
  3. Configuration is provided via user_config.h. Default options and values are listed in aether/config.h. Set the path to your user_config.h in the USER_CONFIG CMake variable:

    set(USER_CONFIG "user_config.h" CACHE PATH "" FORCE)

To verify your integration, try our code example further in the tutorial.

PlatformIO

PlatformIO can fetch the aether-client-cpp dependency automatically.

  1. Add the aether-client-cpp dependency to your platformio.ini:

    lib_deps = https://github.com/aethernetio/aether-client-cpp.git
  2. Add the aether component to your project CMakeLists.txt and require it from your application component:

    list(APPEND EXTRA_COMPONENT_DIRS "${CMAKE_SOURCE_DIR}/.pio/libdeps/${configName}/Aether/aether")
    idf_component_register(SRCS ${src_list}
          INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}
          PRIV_REQUIRES aether)
  3. Configuration is provided via user_config.h. Default options and values are listed in aether/config.h. Set the path to your user_config.h in the USER_CONFIG CMake variable:

    set(USER_CONFIG "user_config.h" CACHE PATH "" FORCE)

To verify your integration, try our code example further in the tutorial.

Arduino IDE

The Arduino client has its own repository: aether-client-arduino-library.

  1. Download the Arduino library as a ZIP archive from the GitHub repository and install it into your Arduino IDE libraries folder.

  2. In Arduino IDE, select:
    Sketch -> Include Library -> Add .ZIP Library…

  3. Build aether in distillation mode. Read about it in Documentation. Add this to build_opt.h:

    "-DAE_DISTILLATION=On"
  4. Configuration is provided via user_config.h. Default options and values are listed in src/aether/config.h. Place user_config.h near your sketch and set this definition in build_opt.h:

    “-DUSER_CONFIG=user_config.h”‘

To verify your integration, try our code example further in the tutorial.

Usage

Code examples are almost the same for all supported platforms.

  1. First, include the aether library header.

    Arduino IDE:

    #include “aether_lib.h” - for Arduino IDE

    Other platforms:

    #include “aether/all.h” - for the rest
  2. Then create the core objects: the Æthernet application, a client, and a message stream. You can think of them as a messaging app, a user account, and a dedicated chat.

    ae::RcPtr<AetherApp> aether_app;
    ae::Client::ptr client;
    ae::RcPtr<P2pStream> message_stream;
  3. Create the Æthernet application:

    aether_app = ae::AetherApp::Construct(ae::AetherAppContext{});

    Using ae::AetherAppContext{} works for most cases. On ESP32, you typically need to provide a WiFi adapter factory with your WiFi settings:

    aether_app = ae::AetherApp::Construct(
          ae::AetherAppContext{}
         .AddAdapterFactory([&](auto const& context) {
               return ae::WifiAdapter::ptr::Create( context.domain(), context.aether(), context.poller(), context.dns_resolver(), ae::WiFiInit{{ae::WifiCreds{
                        /* .ssid*/ std::string{WIFI_SSID},
                        /* .password*/ std::string{WIFI_PASSWORD},}
                    }} );
    }));
  4. Select the Æthernet client. Selection means registering a new client or loading one from persistent storage. Both are asynchronous operations, so you need to subscribe to StatusEvent and wait until the action completes.

    Replace <You application ID> with your application ID (see your application ID).

    constexpr auto kParentUid =ae::Uid::FromString("<You application ID>");
    auto select_client = aether_app->aether()->SelectClient(kParentUid, "Controller");
    select_client->StatusEvent().Subscribe(ae::ActionHandler{ 
        ae::OnResult{[](auto& action){ClientSelected(action.client())} 
       ae::OnError{[&]() { std::cerr << " !!! Client selection error"; aether_app->Exit(1);}}
    });
  5. After the client is selected, create a message stream to another client. You can then send and receive messages through the stream:

    void ClientSelected(ae::Client::ptr const& c) {
       client = c;
       message_stream =
       client->message_stream_manager().CreateStream(
                              ae::Uid::FromString(“<Another client uid>”));
       // send message
       auto message= std::string_view{"Hello"};
      message_stream->Write({std::begin(message), std::end(message)});
    
      // receive messages
      message_stream->out_data_event().Subscribe(
           [&](std::vector<std::uint8_t> const& data){
               /* ... your code for message processing … */
              std::cout << “Received ” << std::string_view{data.data(), data.size()};
       });   
     } 
  6. Lastly, integrate aether_app into your event loop (or into your Arduino loop function):

    if(!aehter_app->IsExcited()){
      auto next_time = aether_app->Update(ae::Now());
      aether_app->WaitUntil(next_time);
    }

For more examples, check our GitHub repository.

Java

Install

  1. Add the following code to your build file ( build.gradle )

    repositories {
        maven {
            url 'https://nexus.aethernet.io/repository/maven-releases/'
        }
    }
    
    dependencies {
        implementation 'io.aether:cloud-client:+'
    }

Usage

  1. Create two clients and wait for the registration to be completed

    var aether1 = new AetherCloudClient().waitStart(10);
    var aether2 = new AetherCloudClient().waitStart(10);
  2. Configure receiving messages

    var messageFuture = new ARFuture<string>();
    aether1.onMessage((uid, msg)
    -> messageFuture.tryDone(new String(msg)));
  3. Send a message to aether1

    aether2.sendMessage(aether1.getUid(), "Hello World!".getBytes());
  4. We are waiting for a message

    if (messageFuture.waitDoneSeconds(10)) {
      System.out.println("receive the message: " + messageFuture.get());
    } else {
      throw new IllegalStateException();
    }
  5. As a result, you should get this code in your SomeJavaClass. java file

    import io.aether.cloud.client.AetherCloudClient;
    import io.aether.utils.futures.ARFuture;
    
    public class SomeJavaClass {
        public static void main(String[] args) {
            var aether1 = new AetherCloudClient().waitStart(10);
            var aether2 = new AetherCloudClient().waitStart(10);
            var messageFuture = new ARFuture<string>();
            aether1.onMessage((uid, msg)
            -> messageFuture.tryDone(new String(msg)));
    
            aether2.sendMessage(aether1.getUid(), "Hello World!".getBytes());
    
            if (messageFuture.waitDoneSeconds(10)) {
                System.out.println("receive the message: " + messageFuture.get());
            } else {
                throw new IllegalStateException();
            }
        }
    }

TypeScript

Install

  1. Open a Terminal in Your Project Folder bash

    cd /path/to/your/project
  2. Initialize the Project

    npm init -y
  3. Add dependency to aether

    npm install https://nexus.aethernet.io/repository/npm-private/cloud-client/-/cloud-client-latest.tgz
  4. Install TypeScript & Node.js Type Definitions (if needed)

    npm install typescript @types/node --save-dev
  5. Initialize TypeScript Configuration

    npx tsc --init
  6. Create Project Structure

    mkdir src
    touch src/index.ts
  7. Add Scripts to package.json

    {
      "scripts": {
        "build": "tsc",
        "start": "node dist/index.js",
        "dev": "tsc --watch & nodemon dist/index.js"
      }
    }

Usage

  1. Open the index.ts file

  2. Create two Æthernet clients and wait for the registration to be completed

    const aether1 = await new AetherCloudClient().waitStart(10);
    const aether2 = await new AetherCloudClient().waitStart(10);
  3. Configure receiving messages

    const messageFuture = new ARFuture<string>();
    aether1.onMessage((uid: string, msg: Uint8Array) => {
        messageFuture.tryDone(Buffer.from(msg).toString());
    });
  4. Send a message to aether1

    aether2.sendMessage(aether1.getUid(), "Hello World!".getBytes());
  5. We are waiting for a message

    try {
       const message = await messageFuture.waitDoneSeconds(10);
       console.log("receive the message:", message);
    } catch (e) {
       throw new Error("Failed to receive message in time");
    }
  6. As a result, you should get this code in your file

    import { AetherCloudClient } from 'io.aether.cloud.client';
    import { ARFuture } from 'io.aether.utils.futures';
    
    async function main() {
        const aether1 = await new AetherCloudClient().waitStart(10);
        const aether2 = await new AetherCloudClient().waitStart(10);
        
        const messageFuture = new ARFuture<string>();
        
        aether1.onMessage((uid: string, msg: Uint8Array) => {
            messageFuture.tryDone(Buffer.from(msg).toString());
        });
    
        await aether2.sendMessage(aether1.getUid(), Buffer.from("Hello World!"));
        
        try {
            const message = await messageFuture.waitDoneSeconds(10);
            console.log("receive the message:", message);
        } catch (e) {
            throw new Error("Failed to receive message in time");
        }
    }
    
    main().catch(console.error);