Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
60e5573
Add ESP32 prepared send integration scaffold
NikolayChirkov Jun 29, 2026
c9a3cf8
Fetch aether-client-cpp via CPM on prepared-packet-v0 branch.
NikolayChirkov Jun 30, 2026
4ac8b9c
Cursor work.
dmitriyk-aethernet Jul 1, 2026
dd9ef03
Merge branch 'thermometer-prepared-send-v0' of https://github.com/aet…
dmitriyk-aethernet Jul 2, 2026
9623f33
Updating PreparedSend functionality.
dmitriyk-aethernet Jul 2, 2026
2bd8214
Test.
dmitriyk-aethernet Jul 3, 2026
3680297
Removing Test.
dmitriyk-aethernet Jul 3, 2026
951d56f
Changed temperature format from std:uint16_t to std::string and added…
dmitriyk-aethernet Jul 3, 2026
1bcd505
Fixing WiFi connection.
dmitriyk-aethernet Jul 9, 2026
3a82d18
Removing UAP.
dmitriyk-aethernet Jul 10, 2026
31617dc
Add optimizations.
dmitriyk-aethernet Jul 20, 2026
a095e4e
Some Wi-Fi optimization has been added.
dmitriyk-aethernet Jul 21, 2026
56eefb8
Fixing restoring BSSID + Channel.
dmitriyk-aethernet Jul 22, 2026
0eda112
Fixing mikrotik association.
dmitriyk-aethernet Jul 23, 2026
85bd81d
Changing the initialization method of the adress_is_valid and bs_is_v…
dmitriyk-aethernet Jul 24, 2026
865b068
update to adopt/prepared-packet-v0
BartolomeyKant Aug 26, 2026
af35277
Add Wi-Fi lifecycle bench and ignore-BSSID control experiment.
NikolayChirkov Aug 28, 2026
bfb86c0
Pin Aether BSSID-cache removal and record ESP32-C6 verify runs.
NikolayChirkov Aug 28, 2026
8b887ce
Align desktop CPM aether pin with BSSID-removal SHA.
NikolayChirkov Aug 28, 2026
cf33566
Add no-sleep prepared-message E2E bench and hardware results.
NikolayChirkov Aug 28, 2026
669ccdc
Fix prepared Wi-Fi cache and add silent 5x20 E2E experiment.
NikolayChirkov Aug 28, 2026
f2045c8
Add gateway MAC cache and static ARP on prepared UDP path.
NikolayChirkov Aug 28, 2026
631bc2f
Add keep-Wi-Fi-up prepared 5x20 A/B and report 96/100 delivery.
NikolayChirkov Aug 28, 2026
14d1bb7
Add silent Wi-Fi single-factor prepared bisect and 13x20 results.
NikolayChirkov Aug 29, 2026
adf5619
Add silent fastest-path prepared Wi-Fi campaign and 710ms winner report.
NikolayChirkov Aug 29, 2026
2c50b1f
Add late TX-done callback prepared path and WPA2 430ms VAL200 report.
NikolayChirkov Aug 29, 2026
40a0e9b
Validate short PRE=25 callback path: N50 screen and 230ms VAL200.
NikolayChirkov Aug 29, 2026
5b878e1
Add deep-sleep 5x50 prepared E2E with RTC cache and 250ms HOT median.
NikolayChirkov Aug 29, 2026
724a173
Fix TX-done FULL loop: enable early entry and report FullReason.
NikolayChirkov Aug 29, 2026
e7f4804
Record pushed SHA in TXD3 FULL-loop diagnostic report.
NikolayChirkov Aug 29, 2026
5f318b9
Add MAC retry-limit diagnostic campaign and report.
NikolayChirkov Aug 29, 2026
451f526
Add boot/Wi-Fi HOT path optimization campaign and VAL100.
NikolayChirkov Aug 30, 2026
a6c577a
Add aethernetio AP 3x10 prepared-path E2E experiment.
NikolayChirkov Aug 30, 2026
a0a69f3
Add quiet FULL aethernetio baseline receiver stats and uint32 seq.
NikolayChirkov Aug 30, 2026
fbd9bee
Updated by Codex.
dmitriyk-aethernet Aug 31, 2026
36c1e84
Merge branch 'main' into thermometer-prepared-send-v0
dmitriyk-aethernet Sep 11, 2026
d02eaf7
Updating UID & STCC4 sensor code.
dmitriyk-aethernet Sep 11, 2026
668674b
Fixing Ci/Cd build.
dmitriyk-aethernet Sep 11, 2026
6ac0a0d
Fixing if equation.
dmitriyk-aethernet Sep 11, 2026
39e2a80
Fixing Ci/Cd.
dmitriyk-aethernet Sep 11, 2026
2be70fc
Fixing Ci/Cd.
dmitriyk-aethernet Sep 11, 2026
fdcfe53
Fixing Ci/Cd.
dmitriyk-aethernet Sep 11, 2026
d2f26c1
Fixing Ci/Cd.
dmitriyk-aethernet Sep 11, 2026
996ded3
Updating STCC4 sensor code.
dmitriyk-aethernet Sep 14, 2026
fb44432
Add Led sleep power off.
dmitriyk-aethernet Sep 14, 2026
e8a10bf
Add ADC batterry checking task. Power configuration code.
dmitriyk-aethernet Sep 16, 2026
d481dbb
Updating peripherals_off function.
dmitriyk-aethernet Sep 16, 2026
a5bd8d9
Add ULP_WAKEUP_TIMER_US definition.
dmitriyk-aethernet Sep 17, 2026
85eae52
Fixing Copyright.
dmitriyk-aethernet Sep 17, 2026
e08e4ee
Remove benchmark harnesses and experimental results from thermometer
dmitriyk-aethernet Sep 18, 2026
8096991
Use milliseconds for thermometer timing settings
dmitriyk-aethernet Sep 18, 2026
dbcabc3
Updating Aether library git branch in CMake.txt file.
dmitriyk-aethernet Sep 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .clangd
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CompileFlags:
Remove: [-f*, -m*]
18 changes: 10 additions & 8 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
sdkconfig*
!sdkconfig.defaults

