Example on setting up esp32 as BLE server?

Nice work, tx for the reaction and the links provided. I will dig into this solution with only using the manufacturer data string. Keep you posted!

Hi,

Thanks for work on this. I’ve scratching my head to make this esp32_ble: example working but the

id(ble)->get_advertising()

always returns null.

Do i need another component like esp32_ble_server or do i need to init something else?

Thank you!

1 Like

Hi @ashald

Your work is a very welcome oasis in a google search desert :grinning:

I have tried to implement this to simulate a BLE temperature sensor to be received by a Viltron controller, the code works up to this line.

          // Force esp_ble to use new data
          id(ble).get_advertising()->start();

This causes a core panic. No Idea why and I don’t know how to decode a backtrace from esphome ,

If I rem the above line it boots normally, but obviously doesn’t transmit any ble signal

Logs are here:

ets Jun  8 2016 00:22:57

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:1184
load:0x40078000,len:13132
load:0x40080400,len:3036
entry 0x400805e4
[I][logger:262]: Log initialized
[C][ota:469]: There have been 0 suspected unsuccessful boot attempts.
[D][esp32.preferences:114]: Saving 1 preferences to flash...
[D][esp32.preferences:143]: Saving 1 preferences to flash: 0 cached, 1 written, 0 failed
[I][app:029]: Running through setup()...
[C][dht:011]: Setting up DHT...
[C][esp32_ble:027]: Setting up BLE...
[D][esp32_ble:043]: BLE setup complete
[C][wifi:038]: Setting up WiFi...
[C][wifi:039]:   Local MAC: 30:AE:A4:8B:A8:4C
[D][wifi:387]: Starting scan...
[D][advertisement:068]: Refreshing advertisement
Guru Meditation Error: Core  1 panic'ed (LoadProhibited). Exception was unhandled.

Core  1 register dump:
PC      : 0x400d86ca  PS      : 0x00060b30  A0      : 0x800dfb00  A1      : 0x3ffcd410  
A2      : 0x00000000  A3      : 0x00000000  A4      : 0x3ffc3c44  A5      : 0x00000000  
A6      : 0x00000000  A7      : 0x003fffff  A8      : 0x00000000  A9      : 0x00000001  
A10     : 0x3ffcd480  A11     : 0x00000000  A12     : 0x0000000c  A13     : 0x3ffcd48c  
A14     : 0x3ffc0230  A15     : 0x00000000  SAR     : 0x0000001d  EXCCAUSE: 0x0000001c  
EXCVADDR: 0x0000007c  LBEG    : 0x400913cc  LEND    : 0x400913d7  LCOUNT  : 0xffffffff  


Backtrace:0x400d86c7:0x3ffcd4100x400dfafd:0x3ffcd470 0x400dfa4d:0x3ffcd4b0 0x401f3f47:0x3ffcd4d0 0x401f3f77:0x3ffcd4f0 0x401f3de1:0x3ffcd510 0x400df993:0x3ffcd530 0x400de7c2:0x3ffcd580 0x400e061e:0x3ffcd5b0 0x400e646e:0x3ffcd6c0 




ELF file SHA256: 0000000000000000

Rebooting...

YAML is here

esphome:
  name: ble-server
  includes:
  - ble-server-structs.h

esp32_ble:
    id: ble

esp32:
  board: esp32dev
  framework:
    type: arduino

time:
  - platform: sntp
    id: ntp

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "REDACTED"

ota:
  password: "REDACTED"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "ble-server Fallback Hotspot"
    password: "REDACTED"

captive_portal:
    


sensor:
  - platform: dht
    pin: GPIO16
    temperature:
      id: ble_server_temperature
      name: "Ble Server Temperature"
      filters:
      - or:
        - throttle: 10s
        - delta: 0.2
      device_class: temperature
      state_class: measurement
      unit_of_measurement: °C
    humidity:
      id: ble_server_humidity
      name: "Ble Server Humidity"
      filters:
      - delta: 2
      device_class: humidity
      state_class: measurement
      unit_of_measurement: "%"
    update_interval: 10s



