NimBLE-Arduino icon indicating copy to clipboard operation
NimBLE-Arduino copied to clipboard

ESP32-C3 not receiving data with ESP-NOW after initialising NimBLE

Open Gabriele-Cano opened this issue 1 year ago • 9 comments

Hi,

I'm using two ESP32-C3, the first one to turn on is the one who becomes the master after 3 seconds. The master should initialise NimBLE and then broadcast every second or so a packet called ACK for other ESP32s with esp now. The problem is that when the slave device receives the packet it tries to reply back to the master but the attempt fails. I know for sure that this kind of communication works because I tried it without initialising NimBLE.

(Note: after initialising NimBLE the master ESP is correctly seen by the phone) (I already tried to ask on Arduino-ESP32 core but didn't replied and I need to finish this code before friday)

I can't figure out what's the problem.

This is the code

#include <NimBLEDevice.h>
#include "ESP32_NOW.h"
#include "WiFi.h"
#include "structs.cpp"

#include <esp_mac.h>  // For the MAC2STR and MACSTR macros

/* Definitions */

#define ESPNOW_WIFI_CHANNEL 6
#define BATTERY_PIN 3


/* Global Variables */

uint32_t msg_count = 0;
bool inGroup = false;
bool isMaster = false;
long startupTime, lastBatteryCheckSent, lastACKSent, lastAliveCheck;
bool connectedBLE = false;


/* Functions */ 

int getCurrentBatterySOT() {
  int batteria = map(analogReadMilliVolts(BATTERY_PIN) * 2, 3150, 4150, 0, 100);
  batteria = max(1, min(batteria, 100));
  return batteria;
}

/* Classes */

// Creating a new class that inherits from the ESP_NOW_Peer class is required.
class ESP_NOW_Broadcast_Peer : public ESP_NOW_Peer {
public:
  // Constructor of the class using the broadcast address
  ESP_NOW_Broadcast_Peer(uint8_t channel, wifi_interface_t iface, const uint8_t *lmk) : ESP_NOW_Peer(ESP_NOW.BROADCAST_ADDR, channel, iface, lmk) {}

  // Destructor of the class
  ~ESP_NOW_Broadcast_Peer() {
    remove();
  }

  // Function to properly initialize the ESP-NOW and register the broadcast peer
  bool begin() {
    if (!ESP_NOW.begin() || !add()) {
      log_e("Failed to initialize ESP-NOW or register the broadcast peer");
      return false;
    }
    return true;
  }

  // Function to send a message to all devices within the network
  bool send_message(const uint8_t *data, size_t len) {
    if (!send(data, len)) {
      log_e("Failed to broadcast message");
      return false;
    }
    return true;
  }
};

class ESP_NOW_Peer_Class : public ESP_NOW_Peer {
public:
  // Constructor of the class
  ESP_NOW_Peer_Class(const uint8_t *mac_addr, uint8_t channel, wifi_interface_t iface, const uint8_t *lmk) : ESP_NOW_Peer(mac_addr, channel, iface, lmk) {}

  // Destructor of the class
  ~ESP_NOW_Peer_Class() {}

  // Function to register the master peer
  bool add_peer() {
    if (!add()) {
      log_e("Failed to register the peer");
      return false;
    }
    return true;
  }

  // Function to print the received messages from the master
  void onReceive(const uint8_t *data, size_t len, bool broadcast) {
    Serial.println("The peer received a packet");
    if (*data == ACK_PACK_TYPE) return;
    if (*data == BATTERY_PACK_TYPE) {
      Serial.println("Sending battery information to the master");
      BATTERY response = BATTERY(getCurrentBatterySOT());
      send_message((uint8_t *)&response, sizeof(BATTERY));
    }
    Serial.printf("Received a message from master " MACSTR " (%s)\n", MAC2STR(addr()), broadcast ? "broadcast" : "unicast");
    Serial.printf("  Message: %s\n", (char *)data);
  }

    // Function to send a message to the peer
  bool send_message(const uint8_t *data, size_t len) {
    if (!send(data, len)) {
      log_e("Failed to send message to peer %s", MAC2STR(addr()));
      return false;
    }
    return true;
  }
};


class ESP_NOW_Slave_Class : public ESP_NOW_Peer {
public:
  bool isAlive = true;
  int battery = 0;
  // Constructor of the class
  ESP_NOW_Slave_Class(const uint8_t *mac_addr, uint8_t channel, wifi_interface_t iface, const uint8_t *lmk) : ESP_NOW_Peer(mac_addr, channel, iface, lmk) {}

  // Destructor of the class
  ~ESP_NOW_Slave_Class() {}

  // Function to register the master peer
  bool add_peer() {
    if (!add()) {
      log_e("Failed to register the peer");
      return false;
    }
    return true;
  }

  // Function to print the received messages from the master
  void onReceive(const uint8_t *data, size_t len, bool broadcast) {
    isAlive = true;
    //Serial.printf("Received message from: (%s), data*: %d\n", MAC2STR(addr()), *data);
    if (*data == ACK_PACK_TYPE) return;
    if (*data == BATTERY_PACK_TYPE) {
      BATTERY *packet = (BATTERY *) data;
      battery = packet->batt;
    }
  }

  void onSent(bool success) {
    isAlive = success;
    Serial.printf("onSent(), %d\n\n", isAlive);
  }

  bool deinitialise() {
    Serial.printf("I'm removing the peer\n");
    return remove();
  }

    // Function to send a message to the peer
  bool send_message(const uint8_t *data, size_t len) {
    if (!send(data, len)) {
      log_e("Failed to broadcast message");
      return false;
    }
    return true;
  }
};

// Create a broadcast peer object
ESP_NOW_Broadcast_Peer broadcast_peer(ESPNOW_WIFI_CHANNEL, WIFI_IF_STA, NULL);
std::vector<ESP_NOW_Slave_Class> devices;
ESP_NOW_Peer_Class* master = NULL;

// Callback called when an unknown peer sends a message
void register_new_master(const esp_now_recv_info_t *info, const uint8_t *data, int len, void *arg) {
  
  if (memcmp(info->des_addr, ESP_NOW.BROADCAST_ADDR, 6) == 0) {
    Serial.printf("Unknown peer " MACSTR " sent a broadcast message\n", MAC2STR(info->src_addr));

    if (*data == ACK_PACK_TYPE && !inGroup) {
      inGroup = true;
      Serial.println("Registering the peer as a master");
      master = new ESP_NOW_Peer_Class(info->src_addr, ESPNOW_WIFI_CHANNEL, WIFI_IF_STA, NULL);
      if (!master->add_peer()) {
        Serial.println("Failed to register the new master");
        return;
      }
      OK_PACK okPack = OK_PACK();
      master->send_message((uint8_t *)&okPack, sizeof(OK_PACK));
    }
  } else {
    if (isMaster) {
      log_v("Received a unicast message from " MACSTR, MAC2STR(info->src_addr));
      Serial.printf("%d\n", *data);
      if (*data == OK_PACK_TYPE) {
        ESP_NOW_Slave_Class new_device(info->src_addr, ESPNOW_WIFI_CHANNEL, WIFI_IF_STA, NULL);
        devices.push_back(new_device);
        if(!devices.back().add_peer()){
          Serial.println("\n\nFailed to add the new peer to the device list\n\n");
        }
      }
    } else {
      // The slave will only receive broadcast messages
      log_v("Received a unicast message from " MACSTR, MAC2STR(info->src_addr));
      log_v("Igorning the message");
    }
  }
}



NimBLEServer* pServer = NULL;
NimBLEService *serviceBattery = NULL;
NimBLECharacteristic *batteryLevelChar = NULL;


#define SERVICE_BATTERY_UUID "180f"                                                           //batteria
#define BATTERY_LEVEL_CHARACTERISTIC_UUID "492c2d04-f173-4814-9630-e12aedc4f7f6"              
//livello batteria tutti i sensori, 1byte per sensore, max 10 sensori, se sensore non connesso: 0xFF


class ServerCallbacks: public NimBLEServerCallbacks {
  void onConnect(NimBLEServer* pServer) {
    Serial.println("Device connected");
    connectedBLE = true;
  };

  void onDisconnect(NimBLEServer* pServer) {
    Serial.println("Device disconnected");
    connectedBLE = false;
    //resart the advertising
    BLEDevice::startAdvertising();
  }
};


void initBLE() {
  NimBLEDevice::init("B_Rainbow");
  pServer = NimBLEDevice::createServer();
  pServer->setCallbacks(new ServerCallbacks());

  serviceBattery = pServer->createService(SERVICE_BATTERY_UUID);

  batteryLevelChar = serviceBattery->createCharacteristic(
    BATTERY_LEVEL_CHARACTERISTIC_UUID,
     NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ
  );

  serviceBattery->start();
  
  BLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_BATTERY_UUID);
//  pAdvertising->setScanResponse(true);
  pAdvertising->setPreferredParams(0x06, 0x12);
  NimBLEDevice::startAdvertising();

  Serial.println("Finished BLE init");
}



