nkolban / esp32-snippets

Sample ESP32 snippets and code fragments
https://leanpub.com/kolban-ESP32
Apache License 2.0
2.34k stars 712 forks source link

Dynamically change advertisement data #375

Open rammak opened 6 years ago

rammak commented 6 years ago

Hello, I'm trying to make a kitchen top weighing scale wireless capable and I successfully connected the salvaged body with load cells to HX711 based sensor which I have connected to an ESP32 devkit. Now since the amount and frequency of the data is too low I decided to include the data into advertising packet itself so that I can get the data without connecting to the BLE server.

The sketch I've attached below successfully changes the manufacturer data field in the advertisement packet for 2-3 times and then the data stays the same and doesn't get updated. As BLE client I'm using this sketch with slight modifications such as continuous scanning (using loop) and printing the manufacturer data field of the advertisement data packet. Can anybody help me identify what's wrong with the sketch?

#include "HX711.h"
#include "soc/rtc.h"
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLEAdvertising.h>

float f = 20000;
float weight = 0;
BLEAdvertisementData pAdvertisementData;
BLEAdvertising *pAdvertising;

HX711 scale;

void setup() {
  Serial.begin(115200);
  delay(1000);
  rtc_clk_cpu_freq_set(RTC_CPU_FREQ_80M);
  Serial.println("HX711 Demo");

  Serial.println("Initializing the scale");
  // parameter "gain" is ommited; the default value 128 is used by the library
  // HX711.DOUT  - pin #A1
  // HX711.PD_SCK - pin #A0
  scale.begin(4, 5);

  scale.set_scale(f);                     
  scale.tare();              

  Serial.println("Starting Arduino BLE Client application...");
  BLEDevice::init("ESP32-scale");
  BLEServer *pServer = BLEDevice::createServer();
  pAdvertising = pServer->getAdvertising();
  pAdvertising->start();

  Serial.println("Readings:");
}

void loop() {

    Serial.print("one reading:\t");
    Serial.print(scale.get_units(), 1);
    Serial.print("\t| average:\t");
    weight = scale.get_units(10);
    Serial.println(weight);

    String s = String(weight);
    char str[10];
    s.toCharArray(str, 10);
    Serial.println(str);
    pAdvertising->stop();
    pAdvertisementData.setManufacturerData(str);
    pAdvertising->setAdvertisementData(pAdvertisementData);
    pAdvertising->start();
}
rammak commented 6 years ago

Ok so the followup is that I've made a new sketch just for the beacon by removing any HX711 code. First thing I changed was to add setScanResponseData() because without that I cannot find the beacon on nRFConnect. Second thing is that I can call setManufacturerData() in the setup phase and it works fine as expected but I cannot call the method in loop and if I do that then I cannot scan the beacon in the app anymore. This is the modified code -

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

BLEAdvertisementData advert;
BLEAdvertisementData scan_response;
BLEAdvertising *pAdvertising;

int i = 0;

void setup() {
  Serial.begin(115200);
  Serial.println("Starting BLE work!");

  BLEDevice::init("MyESP32");
  BLEServer *pServer = BLEDevice::createServer();

//  BLEAdvertising *pAdvertising = pServer->getAdvertising();
  pAdvertising = pServer->getAdvertising();
  advert.setName("ESP32-new");
  scan_response.setManufacturerData("rammak");
//  advert.setShortName("ESP");
  pAdvertising->setAdvertisementData(advert);
  pAdvertising->setScanResponseData(scan_response);
  pAdvertising->start();
}

void loop() {

// The following commented code doesn't work

//  pAdvertising->stop();
//  i++;
//  if(i == 1) {
//    scan_response.setManufacturerData("A");
//  }
//  if(i == 2) {
//    scan_response.setManufacturerData("B");
//  }
//  if(i == 3) {
//    scan_response.setManufacturerData("C");
//  }
//  if(i == 4) {
//    scan_response.setManufacturerData("D");
//    i = 0;
//  }
//  pAdvertising->setScanResponseData(scan_response);
//  pAdvertising->start();

  delay(2000);
}           
rammak commented 6 years ago

Alright, I finally found the solution! The reason I could only update the values for 2-3 times was that if I use setManufacturerData() on the same BLEAdvertisementData variable again then it doesn't rewrite the previous value but appends the new value so there would be multiple manufacturerData fields in an advertisement packet and you can keep adding the fields until the maximum data length is reached. The solution is that we should update the manufacturerData in a newly initialized variable each time and then pass that to setScanResponseData. Here I've noticed that changing the AdvertisementData periodically doesn't change the data packet but changing ScanResponseData does.

One other thing to note is that according to BLE standard the first two bytes of manufacturerData are taken as company identifier. I've added a function to add this field in front of the packet in my beacon code here.

May be I should've spend some more time on the code before posting an issue here but may be this process of troubleshooting could help other tinkerers like me :)

31337Ghost commented 6 years ago

Having same issue, but in my case i need change (or remove) exact AdvertisementData. Now if I setting to ScanResponseData changed BLEAdvertisementData (with another major and minor values) - I'm getting multiple advertises. Thank you for your participation!

JazzzzX commented 5 years ago

Awesome, I was searching for a non connected solution for send some sensor data, Cool..! thanks @rammak for your code link, Hope I could make my solution on top of it.!

hazee007 commented 4 years ago

hey @jazzzzzzz were you able to use the code for what you wanted, i tried the code but still end up send predefined data in the String str[12] = {"Hello" , "Darkness"..........}. what i want to do is send a simple count =1 which increase in the loop using count++, i convert the count to a string which i then convert to hex using the function in the code but it did not advertise the count... let me know if you want the version of my code, i will be glad to share.. thanks...

JazzzzX commented 4 years ago

