diff --git a/.gitignore b/.gitignore index 0896ae0..cda119b 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ exercises/AMY.log exercises/AMY_purged.log exercises/examples/* + +# Reticulum identify (secret) generatued when microReiticulum starts up if not alrady present +transport_identity diff --git a/exercises/306_microReticulum_ble_file_transfer_oled/README.md b/exercises/306_microReticulum_ble_file_transfer_oled/README.md index a7923b0..4c86452 100644 --- a/exercises/306_microReticulum_ble_file_transfer_oled/README.md +++ b/exercises/306_microReticulum_ble_file_transfer_oled/README.md @@ -83,12 +83,15 @@ Host-native environment: ```text jp_native jp_native_peripheral +jp_native_dual ``` `jp_native` builds a Linux console program instead of ESP32 firmware. It uses the host Bluetooth adapter through BlueZ D-Bus, skips the OLED path, and prints received text to stdout. The current jp payload is `texts/little_boy_blue.txt`. `jp_native_peripheral` is the first Linux peripheral/server scaffold. It builds a separate binary that checks for BlueZ `GattManager1` and `LEAdvertisingManager1` support on the host adapter. It does not yet register the full Exercise 306 GATT service or accept a T-Beam connection. +`jp_native_dual` registers both host interfaces in one process: the proven Linux central/client path and the current Linux peripheral/server scaffold. Today it can still use the central path to pair with a T-Beam, while also reporting whether BlueZ exposes the services needed for future host-native peripheral work. + ## Building ### ESP32 @@ -192,6 +195,28 @@ exercises/306_microReticulum_ble_file_transfer_oled/.pio/build/jp_native_periphe This build is expected to report whether the current BlueZ adapter exposes the GATT server and LE advertising managers needed for true Linux peripheral mode. +Build the jp dual-role test binary with: + +```bash +source /home/jlpoole/rnsenv/bin/activate +cd /usr/local/src/microreticulum/microReticulumTbeam +pio run -d exercises/306_microReticulum_ble_file_transfer_oled -e jp_native_dual +``` + +The resulting executable is: + +```text +exercises/306_microReticulum_ble_file_transfer_oled/.pio/build/jp_native_dual/program +``` + +Run it from the repository root: + +```bash +exercises/306_microReticulum_ble_file_transfer_oled/.pio/build/jp_native_dual/program +``` + +For the current T-Beam test, start `jp_native` or `jp_native_dual` first and wait for `BLE linux-central: scanning for Reticulum service`, then RESET the T-Beam. The dual binary is not fully ambidextrous until the Linux peripheral/server scaffold registers the real GATT service and advertisement. + ### AMD64 AMD64 is the same 64-bit x86 Linux target class as Intel x86_64 for this PlatformIO `native` build. On eos, build the same `jp_native` environment on that machine: diff --git a/exercises/306_microReticulum_ble_file_transfer_oled/platformio.ini b/exercises/306_microReticulum_ble_file_transfer_oled/platformio.ini index 53be8de..a768563 100644 --- a/exercises/306_microReticulum_ble_file_transfer_oled/platformio.ini +++ b/exercises/306_microReticulum_ble_file_transfer_oled/platformio.ini @@ -174,6 +174,47 @@ lib_deps = microReticulum=symlink:///usr/local/src/microreticulum/microReticulum lib_compat_mode = off +[env:jp_native_dual] +platform = native +build_type = debug +extra_scripts = pre:scripts/embed_text.py +custom_text_source = texts/little_boy_blue.txt +build_unflags = + -std=gnu++11 +build_flags = + -std=c++17 + -g3 + -ggdb + -Wall + -Wextra + -Wno-missing-field-initializers + -Wno-format + -Wno-unused-parameter + -include stdint.h + -D HOST_NATIVE + -D HOST_BLE_DUAL + -D NATIVE + -D RNS_USE_FS + -D RNS_PERSIST_PATHS + -D USTORE_USE_UNIVERSALFS + -D MSGPACK_USE_BOOST=OFF + -D FILE_TRANSFER_CHUNK_SIZE=32 + -D FILE_TRANSFER_CHUNK_INTERVAL_MS=500 + -D HOST_NODE_LABEL=\"Node-JP-DUAL\" + !pkg-config --cflags gio-2.0 glib-2.0 bluez + !pkg-config --libs gio-2.0 glib-2.0 bluez +build_src_filter = + + + + + + +lib_deps = + ArduinoJson@^7.4.2 + MsgPack@^0.4.2 + https://github.com/attermann/Crypto.git + https://github.com/attermann/microStore.git + microReticulum=symlink:///usr/local/src/microreticulum/microReticulum +lib_compat_mode = off + [env:amy] extends = tbeam_base upload_port = /dev/ttytAMY diff --git a/exercises/306_microReticulum_ble_file_transfer_oled/src/host_jp_main.cpp b/exercises/306_microReticulum_ble_file_transfer_oled/src/host_jp_main.cpp index f5850cd..7c43b01 100644 --- a/exercises/306_microReticulum_ble_file_transfer_oled/src/host_jp_main.cpp +++ b/exercises/306_microReticulum_ble_file_transfer_oled/src/host_jp_main.cpp @@ -1,4 +1,7 @@ -#if defined(HOST_BLE_PERIPHERAL) +#if defined(HOST_BLE_DUAL) +#include "HostBluezBleInterface.h" +#include "HostBluezPeripheralInterface.h" +#elif defined(HOST_BLE_PERIPHERAL) #include "HostBluezPeripheralInterface.h" using HostBleInterface = HostBluezPeripheralInterface; #else @@ -24,7 +27,9 @@ using HostBleInterface = HostBluezBleInterface; #include #include #include +#include #include +#include static constexpr const char* APP_NAME = "microreticulum"; static constexpr const char* APP_ASPECT = "filetransfer"; @@ -53,7 +58,14 @@ static constexpr uint32_t FNV1A_OFFSET = 2166136261UL; static constexpr uint32_t FNV1A_PRIME = 16777619UL; static RNS::Reticulum reticulum({RNS::Type::NONE}); +#if defined(HOST_BLE_DUAL) +static std::vector ble_interfaces; +static HostBluezBleInterface* ble_central_impl = nullptr; +static HostBluezPeripheralInterface* ble_peripheral_impl = nullptr; +#else static RNS::Interface ble_interface({RNS::Type::NONE}); +static HostBleInterface* ble_impl = nullptr; +#endif static RNS::Identity local_identity({RNS::Type::NONE}); static RNS::Destination inbound_destination({RNS::Type::NONE}); static RNS::Destination peer_destination({RNS::Type::NONE}); @@ -64,7 +76,6 @@ static std::string peer_label; static bool have_peer = false; static bool link_active = false; static bool link_attempted = false; -static HostBleInterface* ble_impl = nullptr; static std::string node_label = HOST_NODE_LABEL; static bool running = true; @@ -421,12 +432,30 @@ static void setup_reticulum() { filesystem.init(); RNS::Utilities::OS::register_filesystem(filesystem); +#if defined(HOST_BLE_DUAL) + auto central = std::shared_ptr(new HostBluezBleInterface(node_label)); + ble_central_impl = static_cast(central.get()); + RNS::Interface central_interface(central); + central_interface.mode(RNS::Type::Interface::MODE_GATEWAY); + RNS::Transport::register_interface(central_interface); + ble_interfaces.push_back(central_interface); + central_interface.start(); + + auto peripheral = std::shared_ptr(new HostBluezPeripheralInterface(node_label)); + ble_peripheral_impl = static_cast(peripheral.get()); + RNS::Interface peripheral_interface(peripheral); + peripheral_interface.mode(RNS::Type::Interface::MODE_GATEWAY); + RNS::Transport::register_interface(peripheral_interface); + ble_interfaces.push_back(peripheral_interface); + peripheral_interface.start(); +#else auto impl = std::shared_ptr(new HostBleInterface(node_label)); ble_impl = static_cast(impl.get()); ble_interface = RNS::Interface(impl); ble_interface.mode(RNS::Type::Interface::MODE_GATEWAY); RNS::Transport::register_interface(ble_interface); ble_interface.start(); +#endif reticulum = RNS::Reticulum(); reticulum.transport_enabled(false); @@ -447,6 +476,59 @@ static void setup_reticulum() { std::printf("Local SINGLE destination: %s\n", inbound_destination.hash().toHex().c_str()); } +static void loop_ble_interfaces() { +#if defined(HOST_BLE_DUAL) + if (ble_central_impl) { + ble_central_impl->loop(); + } + if (ble_peripheral_impl) { + ble_peripheral_impl->loop(); + } +#else + if (ble_impl) { + ble_impl->loop(); + } +#endif +} + +static bool ble_connected() { +#if defined(HOST_BLE_DUAL) + return (ble_central_impl && ble_central_impl->connected()) || + (ble_peripheral_impl && ble_peripheral_impl->connected()); +#else + return ble_impl && ble_impl->connected(); +#endif +} + +static const char* ble_role_status() { +#if defined(HOST_BLE_DUAL) + if (ble_central_impl && ble_central_impl->connected()) { + return ble_central_impl->role_name(); + } + if (ble_peripheral_impl && ble_peripheral_impl->connected()) { + return ble_peripheral_impl->role_name(); + } + return "linux-dual"; +#else + return ble_impl ? ble_impl->role_name() : "unknown"; +#endif +} + +static void stop_ble_interfaces() { +#if defined(HOST_BLE_DUAL) + if (ble_peripheral_impl) { + ble_peripheral_impl->stop(); + } + if (ble_central_impl) { + ble_central_impl->stop(); + } +#else + if (ble_impl) { + ble_impl->stop(); + } +#endif +} + static void handle_signal(int signal) { (void)signal; running = false; @@ -474,15 +556,13 @@ int main() { while (running) { reticulum.loop(); - if (ble_impl) { - ble_impl->loop(); - } + loop_ble_interfaces(); uint64_t now = millis64(); - if (ble_impl && !ble_impl->connected()) { + if (!ble_connected()) { if (next_wait_log_ms == 0 || now >= next_wait_log_ms) { next_wait_log_ms = now + 10000; - std::printf("BLE %s waiting for peer\n", ble_impl->role_name()); + std::printf("BLE %s waiting for peer\n", ble_role_status()); } RNS::Utilities::OS::sleep(0.005); continue; @@ -510,9 +590,7 @@ int main() { RNS::Utilities::OS::sleep(0.005); } - if (ble_impl) { - ble_impl->stop(); - } + stop_ble_interfaces(); std::printf("\nStopped\n"); return 0; }