void updateBLEData() {
  uint8_t *data = (uint8_t*) malloc(devices.size() + 1);
  data[0] = devices.size()+1;
  for (int i = 0; i < devices.size(); i++) {
    data[i] = devices[i].battery;
  }
  batteryLevelChar->setValue(data, devices.size()+1);
  batteryLevelChar->notify();
  free(data);
  data = NULL;
}

/* Main */

void setup() {
  Serial.begin(115200);

  // Initialize the Wi-Fi module
  WiFi.mode(WIFI_STA);
  WiFi.setChannel(ESPNOW_WIFI_CHANNEL);
  while (!WiFi.STA.started()) {
    delay(100);
  }

  // Register the broadcast peer
  if (!broadcast_peer.begin()) {
    Serial.println("Failed to initialize broadcast peer");
    Serial.println("Reebooting in 5 seconds...");
    delay(5000);
    ESP.restart();
  }

  ESP_NOW.onNewPeer(register_new_master, NULL);

  startupTime = millis();
}

void loop() {
  // Broadcast a message to all devices within the network
  
  if (!inGroup && startupTime + 3000 < millis() && !isMaster) {
    //diventa il master
    isMaster = true;
    Serial.println("\n\n\nIM THE MASTER \n\n\n");
    initBLE();
  }


  if (isMaster && lastACKSent + 1000 < millis()) {
    ACK pack = ACK();
    broadcast_peer.send_message((uint8_t *)&pack, sizeof(ACK));
    lastACKSent = millis();
  }

  if (isMaster && lastBatteryCheckSent + 2000 < millis()) {
    BATTERY pack = BATTERY(255);
    for (int i = devices.size() - 1; i >= 0; i--) {
      devices[i].send_message((uint8_t *)&pack, sizeof(BATTERY));
    }
    lastBatteryCheckSent = millis();
  }


  if (isMaster && lastAliveCheck + 1000 < millis()) {
    for (int i = devices.size() - 1; i >= 0; i--) {
      if (!devices[i].isAlive) {
        devices[i].deinitialise();
        devices.erase(devices.begin() + i);
      }
    }
    lastAliveCheck = millis();
  }

  delay(100);
}