hey @jazzzzzzz were you able to use the code for what you wanted, i tried the code but still end up send predefined data in the String str[12] = {"Hello" , "Darkness"..........}. what i want to do is send a simple count =1 which increase in the loop using count++, i convert the count to a string which i then convert to hex using the function in the code but it did not advertise the count... let me know if you want the version of my code, i will be glad to share.. thanks...

Below code sends the counter value continuously. I have tested it, counter value is receiving in nRF Connect app. I hope this is what you need

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

BLEAdvertisementData advert;
BLEAdvertising *pAdvertising;

int i = 0;

//manufacturer code (0x02E5 for Espressif)
int man_code = 0x02E5;

//function takes String and adds manufacturer code at the beginning 
void setManData(String c, int c_size, BLEAdvertisementData &adv, int m_code) {

  String s;
  char b2 = (char)(m_code >> 8);
  m_code <<= 8;
  char b1 = (char)(m_code >> 8);
  s.concat(b1);
  s.concat(b2);
  s.concat(c);
  adv.setManufacturerData(s.c_str());

}

void setup() {
  Serial.begin(115200);
  Serial.println("Starting BLE work!");

  BLEDevice::init("MyESP32");
  BLEServer *pServer = BLEDevice::createServer();

  pAdvertising = pServer->getAdvertising();
  advert.setName("ESP32-new");
  pAdvertising->setAdvertisementData(advert);
  pAdvertising->start();
}

void loop() {
  i++;
  String a = String(i);

  BLEAdvertisementData scan_response;
  setManData(a, a.length() , scan_response, man_code);

  pAdvertising->stop();
  pAdvertising->setScanResponseData(scan_response);
  pAdvertising->start();

  delay(2000);
}
hazee007 commented 4 years ago

hey @jazzzzzzz were you able to use the code for what you wanted, i tried the code but still end up send predefined data in the String str[12] = {"Hello" , "Darkness"..........}. what i want to do is send a simple count =1 which increase in the loop using count++, i convert the count to a string which i then convert to hex using the function in the code but it did not advertise the count... let me know if you want the version of my code, i will be glad to share.. thanks...

Below code sends the counter value continuously. I have tested it, counter value is receiving in nRF Connect app. I hope this is what you need

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

BLEAdvertisementData advert;
BLEAdvertising *pAdvertising;

int i = 0;

//manufacturer code (0x02E5 for Espressif)
int man_code = 0x02E5;

//function takes String and adds manufacturer code at the beginning 
void setManData(String c, int c_size, BLEAdvertisementData &adv, int m_code) {

  String s;
  char b2 = (char)(m_code >> 8);
  m_code <<= 8;
  char b1 = (char)(m_code >> 8);
  s.concat(b1);
  s.concat(b2);
  s.concat(c);
  adv.setManufacturerData(s.c_str());

}

void setup() {
  Serial.begin(115200);
  Serial.println("Starting BLE work!");

  BLEDevice::init("MyESP32");
  BLEServer *pServer = BLEDevice::createServer();

  pAdvertising = pServer->getAdvertising();
  advert.setName("ESP32-new");
  pAdvertising->setAdvertisementData(advert);
  pAdvertising->start();
}

void loop() {
  i++;
  String a = String(i);

  BLEAdvertisementData scan_response;
  setManData(a, a.length() , scan_response, man_code);

  pAdvertising->stop();
  pAdvertising->setScanResponseData(scan_response);
  pAdvertising->start();

  delay(2000);
}

Thanks Man, Works wonderfully..

ericbarch commented 4 years ago

I know this issue is a bit stale. But I was looking to do this very thing and ended up writing a simple library to allow me to update the advertising data without updating the scan response or start/stopping the stack. Feel free to use if it suits your purpose 😃 https://github.com/ericbarch/BLECast

trullock commented 1 year ago

This was a really helpful thread, thanks all

KASSIMSAMJI commented 1 year ago

hey @jazzzzzzz were you able to use the code for what you wanted, i tried the code but still end up send predefined data in the String str[12] = {"Hello" , "Darkness"..........}. what i want to do is send a simple count =1 which increase in the loop using count++, i convert the count to a string which i then convert to hex using the function in the code but it did not advertise the count... let me know if you want the version of my code, i will be glad to share.. thanks...

Below code sends the counter value continuously. I have tested it, counter value is receiving in nRF Connect app. I hope this is what you need

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

BLEAdvertisementData advert;
BLEAdvertising *pAdvertising;

int i = 0;

//manufacturer code (0x02E5 for Espressif)
int man_code = 0x02E5;

//function takes String and adds manufacturer code at the beginning 
void setManData(String c, int c_size, BLEAdvertisementData &adv, int m_code) {

  String s;
  char b2 = (char)(m_code >> 8);
  m_code <<= 8;
  char b1 = (char)(m_code >> 8);
  s.concat(b1);
  s.concat(b2);
  s.concat(c);
  adv.setManufacturerData(s.c_str());

}

void setup() {
  Serial.begin(115200);
  Serial.println("Starting BLE work!");

  BLEDevice::init("MyESP32");
  BLEServer *pServer = BLEDevice::createServer();

  pAdvertising = pServer->getAdvertising();
  advert.setName("ESP32-new");
  pAdvertising->setAdvertisementData(advert);
  pAdvertising->start();
}

void loop() {
  i++;
  String a = String(i);

  BLEAdvertisementData scan_response;
  setManData(a, a.length() , scan_response, man_code);

  pAdvertising->stop();
  pAdvertising->setScanResponseData(scan_response);
  pAdvertising->start();

  delay(2000);
}

Thankz a trillion because this script here solved two case/issues in my code

I didn't know I need to have two variables of BLEAdvertisementData, one to set a Name and the other to repeatedly update the manufacturerData values

Thanks Again mate