# Build directory
build/

# IDE directory
.pio
.vscode
# SDK configuration
sdkconfig*
!sdkconfig.defaults
!sdkconfig.defaults.*

# IDE directories
.pio/
.vscode/

# CLang
.clangd
# Clang
.clangd
Empty file removed .gitmodules
Empty file.
532 changes: 532 additions & 0 deletions .vscode/c_cpp_properties.json

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"Jason2866.esp-decoder",
"pioarduino.pioarduino-ide",
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack",
"pioarduino.pioarduino-ide"
]
}
44 changes: 44 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// AUTOMATICALLY GENERATED FILE. PLEASE DO NOT MODIFY IT MANUALLY
//
// PlatformIO Debugging Solution
//
// Documentation: https://docs.platformio.org/en/latest/plus/debugging.html
// Configuration: https://docs.platformio.org/en/latest/projectconf/sections/env/options/debug/index.html

{
"version": "0.2.0",
"configurations": [
{
"type": "platformio-debug",
"request": "launch",
"name": "PIO Debug",
"executable": "G:/projects/prj_aether/GitHub/temperature-sensor/.pio/build/esp32-c6-devkitc-1/firmware.elf",
"projectEnvName": "esp32-c6-devkitc-1",
"toolchainBinDir": "C:/Users/Vise/.platformio/packages/toolchain-riscv32-esp/bin",
"internalConsoleOptions": "openOnSessionStart",
"preLaunchTask": {
"type": "PlatformIO",
"task": "Pre-Debug"
}
},
{
"type": "platformio-debug",
"request": "launch",
"name": "PIO Debug (skip Pre-Debug)",
"executable": "G:/projects/prj_aether/GitHub/temperature-sensor/.pio/build/esp32-c6-devkitc-1/firmware.elf",
"projectEnvName": "esp32-c6-devkitc-1",
"toolchainBinDir": "C:/Users/Vise/.platformio/packages/toolchain-riscv32-esp/bin",
"internalConsoleOptions": "openOnSessionStart"
},
{
"type": "platformio-debug",
"request": "launch",
"name": "PIO Debug (without uploading)",
"executable": "G:/projects/prj_aether/GitHub/temperature-sensor/.pio/build/esp32-c6-devkitc-1/firmware.elf",
"projectEnvName": "esp32-c6-devkitc-1",
"toolchainBinDir": "C:/Users/Vise/.platformio/packages/toolchain-riscv32-esp/bin",
"internalConsoleOptions": "openOnSessionStart",
"loadMode": "manual"
}
]
}
27 changes: 27 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"idf.cmakeCompilerArgs": [
"-G",
"Ninja",
"-DCMAKE_BUILD_TYPE=Debug",
"-DIDF_TARGET=esp32c6",
"-DBOARD=BOARD_AETHER_ESP32_C6",
"-DWIFI_SSID=Test1234",
"-DWIFI_PASSWORD=Test2345",
"-DUSER_CONFIG=../main/user_config.h"
],
"idf.currentSetup": "G:\\dev\\esp32\\v6.1\\esp-idf",
"idf.openOcdConfigs": [
"board/esp32c6-lpcore-builtin.cfg"
],
"idf.customExtraVars": {
"IDF_TARGET": "esp32c6"
},
"clangd.path": "G:\\dev\\esp32\\.espressif\\v6.0.2\\tools\\esp-clang\\esp-20.1.1_20250829\\esp-clang\\bin\\clangd.exe",
"clangd.arguments": [
"--background-index",
"--query-driver=**",
"--compile-commands-dir=g:\\projects\\prj_aether\\GitHub\\temperature-sensor\\build"
],
"idf.flashType": "UART",
"idf.portWin": "COM9"
}
7 changes: 4 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(AE_FILTRATION On CACHE BOOL "Set filtration mode by default" FORCE)