interval:
  - interval: 10seconds
    then:
      - lambda: |-
          ESP_LOGD("advertisement", "Refreshing advertisement");

          // We allocate single instance in memory and refresh it in place
          static manufacturer_data_t* advertisement = new manufacturer_data_t;

          // Assuming there is a time component configured with identifier ntp
          advertisement->data.timestamp = id(ntp).now().timestamp;

          // Assuming there is a sensor component configured with identifier ble_server_temperature
          advertisement->data.ble_server_temperature = (uint16_t) id(ble_server_temperature).state;

          if (advertisement->company_id == 0) { // Execute once after boot for initial setup
              ESP_LOGD("advertisement", "Initializing advertisement");

              advertisement->company_id = 0xFFFF; // 0xFFFF reserved for local testing and non-commercial use
              id(ble).get_advertising()->set_manufacturer_data((uint8_t*) advertisement, sizeof(manufacturer_data_t));

              ESP_LOGD("main", "Advertisement initialized");
          }

          // Force esp_ble to use new data
          id(ble).get_advertising()->start();

          ESP_LOGD("advertisement", "Advertisement refreshed");


ble-server-structs.h

typedef struct payload {

  time_t   timestamp;
  uint16_t ble_server_temperature;

} __attribute__((packed)) payload_t;


typedef struct manufacturer_data {

  uint16_t   company_id;
  payload_t  data;

} manufacturer_data_t;

Any idea what I am doing wrong?

Hi @anthonykeane, from a glance, you’re missing

esp32_ble_server:

from your YAML that would pull-in server-related BLE dependencies.