Gabriele-Cano avatar Jan 27 '25 21:01 Gabriele-Cano

Try initializing BLE at startup, but not advertising until you need to, for some reason it seem to help sometimes.

Other than that I would need to see a debug log to start with.

h2zero avatar Jan 27 '25 22:01 h2zero

Thank you for your answer

Ok, I put initBLE in the setup function just after Serial.begin, then moved NimBLEDevice::startAdvertising(); inside the loop right after IM THE MASTER println. But still there is the same problem.

These are the logs:

Master

============ Before Setup End ============
[   484][I][esp32-hal-periman.c:141] perimanSetPinBus(): Pin 18 already has type USB_DM (38) with bus 0x3fc98ed0
[   485][I][esp32-hal-periman.c:141] perimanSetPinBus(): Pin 19 already has type USB_DP (39) with bus 0x3fc98ed0
I NimBLEDevice: BLE Host Task Started
I NimBLEDevice: NimBle host synced.
D NimBLEService: >> start(): Starting service: UUID: 0x180f, handle: 0x0000
D NimBLEService: Adding 1 characteristics for service UUID: 0x180f, handle: 0x0000
D NimBLEService: << start()
Finished BLE init
[   577][V][NetworkEvents.cpp:113] _checkForEvent(): Network Event: 101 - WIFI_READY
[   581][V][STA.cpp:186] _onStaEvent(): STA Started
[   581][V][NetworkEvents.cpp:113] _checkForEvent(): Network Event: 110 - STA_START
[   582][V][STA.cpp:110] _onStaArduinoEvent(): Arduino STA Event: 110 - STA_START
[   584][V][ESP32_NOW.cpp:18] _esp_now_add_peer(): ff:ff:ff:ff:ff:ff
[   585][V][ESP32_NOW.cpp:311] add(): Peer added - ff:ff:ff:ff:ff:ff
=========== After Setup Start ============


