BBQ Grill Thermometer - AT-02

I have just installed this great BBQ Grill Thermometer and it would be great to get this working natively with HA.

Looks like the device requires an active bluetooth connection or at least this is what the OOTB app (toGrill) is doing…

Could some one guide me how on what would be needed to make it work with HA.

I can see the following logs on my HA, but don’t know what kind of integration works with LE active devices.

2023-06-20 22:45:15.696 DEBUG (MainThread) [homeassistant.components.bluetooth.manager] BLE-Proxy (4c:75:25:d6:6c:e8) [connectable]: DD:49:AF:6A:D2:BD AdvertisementData(local_name='AT-02', manufacturer_data={53949: b'j\xafI\xdd\x00'}, service_uuids=['0000cee0-0000-1000-8000-00805f9b34fb'], tx_power=-127, rssi=-93) match: set()

image

Updates: After some investigation I have found out that this device does not broadcast the values, it requires to have an active connection to it and then after it starts sending the values of the different probes. So it will require that I build a custom driver.

Below are some samples from the main (Ambient) probe with the corresponding displayed readings, I have tried several of the common techniques (1*, 2*) but with non of them I am able to decode the readings.

Could anyone out their with more experience on decoding please help me figure out what is the algorithm I need to use.

26 Celsius
55aa000fa1ffffffffffffffffffffffff010d5d

27 Celsius
55aa000fa1ffffffffffffffffffffffff011040

28 Celsius
55aa000fa1ffffffffffffffffffffffff011848

1* BBQ Thermometer Hacking - Part 2 (Wireshark & GATT) - YouTube
2* Chinese ble bbq probe · theengs/decoder · Discussion #149 · GitHub

Hi, did you get this to work?

26 Celsius
55aa000fa1ffffffffffffffffffffffff**010d**5d

27 Celsius
55aa000fa1ffffffffffffffffffffffff**0110**40

28 Celsius
55aa000fa1ffffffffffffffffffffffff**0118**48

Looks like the two before last octets contain the temp, likely with a decimal which might not display on the actual device

010d hex big endian = 269 decimal / 10 = 26.9°C
0110 hex big endian = 272 decimal / 10 = 27.2°C
0118 hex big endian = 280 decimal / 10 = 28°C

Just a hunch from your above data, as I would have assumed 26.9 to be rounded to 27 as well, but could also be a slight reading time discrepancy :wink:

I’ve also been looking into this, have no experience in it at al but with trial,error and guessing it looks like:

Header (6): 55aa00 → All messages start with this
Message type? (4): 0fa1 → Changes for different payload types (i.e. 07a3 for command, 02a3 on confirmation)
Payload (20): (ffffffffffffffffffffffff010d) probe1(4),probe2(4),probe3(4),probe4(4),unknown(8),ambient(4)
Checksum: 2 (5d) → using xor on decimal values, so first change all pairs (except checksum) from hex to dec (55=85,aa=170 etc) and then XOR their values (85 ^ 170 ^ etc), convert the result to hex, and it matches the checksum 5d

Some of my examples:

55aa000fa1ffffffffffffffffffffffff01b5e5

  • ambient (01b5) → 437 → 43.7C

55aa000fa1014bffffffffffffffffffff01b5af

  • probe1 (014b) → 331 → 31.1C
  • ambient (01b5) → 437 → 43.7C

55aa000fa1ffffffff0146ffffffffffff01a3b4

  • probe 3 (0146) → 326 → 32.6C
  • ambient (01a3) → 419-> 41.9C

Also looked at some commands:

Set ambient temp range alarm to 30c-80c:
55aa0007a30000012c032055

  • 55aa00 → header
  • 07a3 → message type?
  • 0000 → command?
  • 012c → 300 → 30.0c
  • 0320 → 800 → 80.0c
  • 55 → checksum

Clear ambient temp alarm
55aa0007a300020000000059

  • 55aa00 → header
  • 07a3 → message type?
  • 0002 → command
  • 0000 → empty
  • 0000 → empty
  • 59 → checksum

Set peak temp alarm to 89c
55aa0007a30001037a000023

  • 55aa00 → Header
  • 07a3 → Message type
  • 0001 → Command
  • 037a → 890 → 89.0c
  • 0000 → empty
  • 23 → checksum

Edit:
Thermometer always response to commands with
55aa0002a3015f

2 Likes

Hi,

Bought mine a month ago thinking it was already possible to use in HA. I have also another blue tooth thermometer from the brand Bastard and this works out of the box. So I hope someone gets this working soon. The range is also very poor of the device that could be fixed then by my bluetooth proxies.

Thanks. JD

1 Like

Any type of support would be awesome.

Hi all,

So I’ve bought AT-02 from AliExpress and managed to connect it to my ESP32 via BLE. I’ve done it by reverse engineering the app code…

Here’s how:

  1. Scan / connect to AT-02
  2. Write the following codes to 0xCEE1:
    a) 0x55AA0003A000005C; app waits for 200ms, then writes
    b) 0x55AA0002A1005C