As a follow-up, I’d just mention that I found the above setup working perfectly when I have a constantly powered server, and a client that has to consume data quickly every now and then. If both transmitter and receiver have constant power, you might want to look into BTHome or ESP-Now based solutions - I think both are widely mentioned in ESPHome discord (#show-off) and docs.

@ashald Thank you, It works well now.

I really appreciate your help,

Any chance you could update the docs so the example has esp32_ble_server: included?

Hi, I wanted to create an esp32 water level sensor to run from a battery.
I was hoping to use BLE and although I have enabled esp32_ble_server: in my yaml. Home assistant doesn’t find it. but my phone does.

My home assistant is a raspberry pi4 which does have Bluetooth and is added as a device within HA.

is there something I’m doing wrong? can someone point me in the right direction?

The “esp32_ble_server” is for other components to use it as a base for them to expose thier entity but that does not mean it shows up in HA. You could use “esphome-ble-controller” and a second esp connected to WIFI and main’s power to publish it to HA

Hi
Unfortunately this now fails I guess, because of some code changes around the BLE improv push

Any idea on how to get advertising of data working again without needing to go through client/server connection?

Thanks!

Ok. To follow up my earlier message, I have successfully managed to get advertising of state data within Manufacturer data working correctly. This allows sending data, and also reading of data on IOS which locks downs all advertised attributes except manufacturer data.

Using latest esphome 2023.10.6

No need for structs or additional files

Make sure ble_server enabled. Note here this has id: ble

esp32_ble_server:
  id: ble
  manufacturer_data: [0xFF, 0xFF, 0xF1, 0x00]

&&

time:
  - platform: sntp
    id: ntp  
    on_time:
    - seconds: 0,10
      then:
        - lambda: |-
            ESP_LOGD("main", "Advertisement updated");           
            float f_value = id(levelmetres).state;
            uint8_t *float_data = (uint8_t*)&f_value;
            ESP_LOGD("main", "Sending updated Level %f", id(levelmetres).state );
            id(ble).set_manufacturer_data({ 0xFF, 0xFF, 0xF9, float_data[0], float_data[1], float_data[2], float_data[3]});

Using time, repeating every 10 seconds.
levelmetres = is a template level sensor.
The rest convert the float value to hex chars and updates the manufacturer data, with the values.
To read back convert the values after 0xF9 to float using Little endian.

Glenn

1 Like

Hi, I can’t have esphome to compile. I get a error on get_advertising()

error: ‘class esphome::esp32_ble::ESP32BLE’ has no member named ‘get_advertising’; did you mean ‘advertising_’?

Is that still working for you?

How do you get this information with ESP32 esphome ble client?

It tells you in the docs how to set up devices, scan for devices, etc…

I understand the concept but Im not sure how to write a lambda code to “convert the values after 0xF9 to float using Little endian.”

That’s what I see in the Logs:

[13:59:43][VV][esp32_ble_tracker:440]: Address: 68:B6:B3:AF:9A:22 (PUBLIC)
[13:59:43][VV][esp32_ble_tracker:442]: RSSI: -58
[13:59:43][VV][esp32_ble_tracker:443]: Name: ‘’
[13:59:43][VV][esp32_ble_tracker:451]: Ad Flag: 6
[13:59:43][VV][esp32_ble_tracker:457]: Manufacturer data: F9.A4.55.F8.42 (5)
[13:59:43][VV][esp32_ble_tracker:473]: Adv data: 02.01.06.08.FF.FF.FF.F9.A4.55.F8.42 (12)

So the msg is being advertised, I know the MAC address. Seems like I need manufacture_id for ble_client which I assume is FFFF in this case. The manufacture data is there.

I see this example on esphome website that would update a template sensor with the BLE value above, just don’t know how to configure.

esp32_ble_tracker:
  on_ble_manufacturer_data_advertise:
    - mac_address: 68:B6:B3:AF:9A:22
      manufacturer_id: FFFF
      then:
        - lambda: |-
            if (x[0] != 0x7b || x[1] != 0x61) return;
            int value = x[2] + (x[3] << 8);
            id(ble_sensor).publish_state(value);

Thank you!

Yaml please, not gibberish.

I have tried this:

  on_ble_manufacturer_data_advertise:
    - mac_address: 68:B6:B3:AF:9A:22
      manufacturer_id: FFFF
      then:
        - lambda: |-
            int value = x[1] + (x[2] << 8) + (x[3] << 16) + (x[3] << 24);
            id(ble_sensor).publish_state(value);

But Im getting wrong values:

[17:10:14][VV][esp32_ble_tracker:440]:   Address: 68:B6:B3:AF:9A:22 (PUBLIC)
[17:10:14][VV][esp32_ble_tracker:442]:   RSSI: -60
[17:10:14][VV][esp32_ble_tracker:443]:   Name: ''
[17:10:14][VV][esp32_ble_tracker:451]:   Ad Flag: 6
[17:10:14][VV][esp32_ble_tracker:457]:   Manufacturer data: F9.74.A5.F7.42 (5)
[17:10:14][VV][esp32_ble_tracker:473]: Adv data: 02.01.06.08.FF.FF.FF.F9.74.A5.F7.42 (12)
[17:10:14][V][sensor:043]: 'Test Voltage': Received new state -134765200.000000
[17:10:14][D][sensor:094]: 'Test Voltage': Sending state -134765200.00000 V with 0 decimals of accuracy
[17:10:14][VV][api.service:140]: send_sensor_state_response: SensorStateResponse {
  key: 3719805054
  state: -1.34765e+08
  missing_state: NO
}

Im picking up the BLE transmitted Hex values just not getting the decimal conversion right.
Thank you!

Have you asked the people in the discord server?

I got the answer from someone smart in another group. I was converting Int instead of Float. That’s what works for me:

  on_ble_manufacturer_data_advertise:
    - mac_address: #your MAC address
      manufacturer_id: FFFF
      then:
        - lambda: |-
            float value = *((float*)&x[1]);
            id(ble_sensor).publish_state(value);

wasn’t that suggested to you right here?

I did write I understand but didn’t know how to do it, see below.

Nonetheless, you helped a lot with answers like this:

Fallingaway24Justin6d
It tells you in the docs how to set up devices, scan for devices, etc…

Or this:

Fallingaway24Justin6d
Have you asked the people in the discord server?

Thanks for pointing out that too, very helpful:

Fallingaway24Justin6d
wasn’t that suggested to you right here?

I just really want to thank you for the great help you provided. I didn’t know what I would do without you.

Hopefully my reply helps someone else that is not that good on coding.