IM THE MASTER
D NimBLEAdvertising: >> Advertising start: duration=0, dirAddr=NULL
primary service
           uuid 0x1800
         handle 1
     end_handle 5
characteristic
           uuid 0x2a00
     def_handle 2
     val_handle 3
   min_key_size 0
          flags [READ]
characteristic
           uuid 0x2a01
     def_handle 4
     val_handle 5
   min_key_size 0
          flags [READ]
primary service
           uuid 0x1801
         handle 6
     end_handle 13
characteristic
           uuid 0x2a05
     def_handle 7
     val_handle 8
   min_key_size 0
          flags [INDICATE]
ccc descriptor
           uuid 0x2902
         handle 9
   min_key_size 0
          flags [READ|WRITE]
characteristic
           uuid 0x2b3a
     def_handle 10
     val_handle 11
   min_key_size 0
          flags [READ]
characteristic
           uuid 0x2b29
     def_handle 12
     val_handle 13
   min_key_size 0
          flags [READ|WRITE]
primary service
           uuid 0x180f
         handle 14
     end_handle 17
characteristic
           uuid 492c2d04-f173-4814-9630-e12aedc4f7f6
     def_handle 15
     val_handle 16
   min_key_size 0
          flags [READ|NOTIFY]
ccc descriptor
           uuid 0x2902
         handle 17
   min_key_size 0
          flags [READ|WRITE]
D NimBLEAdvertising: setAdvertisementData: 02 01 06 03 03 0f 18 05 12 06 00 12 00
D NimBLEAdvertising: << Advertising start
Started Advertising


[  3727][V][ESP32_NOW.cpp:384] send(): ff:ff:ff:ff:ff:ff, data length 1
[  3829][V][ESP32_NOW.cpp:133] _esp_now_tx_cb(): ff:ff:ff:ff:ff:ff : SUCCESS
[  3830][I][ESP32_NOW.h:78] onSent(): Message transmission to peer ff:ff:ff:ff:ff:ff successful
.....

Slave