These are sort of authorisation codes. Then you should register for notify at 0xCEE2, where AT-02 will send all updated temperatures.

Decifering works as follows:
Data = 55 aa 00 0f a1 ffff ffff ffff ffff ffff ffff 01b5 e5

If Data[0]=0x55, Data[1]=0xAA and Data[4]=0xA1, then temperature is reported.
Then the app loops from byte 5 to 17 in pairs, e.g. 5, 7, 9…
Probe_id is byte number less 5 divided by 2 and +1, i.e. ((i-5)/2+1).
If probe_id is 7, it is ambient probe. In the code above, the temp at position #17 (which is ambient probe (17-5)/2+1=7 ).

That was sufficient for my case, but in case anyone needs other functions - happy to look into decompiled code of the app together :slight_smile:

1 Like

Hello. I’m trying to write some ESPHome code to integrate the AT02 into Home Assistant. Here is where I’ve got to so far. At the moment I’m just trying to dump out the data to a string at the moment. My thinking if I can see some data, I can then try to process it as above.

Here is my code

esphome:
  name: barbeque
  friendly_name: Barbeque

esp32:
  board: esp32dev
  framework:
    type: esp-idf
  

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

ota:
  - platform: esphome
    password: "####################"

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

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

captive_portal:
    
# Enable logging
logger:
  level: DEBUG

# Enable Home Assistant API


esp32_ble_tracker:
  scan_parameters:
    interval: 1100ms
    window: 1100ms
    active: true
#  on_ble_advertise:
#    - then:
#        - lambda: |-
#            ESP_LOGD("ble_adv", "New BLE device");
#            ESP_LOGD("ble_adv", "  address: %s", x.address_str().c_str());
#            ESP_LOGD("ble_adv", "  name: %s", x.get_name().c_str());
#            ESP_LOGD("ble_adv", "  Advertised service UUIDs:");
#            for (auto uuid : x.get_service_uuids()) {
#                ESP_LOGD("ble_adv", "    - %s", uuid.to_string().c_str());
#            }
#            ESP_LOGD("ble_adv", "  Advertised service data:");
#            for (auto data : x.get_service_datas()) {
#                ESP_LOGD("ble_adv", "    - %s: (length %i)", data.uuid.to_string().c_str(), data.data.size());
#            }
#            ESP_LOGD("ble_adv", "  Advertised manufacturer data:");
#            for (auto data : x.get_manufacturer_datas()) {
#                ESP_LOGD("ble_adv", "    - %s: (length %i)", data.uuid.to_string().c_str(), data.data.size());}

ble_client:
  - mac_address: E6:2D:7C:52:22:B3
    id: at02
    on_connect: 
      then:
        - lambda: |-
            ESP_LOGD("ble_client_lambda", "Connected to Barbeque Sensor");
        - delay: 3s
        - ble_client.ble_write:
            id: at02
            service_uuid: CEE0
            characteristic_uuid: CEE1
          # List of bytes to write.
            value: [0x55, 0xAA, 0x00, 0x03, 0xA0, 0x00, 0x00, 0x5C]
        - delay: 200ms
        - ble_client.ble_write:
            id: at02
            service_uuid: CEE0
            characteristic_uuid: CEE1
            # List of bytes to write.
            value: [0x55, 0xAA, 0x00, 0x02, 0xA1, 0x00, 0x5C] 
    on_disconnect:
      then:
        - lambda: |-
            ESP_LOGD("ble_client_lambda", "Disconnected from Barbeque Sensor");


text_sensor:
  - platform: ble_client
    ble_client_id: "at02"
    name: "data"
    service_uuid: CEE0
    characteristic_uuid: CEE2
    icon: 'mdi:battery'
    on_value: 
      then: 
        - lambda: |- 
            ESP_LOGD("ble_client_lambda", "Characteristic value: %s", x.c_str());

Here is my log:


[21:11:46][D][esp32_ble_tracker:694]:   Name: 'AT-02'
[21:11:46][D][esp32_ble_tracker:219]: Pausing scan to make connection...
[21:11:46][D][esp32_ble_tracker:219]: Pausing scan to make connection...
[21:11:46][D][esp-idf:000][BTU_TASK]: E (705388) BT_BTM: BTM_BleScan scan not active


[21:11:46][D][esp-idf:000][BTU_TASK]: W (705392) BT_APPL:  bta_dm_ble_scan stop scan failed, status=0x6


