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.
-
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’ -
Add the aether library to your
CMakeLists.txtand link it with your project:add_subdirectory(aether-client-cpp/aether aether) target_link_libraries(<your_target_name> PRIVATE aether) -
For configuration, the user-provided
user_config.his used. All options with their default values are listed in aether/config.h. Provide the path touser_config.hin theUSER_CONFIGvariable.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.
-
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’ -
Add an aether component to your ESP-IDF project
CMakeLists.txtand 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) -
For configuration, the user-provided
user_config.his used. All options with their default values are listed in aether/config.h. Provide the path touser_config.hin theUSER_CONFIGvariable.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.
-
Add the aether-client-cpp library dependency to your
platformio.ini:lib_deps = https://github.com/aethernetio/aether-client-cpp.git -
Add an aether component to your PlatformIO project
CMakeLists.txtand 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) -
For configuration, the user-provided
user_config.his used. All options with their default values are listed in aether/config.h. Provide the path touser_config.hin theUSER_CONFIGvariable.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.
-
Download an Arduino repository as a ZIP file from the GitHub repository and install it to the Arduino IDE library path.
-
In Arduino IDE, select:
Sketch -> Include Library -> Add .ZIP Library… -
Make
aetherbuild in distillation mode. Read about it in Documentation. Add tobuild_opt.h."-DAE_DISTILLATION=On" -
For configuration, the user-provided
user_config.his used. All options with their default values are listed in src/aether/config.h. Provide the path touser_config.hin theUSER_CONFIGvariable inbuild_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.
-
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 -
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, }; -
Then call the init function:
AetherInit(&aether_config); -
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
AetherUpdatefunction returns the timestamp for the next update call, andAetherWaitwaits until the timestamp or until a new event occurs. -
AetherEndreturns the exit code from the application. -
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); } -
SendStris an asynchronous operation. The result is provided byMessageSentCb, 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.
-
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’ -
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) -
Configuration is provided via
user_config.h. Default options and values are listed in aether/config.h. Set the path to youruser_config.hin theUSER_CONFIGCMake 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.
-
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’ -
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) -
Configuration is provided via
user_config.h. Default options and values are listed in aether/config.h. Set the path to youruser_config.hin theUSER_CONFIGCMake 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.
-
Add the aether-client-cpp dependency to your
platformio.ini:lib_deps = https://github.com/aethernetio/aether-client-cpp.git -
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) -
Configuration is provided via
user_config.h. Default options and values are listed in aether/config.h. Set the path to youruser_config.hin theUSER_CONFIGCMake 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.
-
Download the Arduino library as a ZIP archive from the GitHub repository and install it into your Arduino IDE libraries folder.
-
In Arduino IDE, select:
Sketch -> Include Library -> Add .ZIP Library… -
Build aether in distillation mode. Read about it in Documentation. Add this to
build_opt.h:"-DAE_DISTILLATION=On" -
Configuration is provided via
user_config.h. Default options and values are listed in src/aether/config.h. Placeuser_config.hnear your sketch and set this definition inbuild_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.
-
First, include the aether library header.
Arduino IDE:
#include “aether_lib.h” - for Arduino IDEOther platforms:
#include “aether/all.h” - for the rest -
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; -
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},} }} ); })); -
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
StatusEventand 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);}} }); -
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()}; }); } -
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
-
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
-
Create two clients and wait for the registration to be completed
var aether1 = new AetherCloudClient().waitStart(10); var aether2 = new AetherCloudClient().waitStart(10); -
Configure receiving messages
var messageFuture = new ARFuture<string>(); aether1.onMessage((uid, msg) -> messageFuture.tryDone(new String(msg))); -
Send a message to aether1
aether2.sendMessage(aether1.getUid(), "Hello World!".getBytes()); -
We are waiting for a message
if (messageFuture.waitDoneSeconds(10)) { System.out.println("receive the message: " + messageFuture.get()); } else { throw new IllegalStateException(); } -
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
-
Open a Terminal in Your Project Folder bash
cd /path/to/your/project -
Initialize the Project
npm init -y -
Add dependency to aether
npm install https://nexus.aethernet.io/repository/npm-private/cloud-client/-/cloud-client-latest.tgz -
Install TypeScript & Node.js Type Definitions (if needed)
npm install typescript @types/node --save-dev -
Initialize TypeScript Configuration
npx tsc --init -
Create Project Structure
mkdir src touch src/index.ts -
Add Scripts to package.json
{ "scripts": { "build": "tsc", "start": "node dist/index.js", "dev": "tsc --watch & nodemon dist/index.js" } }
Usage
-
Open the index.ts file
-
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); -
Configure receiving messages
const messageFuture = new ARFuture<string>(); aether1.onMessage((uid: string, msg: Uint8Array) => { messageFuture.tryDone(Buffer.from(msg).toString()); }); -
Send a message to aether1
aether2.sendMessage(aether1.getUid(), "Hello World!".getBytes()); -
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"); } -
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);