00:00:27.572 ============ Before Setup End ============
00:00:27.676 [  2279][I][esp32-hal-periman.c:141] perimanSetPinBus(): Pin 18 already has type USB_DM (38) with bus 0x3fc98ed0
00:00:27.676 [  2280][I][esp32-hal-periman.c:141] perimanSetPinBus(): Pin 19 already has type USB_DP (39) with bus 0x3fc98ed0
00:00:27.725 I NimBLEDevice: BLE Host Task Started
00:00:27.728 I NimBLEDevice: NimBle host synced.
00:00:27.730 D NimBLEService: >> start(): Starting service: UUID: 0x180f, handle: 0x0000
00:00:27.735 D NimBLEService: Adding 1 characteristics for service UUID: 0x180f, handle: 0x0000
00:00:27.737 D NimBLEService: << start()
00:00:27.739 Finished BLE init
00:00:27.749 [  2352][V][NetworkEvents.cpp:113] _checkForEvent(): Network Event: 101 - WIFI_READY
00:00:27.749 [  2356][V][STA.cpp:186] _onStaEvent(): STA Started
00:00:27.750 [  2356][V][NetworkEvents.cpp:113] _checkForEvent(): Network Event: 110 - STA_START
00:00:27.750 [  2357][V][STA.cpp:110] _onStaArduinoEvent(): Arduino STA Event: 110 - STA_START
00:00:27.753 [  2359][V][ESP32_NOW.cpp:18] _esp_now_add_peer(): ff:ff:ff:ff:ff:ff
00:00:27.754 [  2359][V][ESP32_NOW.cpp:311] add(): Peer added - ff:ff:ff:ff:ff:ff
00:00:27.754 =========== After Setup Start ============
00:00:27.754 INTERNAL Memory Info:
00:00:27.755 ------------------------------------------
00:00:27.755   Total Size        :   272580 B ( 266.2 KB)
00:00:27.755   Free Bytes        :   131276 B ( 128.2 KB)
00:00:27.755   Allocated Bytes   :   133592 B ( 130.5 KB)
00:00:27.755   Minimum Free Bytes:   131172 B ( 128.1 KB)
00:00:27.755   Largest Free Block:   110580 B ( 108.0 KB)
00:00:27.755 ------------------------------------------
00:00:27.755 GPIO Info:
00:00:27.755 ------------------------------------------
00:00:27.755   GPIO : BUS_TYPE[bus/unit][chan]
00:00:27.755   --------------------------------------  
00:00:27.755     18 : USB_DM
00:00:27.755     19 : USB_DP
00:00:27.755     20 : UART_RX[0]
00:00:27.756     21 : UART_TX[0]
00:00:27.756 ============ After Setup End =============
00:00:28.247 [  2850][V][ESP32_NOW.cpp:112] _esp_now_rx_cb(): Broadcast from d8:3b:da:e3:1c:cc, data length : 1
00:00:28.248 0x00, // .
00:00:28.248 [  2850][V][ESP32_NOW.cpp:115] _esp_now_rx_cb(): Calling new_cb, peer not found.
00:00:28.248 Unknown peer d8:3b:da:e3:1c:cc sent a broadcast message
00:00:28.248 Registering the peer as a master
00:00:28.248 [  2853][V][ESP32_NOW.cpp:18] _esp_now_add_peer(): d8:3b:da:e3:1c:cc
00:00:28.248 [  2856][V][ESP32_NOW.cpp:311] add(): Peer added - d8:3b:da:e3:1c:cc
00:00:28.248 [  2856][V][ESP32_NOW.cpp:384] send(): d8:3b:da:e3:1c:cc, data length 1
00:00:28.288 [  2894][V][ESP32_NOW.cpp:133] _esp_now_tx_cb(): d8:3b:da:e3:1c:cc : FAILED
00:00:28.289 [  2895][I][ESP32_NOW.h:78] onSent(): Message transmission to peer d8:3b:da:e3:1c:cc failed
00:00:29.245 [  3850][V][ESP32_NOW.cpp:112] _esp_now_rx_cb(): Broadcast from d8:3b:da:e3:1c:cc, data length : 1
00:00:29.245 0x00, // .
00:00:29.245 [  3851][V][ESP32_NOW.cpp:122] _esp_now_rx_cb(): Checking peer ff:ff:ff:ff:ff:ff
00:00:29.246 [  3851][V][ESP32_NOW.cpp:122] _esp_now_rx_cb(): Checking peer d8:3b:da:e3:1c:cc
00:00:29.246 [  3851][V][ESP32_NOW.cpp:125] _esp_now_rx_cb(): Calling onReceive
00:00:29.246 The peer received a packet
00:00:30.345 [  4950][V][ESP32_NOW.cpp:112] _esp_now_rx_cb(): Broadcast from d8:3b:da:e3:1c:cc, data length : 1
00:00:30.346 0x00, // .
00:00:30.346 [  4951][V][ESP32_NOW.cpp:122] _esp_now_rx_cb(): Checking peer ff:ff:ff:ff:ff:ff
00:00:30.346 [  4951][V][ESP32_NOW.cpp:122] _esp_now_rx_cb(): Checking peer d8:3b:da:e3:1c:cc
00:00:30.346 [  4952][V][ESP32_NOW.cpp:125] _esp_now_rx_cb(): Calling onReceive
00:00:30.346 The peer received a packet


Gabriele-Cano avatar Jan 27 '25 23:01 Gabriele-Cano

I can see that it's successfully advertising, your slave device doesn't scan for BLE advertisements though but it is receiving the esp now messages. What am I missing here?