# Add a user-provided config file, which will be included as a regular .h file.
set(USER_CONFIG "${CMAKE_CURRENT_LIST_DIR}/main/user_config.h" CACHE FILEPATH
"Path to the Aether user configuration header")
# Add a user-provided config file, which will be included as a regular .h file
set(USER_CONFIG "../main/user_config.h" CACHE PATH "" FORCE)
# ${USER_CONFIG} must be an absolute path or a path to something listed in include directories

include_directories(${CMAKE_CURRENT_LIST_DIR}/main)

if (CM_PLATFORM STREQUAL "ESP32")
Expand Down
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
## Build Instructions
### Init dependencies
It uses git submodules to manage dependencies.
```
git submodule update --init --remote ./aether-client-cpp
```
`aether-client-cpp` is fetched automatically by [CPM.cmake](https://github.com/cpm-cmake/CPM.cmake) during the CMake configure step (branch `prepared-packet-v0`).

### For desktop
For desktop a regular cmake project is used.
Expand Down
47 changes: 36 additions & 11 deletions main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ cmake_minimum_required(VERSION 3.16.0)
list(APPEND src_list
"main.cpp"
"controller.cpp"
"prepared_send/prepared_send.cpp"
)

list(APPEND bme68x_srcs
Expand Down Expand Up @@ -47,7 +48,8 @@ if(NOT CM_PLATFORM)

include(../cmake/CPM.cmake)

CPMAddPackage(URI "https://github.com/aethernetio/aether-client-cpp.git#main")
# Pin exact SHA. Override locally with -DCPM_aether-client-cpp_SOURCE=<path>.
CPMAddPackage(URI "https://github.com/aethernetio/aether-client-cpp.git")

add_executable(${PROJECT_NAME} ${src_list} ${sleeping_src} ${sensors_src})
set(TARGET_NAME ${PROJECT_NAME})
Expand All @@ -61,14 +63,17 @@ else()
set(BOARD "" CACHE STRING "Selected board - see user_config.h")

#ESP32 CMake
idf_component_register(SRCS ${src_list} ${bme68x_srcs} ${sleeping_src} ${sensors_src}
idf_component_register(SRCS "peripherals/power.cpp" "peripherals/adc.cpp" ${src_list} ${bme68x_srcs} ${sleeping_src} ${sensors_src}
INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/.."
REQUIRES soc ulp
PRIV_REQUIRES
idf::esp_adc
idf::esp_wifi
idf::driver
idf::esp_driver_i2c
idf::esp_driver_tsens
idf::esp_driver_gpio
idf::esp_driver_usb_serial_jtag
idf::esp_http_server
idf::esp_event
idf::esp_timer
Expand All @@ -77,7 +82,8 @@ else()

set(TARGET_NAME "${COMPONENT_LIB}")
include(../cmake/CPM.cmake)
CPMAddPackage(URI "https://github.com/aethernetio/aether-client-cpp.git#main")
# Pin exact SHA. Override locally with -DCPM_aether-client-cpp_SOURCE=<path>.
CPMAddPackage(URI "https://github.com/aethernetio/aether-client-cpp.git#157aadbec8e7b852d0f89274307ff7cb8103e5f7")

target_link_libraries(${TARGET_NAME} PRIVATE aether)

Expand All @@ -91,17 +97,13 @@ else()
target_compile_definitions(${TARGET_NAME} PRIVATE "BOARD=${BOARD}")
endif()

# Define the path to the board configuration file in the build folder
set(ULP_BOARD_FILE "${CMAKE_BINARY_DIR}/ulp_board_config.cmake")

# Write the value of the BOARD variable to this file
# ulp_add_project() configures a separate ExternalProject, so it does not
# inherit ordinary CMake variables from this component. Forward BOARD via
# ESP-IDF's arguments for CMake subprojects.
if(NOT "${BOARD}" STREQUAL "")
file(WRITE "${ULP_BOARD_FILE}" "set(BOARD \"${BOARD}\")\n")
else()
file(WRITE "${ULP_BOARD_FILE}" "set(BOARD \"\")\n")
idf_build_set_property(EXTRA_CMAKE_ARGS "-DBOARD=${BOARD}" APPEND)
endif()

# We call the project addition as usual
ulp_add_project("ulp_main" "${CMAKE_CURRENT_LIST_DIR}/../ulp")

else()
Expand All @@ -113,3 +115,26 @@ endif()
if (NOT "${SERVICE_UID}" STREQUAL "")
target_compile_definitions(${TARGET_NAME} PRIVATE "SERVICE_UID=\"${SERVICE_UID}\"")
endif()

# Thermometer prepared-send settings (override via -D...).
if(NOT DEFINED AETHER_PREPARED_NONCE_RESERVE)
set(AETHER_PREPARED_NONCE_RESERVE 10 CACHE STRING "Prepared nonce reserve count")
endif()
if(NOT DEFINED AETHER_PREPARED_HOT_SLEEP_MS)
set(AETHER_PREPARED_HOT_SLEEP_MS 100000 CACHE STRING "Deep sleep milliseconds after send")
endif()
if(NOT DEFINED AETHER_PREPARED_POST_SEND_HOLD_MS)
set(AETHER_PREPARED_POST_SEND_HOLD_MS 300 CACHE STRING "Post-send TX hold ms on prepared path")
endif()
if(NOT DEFINED AETHER_PREPARED_HOT_WIFI_TIMEOUT_MS)
set(AETHER_PREPARED_HOT_WIFI_TIMEOUT_MS 15000 CACHE STRING "Prepared WiFi connect timeout ms")
endif()
if(NOT DEFINED AETHER_PREPARED_HOT_WIFI_MAX_RETRY)
set(AETHER_PREPARED_HOT_WIFI_MAX_RETRY 10 CACHE STRING "Prepared WiFi max association retries")
endif()
target_compile_definitions(${TARGET_NAME} PRIVATE
"AETHER_PREPARED_NONCE_RESERVE=${AETHER_PREPARED_NONCE_RESERVE}"
"AETHER_PREPARED_HOT_SLEEP_MS=${AETHER_PREPARED_HOT_SLEEP_MS}"
"AETHER_PREPARED_POST_SEND_HOLD_MS=${AETHER_PREPARED_POST_SEND_HOLD_MS}"
"AETHER_PREPARED_HOT_WIFI_TIMEOUT_MS=${AETHER_PREPARED_HOT_WIFI_TIMEOUT_MS}"
"AETHER_PREPARED_HOT_WIFI_MAX_RETRY=${AETHER_PREPARED_HOT_WIFI_MAX_RETRY}")
2 changes: 0 additions & 2 deletions main/boards/aether_esp32_c6.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
# error "Illegal CPU! It must be an ESP32C6."
#endif

#include "soc/gpio_num.h"

#ifndef BOARD_HAS_ULP
# define BOARD_HAS_ULP 0
#endif
Expand Down
83 changes: 73 additions & 10 deletions main/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,16 @@
*/

#include <chrono>
#include <memory>
#include <cstdlib>
#include <iostream>

#include <memory>

#include "aether/all.h"
#include "aether/env.h"
#include "prepared_send/prepared_send.h"
#include "sensors/sensors.h"
#include "sleeping/sleeping.h"


using namespace std::chrono_literals;

/**
Expand All @@ -34,7 +33,7 @@ using namespace std::chrono_literals;
* For real applications you should register your own uid \see aethernet.io
*/
static constexpr auto kParentUid =
ae::Uid::FromString("3ac93165-3d37-4970-87a6-fa4ee27744e4");
ae::Uid::FromString("b1ac52c8-8d94-bd39-4c01-a631ac594165");
/**
* \brief Uid of aether service for store the temperature values.
* TODO: add actual uid
Expand All @@ -43,7 +42,7 @@ static constexpr auto kServiceUid =
#ifdef SERVICE_UID
ae::Uid::FromString(SERVICE_UID);
#else
ae::Uid::FromString("752fa297-ec98-47d9-8def-a3ef80ecca42");
ae::Uid::FromString("6b24b68f-063f-4fe3-8d5a-dc35386983fb");
#endif

#ifdef ESP_PLATFORM
Expand Down Expand Up @@ -95,9 +94,52 @@ static std::unique_ptr<ae::AetherApp> aether_app;
static ae::Client::ptr client;
static std::unique_ptr<ae::P2pStream> message_stream;

#ifndef AETHER_PREPARED_HOT_SLEEP_MS
# define AETHER_PREPARED_HOT_SLEEP_MS 100000
#endif

static constexpr auto kPreparedHotSleep =
std::chrono::milliseconds{AETHER_PREPARED_HOT_SLEEP_MS};

static constexpr std::size_t kPreparedNonceReserve =
#ifdef AETHER_PREPARED_NONCE_RESERVE
AETHER_PREPARED_NONCE_RESERVE;
#else
32;
#endif

void setup() {
std::cout << ae::Format("Setup {:%Y-%m-%d %H:%M:%S}\n") << ae::Now();

#if defined(ESP_PLATFORM)
// Prepared-send hot path: try to send current temperature without creating
// full AetherApp. Any error falls back to the normal full boot below.
{
std::int16_t hot_temperature = {};
ReadSensors(&hot_temperature, nullptr, nullptr, nullptr, nullptr);

#if BOARD_HAS_STCC4 == 1 && BOARD_HAS_ULP == 0
if (stcc4_error != 0) {
std::cerr << " !!! STCC4 read failed; skipping prepared sensor send\n";
} else
#endif
{
auto hot_status =
temp_sensor::prepared_send::TryHotWakePreparedSend(hot_temperature);

std::cout << ae::Format(" >>> Prepared hot path status: {}\n",
temp_sensor::prepared_send::ToString(hot_status));

if (hot_status == temp_sensor::prepared_send::HotSendStatus::kSent) {
auto sleep_until =
std::chrono::system_clock::now() + kPreparedHotSleep;
DeepSleep(sleep_until, sleep_until, 3000);
return;
}
}
}
#endif

aether_app = ae::AetherApp::Construct(
ae::AetherAppContext{}
#if AE_DISTILLATION
Expand Down Expand Up @@ -198,6 +240,14 @@ void UpdateSensors() {
std::uint32_t co2 = {};

ReadSensors(&temperature, &humidity, nullptr, &co2, nullptr);
#if BOARD_HAS_STCC4 == 1 && BOARD_HAS_ULP == 0
if (stcc4_error != 0) {
std::cerr << " !!! STCC4 read failed; no valid sample to send\n";
next_tx_time = ae::Now() + kTxInterval;
SleepReady();
return;
}
#endif
std::cout << ae::Format(" >>> Temperature: [{}], Humidity: [{}], CO2: [{}]\n",
temperature, humidity, co2);
// TODO: add check if wakeup cause is ulp then send value
Expand All @@ -210,16 +260,29 @@ void MessageReceived(ae::DataBuffer const& buffer) {
}

void SendValue(std::int16_t temperature) {
temperature = (temperature/100 + 30) * 3;
auto payload = { 0x03, 0x03, 0x0A, (temperature) & 0xFF, (temperature >> 8) & 0xFF };
auto message = ae::DataBuffer{std::begin(payload), std::end(payload)};
// The stream is not initialized yet
if (!message_stream) {
return;
}

auto message =
temp_sensor::prepared_send::MakeTemperaturePayload(temperature);
std::cout << ae::Format(" [CALL-CHAIN] SendValue payload_size={}\n",
message.size());

next_tx_time = ae::Now() + kTxInterval;
message_stream->Write(std::move(message)).status_event().Subscribe([](auto) {
// Export/refresh prepared block after the full send path has a valid
// stream. If export fails, keep normal behavior and just sleep.
if (client.is_valid() && message_stream) {
temp_sensor::prepared_send::ExportPreparedSendBlock(
client, message_stream->destination(), kPreparedNonceReserve);
}

// with any result ready to sleep
SleepReady();
});

next_tx_time = ae::Now() + kTxInterval;
}

void SleepReady() {
Expand All @@ -236,7 +299,7 @@ void SleepReady() {
go_to_sleep(status.next_service_time);
} else {
std::cout << ">>> Wait for can suspend\n";
client->connectivity_policy()->suspend_allowed_event().Subscribe([&]() {
client->connectivity_policy()->suspend_allowed_event().Subscribe([go_to_sleep]() {
go_to_sleep(client->connectivity_policy()->GetStatus().next_service_time);
});
}
Expand Down
Loading
Loading