[21:11:47][D][esp32_ble_client:110]: [0] [E6:2D:7C:52:22:B3] ESP_GATTC_CONNECT_EVT
[21:11:47][D][esp32_ble_client:110]: [0] [E6:2D:7C:52:22:B3] ESP_GATTC_OPEN_EVT
[21:11:47][I][ble_text_sensor:034]: [data] Connected successfully!
[21:11:47][D][esp32_ble_tracker:270]: Starting scan...
[21:11:47][D][esp32_ble_client:306]: [0] [E6:2D:7C:52:22:B3] Event 46
[21:11:47][D][esp32_ble_client:110]: [0] [E6:2D:7C:52:22:B3] ESP_GATTC_SEARCH_CMPL_EVT
[21:11:47][I][esp32_ble_client:227]: [0] [E6:2D:7C:52:22:B3] Connected
[21:11:47][D][ble_client_lambda:067]: Connected to Barbeque Sensor
[21:11:47][D][ble_client.automation:181]: Write type: ESP_GATT_WRITE_TYPE_NO_RSP
[21:11:47][D][ble_client.automation:188]: Found characteristic 0xCEE1 on device E6:2D:7C:52:22:B3
[21:11:47][D][ble_client.automation:181]: Write type: ESP_GATT_WRITE_TYPE_NO_RSP
[21:11:47][D][ble_client.automation:188]: Found characteristic 0xCEE1 on device E6:2D:7C:52:22:B3
[21:11:47][D][ble_client:058]: All clients established, services released
[21:11:47][D][esp32_ble_client:188]: [0] [E6:2D:7C:52:22:B3] cfg_mtu status 0, mtu 23
[21:11:50][D][esp32_ble_client:110]: [0] [E6:2D:7C:52:22:B3] ESP_GATTC_WRITE_CHAR_EVT
[21:11:51][D][esp32_ble_client:110]: [0] [E6:2D:7C:52:22:B3] ESP_GATTC_WRITE_CHAR_EVT

So the AT02 connects to the ESP32 (and the connected icon appears on the AT02). However, I don’t get any data shown in the text sensor. Can anyone give me a few points please?

Thanks

Adam

So I’ve finally got this working. Here is my YAML.

esphome:
  name: barbeque
  friendly_name: Barbeque

esp32:
  board: esp32dev
  framework:
    type: esp-idf
  

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

ota:
  - platform: esphome
    password: "blah blah blah"

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Barbeque Fallback Hotspot"
    password: "blah blah"

captive_portal:
    
# Enable logging
logger:
  level: DEBUG

# Enable Home Assistant API


esp32_ble_tracker:
  scan_parameters:
    interval: 1100ms
    window: 1100ms
    active: true

ble_client:
  - mac_address: E6:2D:7C:52:22:B3
    id: at02
    on_connect: 
      then:
        - lambda: |-
            ESP_LOGD("ble_client_lambda", "Connected to Barbeque Sensor");
        - delay: 3s
        - ble_client.ble_write:
            id: at02
            service_uuid: CEE0
            characteristic_uuid: CEE1
          # List of bytes to write.
            value: [0x55, 0xAA, 0x00, 0x03, 0xA0, 0x00, 0x00, 0x5C]
        - delay: 2s
        - ble_client.ble_write:
            id: at02
            service_uuid: CEE0
            characteristic_uuid: CEE1
            # List of bytes to write.
            value: [0x55, 0xAA, 0x00, 0x02, 0xA1, 0x00, 0x5C] 
    on_disconnect:
      then:
        - lambda: |-
            ESP_LOGD("ble_client_lambda", "Disconnected from Barbeque Sensor");


sensor:
  - platform: ble_client
    ble_client_id: at02
    id: bbq_data
    type: characteristic
    service_uuid: 'CEE0'  
    characteristic_uuid: 'CEE2'  
    notify: true
    lambda: |-
      if (x.size() >= 18 && x[0] == 0x55 && x[1] == 0xAA && x[4] == 0xA1) {
        int16_t raw_temp1 = (x[5] << 8) | x[6];
        float temperature1 = raw_temp1 / 10.0;
        id(probe1).publish_state(temperature1);

        int16_t raw_temp2 = (x[7] << 8) | x[8];
        float temperature2 = raw_temp2 / 10.0;
        id(probe2).publish_state(temperature2);

        int16_t raw_temp3 = (x[9] << 8) | x[10];
        float temperature3 = raw_temp3 / 10.0;
        id(probe3).publish_state(temperature3);

        int16_t raw_temp4 = (x[11] << 8) | x[12];
        float temperature4 = raw_temp4 / 10.0;
        id(probe4).publish_state(temperature4);

        int16_t raw_ambient_temp = (x[17] << 8) | x[18];
        float ambient_temperature = raw_ambient_temp / 10.0;
        id(ambient).publish_state(ambient_temperature);
      }
      return NAN;
  - platform: template
    name: "Probe 1 Temperature"
    id: probe1
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    update_interval: never

  - platform: template
    name: "Probe 2 Temperature"
    id: probe2
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    update_interval: never

  - platform: template
    name: "Probe 3 Temperature"
    id: probe3
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    update_interval: never

  - platform: template
    name: "Probe 4 Temperature"
    id: probe4
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    update_interval: never

  - platform: template
    name: "Ambient Temperature"
    id: ambient
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    update_interval: never
1 Like

Where will I find the AT-02’s mac address? On the devices printed label and/or from it’s app?