h2zero avatar Jan 27 '25 23:01 h2zero

Yes what you described works, the problem js that the master device doesn't receive back the message from the slave after pairing.

You can see in the slave log I copied here

00:00:28.248 [  2850][V][ESP32_NOW.cpp:115] _esp_now_rx_cb(): Calling new_cb, peer not found.
00:00:28.248 Unknown peer d8:3b:da:e3:1c:cc sent a broadcast message
00:00:28.248 Registering the peer as a master
00:00:28.248 [  2853][V][ESP32_NOW.cpp:18] _esp_now_add_peer(): d8:3b:da:e3:1c:cc
00:00:28.248 [  2856][V][ESP32_NOW.cpp:311] add(): Peer added - d8:3b:da:e3:1c:cc
00:00:28.248 [  2856][V][ESP32_NOW.cpp:384] send(): d8:3b:da:e3:1c:cc, data length 1
00:00:28.288 [  2894][V][ESP32_NOW.cpp:133] _esp_now_tx_cb(): d8:3b:da:e3:1c:cc : FAILED
00:00:28.289 [  2895][I][ESP32_NOW.h:78] onSent(): Message transmission to peer d8:3b:da:e3:1c:cc failed
00

Gabriele-Cano avatar Jan 28 '25 06:01 Gabriele-Cano

Ahh okay, in that case the only thing I can think of is since the master device is advertising BLE the antenna is in use and the coexist functions may not work well with esp now, hard to say for sure, never tried this.

h2zero avatar Jan 28 '25 08:01 h2zero

It seems it should be working according to this page https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/coexist.html

I don't know what to try next

Gabriele-Cano avatar Jan 28 '25 09:01 Gabriele-Cano

Does it work if you do not start advertising?

h2zero avatar Jan 28 '25 14:01 h2zero

Some info here : https://esp32.com/viewtopic.php?t=23406

h2zero avatar Jan 28 '25 14:01 h2zero

check this example.

#include <NimBLEDevice.h>
#include <esp_sleep.h>
#include <WiFi.h>
#include "ESP32_NOW.h"  // We use the new ESP32_NOW library
#include <esp_mac.h>

// =============================================
//                  DEFINES
// =============================================
#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
#define BLE_WAIT_TIME       5000  // 5 seconds for BLE connection
#define SLEEP_TIME_SEC      5     // Sleep time in seconds
#define ESPNOW_WIFI_CHANNEL 6     // Wi-Fi channel for ESP-NOW
// Structure for ESP-NOW data
typedef struct struct_message {
    uint32_t counter;  // We use the same format as BLE
} struct_message;

// =============================================
//              GLOBAL VARIABLES
// =============================================
RTC_DATA_ATTR uint32_t count = 0;  // Persistent counter in RTC memory
struct_message myData;
// BLE objects
NimBLEServer* pServer = nullptr;
NimBLECharacteristic* pCharacteristic = nullptr;
bool deviceConnected = false;
// Class to handle ESP-NOW broadcast
class ESP_NOW_Broadcast_Peer : public ESP_NOW_Peer {
public:
    // Constructor of the class using the broadcast address
    ESP_NOW_Broadcast_Peer(uint8_t channel, wifi_interface_t iface, const uint8_t *lmk)
        : ESP_NOW_Peer(ESP_NOW.BROADCAST_ADDR, channel, iface, lmk) {}
    // Destructor of the class
    ~ESP_NOW_Broadcast_Peer() {
        remove();
    }
    // Function to properly initialize ESP-NOW and register the broadcast peer
    bool begin() {
        if (!ESP_NOW.begin() || !add()) {
            log_e("Failed to initialize ESP-NOW or register the broadcast peer");
            return false;
        }
        return true;
    }
    // Function to send a message to all devices on the network
    bool send_message(const uint8_t *data, size_t len) {
        if (!send(data, len)) {
            log_e("Failed to broadcast message");
            return false;
        }
        return true;
    }
};
// Instance of the broadcast peer
ESP_NOW_Broadcast_Peer broadcast_peer(ESPNOW_WIFI_CHANNEL, WIFI_IF_STA, NULL);

