Seeed Xiao nRF52840 BLE Mesh Could Not Be Detectable

Hi there,

Ok , so Looks like it’s common, I was surprised my colleagues agree
The combination of ECDH + AES encryption is a modern and secure design pattern, widely used in both consumer and industrial products for secure communication over constrained wireless channels like BLE.

From experience, here are the usual stumbling blocks:

1. Lack of Serial Output / Silent Failures

  • Often due to stack overflow, malloc failures, or MbedTLS entropy/seeding issues.
  • The Xiao nRF52840 has 256KB of RAM, but Arduino’s heap layout can be limiting.

:wrench: Fix tip: Make sure they run mbedtls_memory_buffer_alloc_init() and avoid heap-heavy objects unless needed.

2. Missing Platform Functions

  • Some MbedTLS features (like entropy collection or platform time) are stubbed or unimplemented in Arduino builds.
  • ECDH requires seeding RNG properly, and if that’s broken, the keygen silently fails.

:wrench: Fix tip: Explicitly seed using mbedtls_ctr_drbg_seed() with mbedtls_entropy_func and a real entropy source.

3. MbedTLS not linked correctly

  • Even though mbedtls compiles, not all features may be included in the build.
  • If they included mbedtls/config.h without proper flags (e.g., enabling MBEDTLS_ECDH_C and MBEDTLS_BIGNUM_C), the result can be undefined behavior or missing functions.

You must define config flags like:

#define MBEDTLS_ECDH_C
#define MBEDTLS_ECP_C
#define MBEDTLS_BIGNUM_C
#define MBEDTLS_ENTROPY_C
#define MBEDTLS_CTR_DRBG_C

Yes, it’s absolutely possible to:

  • Perform ECDH (e.g., using Curve25519 or secp256r1),
  • Derive a shared key,
  • Encrypt/decrypt with AES using MbedTLS on the Xiao nRF52840.

But Arduino IDE + MbedTLS is a tough combo without debug support, so you may want to:

  • Test smaller ECDH-only examples first,
  • Print intermediate steps (Serial.print()),
  • Consider switching to Zephyr or nRF SDK later for more stability and easier debugging.

The flow is valid and secure in principle:

  1. ECDH → derive a shared secret between two BLE peers.
  2. Use that shared secret to derive a symmetric AES key.
  3. Use AES (e.g. AES-GCM or AES-128-CBC) to encrypt/decrypt the BLE data payload.

This is a classic hybrid encryption scheme: asymmetric key agreement (ECDH) + symmetric encryption (AES). It’s widely used in secure communications (e.g. TLS, Signal protocol).

here is an older lightweight example for the Xiao with a different POV.
mind the header , i.e. must use BSP 1.1.1 for it to run.

/*
 * Xiao nRF52840 - ECDH Key Exchange + AES-128 Encryption Example
 * Uses MbedTLS (included with Seeed nRF52 BSP 1.1.1+)
 */

#include <mbedtls/ecdh.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/entropy.h>
#include <mbedtls/aes.h>

void setup() {
  Serial.begin(115200);
  while (!Serial);
  Serial.println("Starting ECDH + AES example...");

  // === Setup ===
  mbedtls_ecdh_context ctx;
  mbedtls_entropy_context entropy;
  mbedtls_ctr_drbg_context ctr_drbg;
  const char *pers = "ecdh";

  mbedtls_ecdh_init(&ctx);
  mbedtls_ctr_drbg_init(&ctr_drbg);
  mbedtls_entropy_init(&entropy);

  if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
                             (const unsigned char *)pers, strlen(pers)) != 0) {
    Serial.println("DRBG seed failed!");
    return;
  }

  // Load curve
  mbedtls_ecp_group_load(&ctx.grp, MBEDTLS_ECP_DP_SECP256R1);

  // Generate local keypair
  if (mbedtls_ecdh_gen_public(&ctx.grp, &ctx.d, &ctx.Q,
                              mbedtls_ctr_drbg_random, &ctr_drbg) != 0) {
    Serial.println("Keygen failed");
    return;
  }

  // For testing, simulate peer public key with own public key
  mbedtls_ecp_copy(&ctx.Qp, &ctx.Q);

  // Compute shared secret
  unsigned char shared_secret[32];
  if (mbedtls_ecdh_compute_shared(&ctx.grp, &ctx.z, &ctx.Qp, &ctx.d,
                                  mbedtls_ctr_drbg_random, &ctr_drbg) != 0) {
    Serial.println("Shared secret calc failed!");
    return;
  }

  mbedtls_mpi_write_binary(&ctx.z, shared_secret, 32);
  Serial.print("Shared Secret: ");
  for (int i = 0; i < 32; i++) Serial.printf("%02X", shared_secret[i]);
  Serial.println();

  // === AES encrypt with 128-bit key ===
  mbedtls_aes_context aes;
  mbedtls_aes_init(&aes);
  const char *plaintext = "HelloBLE12345678"; // 16 bytes
  uint8_t ciphertext[16];

  mbedtls_aes_setkey_enc(&aes, shared_secret, 128); // Use first 16 bytes
  mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_ENCRYPT, (const uint8_t *)plaintext, ciphertext);

  Serial.print("Ciphertext: ");
  for (int i = 0; i < 16; i++) Serial.printf("%02X", ciphertext[i]);
  Serial.println();

  mbedtls_aes_free(&aes);
  mbedtls_ecdh_free(&ctx);
  mbedtls_ctr_drbg_free(&ctr_drbg);
  mbedtls_entropy_free(&entropy);

  Serial.println("Done.");
}

void loop() {
delay (50); // allows time for bootloader if a crash occurs.
}

HTH
GL :slight_smile: PJ :v:

I will say , the nRF54L15 is a better fit However since it’s not out yet You can add more flash or use the grove Expansion base and add more Flash as in the examples for that.