// =============================================
//             CALLBACKS & CLASSES
// =============================================
// BLE server callbacks
class ServerCallbacks: public NimBLEServerCallbacks {
  void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override {
    Serial.printf("Client address: %s\n", connInfo.getAddress().toString().c_str());
    deviceConnected = true;
    /**
      *  We can use the connection handle here to ask for different connection parameters.
      *  Args: connection handle, min connection interval, max connection interval
      *  latency, supervision timeout.
      *  Units; Min/Max Intervals: 1.25 millisecond increments.
      *  Latency: number of intervals allowed to skip.
      *  Timeout: 10 millisecond increments.
      */
    pServer->updateConnParams(connInfo.getConnHandle(), 24, 48, 0, 180);
  }
  void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) override {
      Serial.printf("Client disconnected\n");
      deviceConnected = false;
  }
}serverCallbacks;

// =============================================
//                  FUNCTIONS
// =============================================
void configBle() {
    // Initialize BLE
    NimBLEDevice::init("ble");  // BLE device name
    Serial.println("Initializing BLE...");
    // Create BLE server
    pServer = NimBLEDevice::createServer();
    pServer->setCallbacks(&serverCallbacks);
    // Create service and characteristic
    NimBLEService* pService = pServer->createService(SERVICE_UUID);
    pCharacteristic = pService->createCharacteristic(
                        CHARACTERISTIC_UUID,
                        NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::WRITE
                      );
    // Start service
    pService->start();
    // Configure BLE advertising to include the service
    NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
    pAdvertising->addServiceUUID(SERVICE_UUID);
    //pAdvertising->setMinInterval(160);  // 100 ms
    //pAdvertising->setMaxInterval(320);  // 200 ms
    pAdvertising->enableScanResponse(false);
    pAdvertising->start();
    Serial.println("BLE started and advertising enabled.");
}
void disableBle() {
    NimBLEDevice::deinit(true);
    Serial.println("BLE disabled");
}
void sendDataBle() {
    Serial.print("Sending via BLE: ");
    Serial.println(count);
    
    pCharacteristic->setValue((uint8_t*)&count, sizeof(count));
    pCharacteristic->notify();
}
void sendDataEspNow() {
    myData.counter = count;
    Serial.print("Sending via ESP-NOW: ");
    Serial.println(myData.counter);
    
    if (!broadcast_peer.send_message((uint8_t*)&myData, sizeof(myData))) {
        Serial.println("Error sending data via ESP-NOW");
    }
}
void goToSleep(int seconds) {
    Serial.println("Entering sleep mode...");
    delay(100);  // Small delay to finish operations
    
    esp_sleep_enable_timer_wakeup(seconds * 1000000LL);
    esp_deep_sleep_start();
}

// =============================================
//               SETUP & LOOP
// =============================================
void setup() {
    Serial.begin(115200);
    setCpuFrequencyMhz(80);  // Reduce frequency to save power
    count++;  // Increment counter upon waking up
    // Configure and wait for BLE connection
    configBle();
    unsigned long startTime = millis();
    
    while (millis() - startTime < BLE_WAIT_TIME) {
        if (deviceConnected) break;
        delay(100);  // Non-blocking wait
    }
    // If no BLE connection, send via ESP-NOW
    if (!deviceConnected) {
        //disableBle();
        // Configure Wi-Fi for ESP-NOW
        WiFi.mode(WIFI_STA);
        WiFi.setChannel(ESPNOW_WIFI_CHANNEL);
        while (!WiFi.STA.started()) {
            delay(100);
        }
        // Register the broadcast peer
        if (!broadcast_peer.begin()) {
            Serial.println("Failed to initialize broadcast peer");
            Serial.println("Rebooting in 5 seconds...");
            delay(5000);
            ESP.restart();
        }
        
        sendDataEspNow();
        goToSleep(SLEEP_TIME_SEC);
    }
}

void loop() {
    if (deviceConnected) {
        sendDataBle();
        count++;  // Increment after sending
        delay(1000);  // Wait 1 second between sends
    } else {
        goToSleep(SLEEP_TIME_SEC);
    }
}

AlexLaramx avatar Feb 05 '25 18:02 AlexLaramx