Inkbird BBQ4T Local Control

I got this done last night. Big thanks to @kcraig73 for your awesome work!

In case others run into trouble compiling (I’m a ESPhome n00blet as well) with errors about home assistant time, and bitset not being part of the std lib, I had to allow ESPhome to craft the API and crypto bits and make sure that portion was in my yaml config. I also did DigiBlur’s Wyse outdoor smart outlet last night which worked without the crypto and HA API stuff.

My plan for this evening is to leverage my newfound bbq4T temps with NodeRed to control my inexpensive MasterBuilt smoker using the Wyse outlet relay.

Going to transplant an esp32 12f onto my iBBQ-4BW in place of the WBR3 tuya chip and report back, I’m sure the ESPhome code should be similar - maybe just some different pins etc. Down the rabbit hole I go!

Woohoo it works with the iBBQ-4BW with the transplanted esp32-c3-12f. All I did so far was change the UART pins from (GPIO20 & 21) on the code from @demitrix, Thanks Bob for the info and mapping - still have to look at mine as I am sure there are some differences but so far I’ve checked and the F/C temp toggle works and the probe temp and numbers are correct on display and in HA updated fast!


Excuse the quick soldering job theres a few points with more than enough goob on em’!
Cheers,
Blake

So, I am also down the rabbit hole for changing the iBBQ-4BW to an ESP8266. So far so good, I got most of it running now! Will post my code when done, because unfortunately almost everything needed a remap to work on this unit. Only temperature and some minor others things where correct, rest was broken.

I am having only this small challenge where I cant get my head around. In order to silent the temperature alarm I need to set datapoint 13. This is a bitmask (value e.g. but changes fast: A1000E0)

When the alarm goes off, the last 0 changes to 1,2,4 or 8 to indicate which probe is giving the alarm. Reading this value is no issue:

    - sensor_datapoint: 13 
      datapoint_type: bitmask
      then:
        lambda: |-
          std::bitset<20> alerts(x);
          id(probe_1_alert).publish_state(alerts.test(0));
          id(probe_2_alert).publish_state(alerts.test(1));
          id(probe_3_alert).publish_state(alerts.test(2));
          id(probe_4_alert).publish_state(alerts.test(3));

But, how can I clear the last byte to stop the alarm without nowing the exact value of the other bytes?? These can change randomly (e.g. Bleutooth disconnect, Backlight off, charger off etc) so reading them and writing it back is not safe to do. So how can I just send a ‘0’ to the last byte with this button:

button:
  - platform: template
    name: "iBBQ Clear Alerts"
    id: clear_alerts
    on_press:
      then: 
        - lambda: |-
        [CODE NEEDED] 

Or an other solution is also fine, but cant get it to work.

When this last thing is working I will share my YAML for others to be at help :slight_smile:

I had a standard ble inkbird that worked great in HA it took A crap and I ordered this unit
Was surprised I could not get it into HA
I guess I have a new project when I have time
Thanks for posting

So many hours later I still can’t clear a probe alarm without disabling the probe monitoring or muting the whole device.

When the alarm goes of, indicating with the final ‘2’ that probe 2 is causing the alarm:

  • Datapoint 13 update to 0XA1800D2

When I clear the alarm using the bluetooth Inkbird app or the button on the device (‘0’ indicating no probe is causing a alarm):

  • Datapoint 13 update to 0XA1800D0

No other data is transfered then setting datapoint 13 to this value, so it must be the place to clear a alarm. I try to do it from ESPHome with this button:

  - platform: template
    name: "iBBQ Clear Alerts"
    id: clear_alerts
    on_press:
      then: 
        - lambda: |-           
           ESP_LOGI("main", "Setting now!");
           uint8_t datapoint_id = 13; 
           uint32_t value = 0xA1800D0; 
           uint8_t length = 4; 
           id(mcu).set_bitmask_datapoint_value(datapoint_id, value, length); // No error, but not working
           id(mcu).set_integer_datapoint_value(datapoint_id, value); // Error setting datapoint with incorrect type
           id(mcu).force_set_bitmask_datapoint_value(datapoint_id, value, length); // No error but not working

The bitmask set functions give a log return:

[D][tuya:623]: Setting datapoint 13 to 169345232

This equals 0xA1800D0 in HEX. But maybe I need to send it differently? Because clearing it using the app says:

[D][tuya:372]: Datapoint 13 update to 0XA1800D0

Any advise? I am lost :slight_smile: @kcraig73 maybe?

EDIT: also tried using set_raw_datapoint_value, but that too is saying incorrect type.

So here is the final yaml. As mentioned above I did not get the probe ‘clear alarm’ working, but as a work around decided to add a ‘mute sound for 2 minutes’ button.

Please note:

  • It is not possible to change a WBR3 for a ESP8266 12f without extra mods. You will miss a couple of crucial pullup resistors for the ESP to run. I decided to use a small D1 mini pro board and feed it from the orginal 3.3V power of the WBR3 chip. You only need four wires (GND, 3.3V, TX and RX) to connect the ESP to the inkbird. I did remove the WBR3, but be aware that you don’t burn the underlaying LCD! I decided to remove the LCD during desoldering the WBR3. Maybe leaving the WBR3 in the PCB and disabling is by cutting the Enable pin is better idea, but I did not try that.

As for the code, here are the mappings I roughly found out:

Datapoint 102: int value (value: 15)     -> Backlight timer
Datapoint 13: bitmask (value: a100060)   -> General info LSB = probe alerts, next byte is probe precense, bit 18 is charger connected
Datapoint 107: int value (value: 5361)   -> T Probe 1
Datapoint 108: int value (value: 327661) -> T Probe 2
Datapoint 109: int value (value: 327661) -> T Probe 3
Datapoint 110: int value (value: 5361)   -> T probe 4
Datapoint 112: int value (value: 10)     -> ?
Datapoint 1: switch (value: ON)          -> Power on button?
Datapoint 19: enum (value: 0)            -> ?
Datapoint 101: int value (value: 100)    -> battery value 
Datapoint 123: raw (value: 00.00.00.00.00 (5)) -> Probe 1 alerts (10.x.x.x is active, 0.x.x.x is not. in F, factor 10, temperature bytes switched around)
Datapoint 120: raw (value: 00.00.00.00.00 (5)) -> Probe 2 alerts
Datapoint 121: raw (value: 00.00.00.00.00 (5)) -> Probe 3 alerts
Datapoint 122: raw (value: 00.00.00.00.00 (5)) -> Probe 4 alerts
Datapoint 103: raw (value: 11.01)        -> Alarm repeat interval
Datapoint 106: raw (value: 80.54.6E.77.X.X)  -> Bluetooth MAC
Datapoint 113: raw (value: 01.02.03.04)  -> ?
Datapoint 111: switch (value: OFF)       -> backlight on/off
Datapoint 104: switch (value: ON)        -> mute device on/off

The ESP YAML file, please note:

  • it is for degrees C (where F is used, HA automatically converts is to C already)
  • I did drop the low-temperature alert support as the original app does not support that and I don’t need it.
  • Backlight always on is not supported by this device, in the code is a workaround to directly activate it when it goes off after 1 hour and was set to always on.
esphome:
  name: esp-bbq-thermometer
  friendly_name: ESP BBQ thermometer
  on_boot: 
    then:
      - delay: 5s
      - number.set:
          id: probe_1_high_temp
          value: 180
      - number.set:
          id: probe_2_high_temp
          value: 180
      - number.set:
          id: probe_3_high_temp
          value: 180
      - number.set:
          id: probe_4_high_temp
          value: 180
      - select.set:
          id: screen_timeout
          option: "Never"  #personal preferences, add your own default value here

api:


ota:
  - platform: esphome


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


esp8266:
  board: esp01_1m
  
logger:
  baud_rate: 0

uart:
  rx_pin: GPIO2
  tx_pin: GPIO4
  baud_rate: 115200

tuya:
  id: mcu
  time_id: homeassistant_time
  on_datapoint_update:
    - sensor_datapoint: 13 
      datapoint_type: bitmask
      then:
        lambda: |-
          std::bitset<8> probes(x);
          probes.flip();
          id(probe_1_present).publish_state(probes.test(4));
          id(probe_2_present).publish_state(probes.test(5));
          id(probe_3_present).publish_state(probes.test(6));
          id(probe_4_present).publish_state(probes.test(7));
    - sensor_datapoint: 13 
      datapoint_type: bitmask
      then:
        lambda: |-
          std::bitset<20> alerts(x);
          id(probe_1_alert).publish_state(alerts.test(0));
          id(probe_2_alert).publish_state(alerts.test(1));
          id(probe_3_alert).publish_state(alerts.test(2));
          id(probe_4_alert).publish_state(alerts.test(3));
          id(charging).publish_state(alerts.test(18));


text_sensor:
  - platform: version
    name: ESPHome Version
    internal: true  
 
time:
  - platform: homeassistant
    id: homeassistant_time

button:
  - platform: template
    name: "iBBQ Mute for 2 mins"
    id: mute_2mins
    icon: "mdi:volume-mute"
    on_press:
      then: 
       - switch.turn_on: mute
       - delay: 120s
       - switch.turn_off: mute         

switch:
  - platform: restart
    id: rebootswitch
    name: "ESP restart"
  - platform: "tuya"
    name: "iBBQ Backlight"
    id: light
    switch_datapoint: 111
    icon: "mdi:television-ambient-light"
    #forever backlight ON not supported, setting it ON after turn OFF instantly is the workaround. 
    on_turn_off: 
        if:
          condition:
            lambda: |-
              return id(screen_timeout).state == "Never";
          then:
            - delay: 0.1s
            - switch.turn_on: light   
  - platform: "tuya"
    name: "iBBQ Mute"
    id: mute
    inverted: true
    switch_datapoint: 104
    icon: "mdi:volume-mute"
  - platform: template
    name: "iBBQ Probe 1 Alerts"
    id: probe_1_alerts
    icon: "mdi:thermometer-alert"
    optimistic: true
    on_turn_off: 
      - lambda: |-
            std::vector<uint8_t> raw(5,0);
            int temp = 5270; //default temp of 300gr C instead o 0gr to prevent direct alarm when activating
            raw.at(0) = 0;
            raw.at(1) = temp & 0xFF;
            raw.at(2) = temp >> 8;
            raw.at(3) = 0;
            raw.at(4) = 0;
            id(mcu).set_raw_datapoint_value(120,raw);
    on_turn_on: 
      - lambda: |-
            std::vector<uint8_t> raw(5,0);
            int temp = ((int)id(probe_1_high_temp).state * (9.0/5.0) + 32.0)* 10; //get setpoint from number input
            raw.at(0) = 16;
            raw.at(1) = temp & 0xFF;
            raw.at(2) = temp >> 8;
            id(mcu).set_raw_datapoint_value(120,raw);

  - platform: template
    name: "iBBQ Probe 2 Alerts"
    id: probe_2_alerts
    icon: "mdi:thermometer-alert"
    optimistic: true
    on_turn_off: 
      - lambda: |-
            std::vector<uint8_t> raw(5,0);
            int temp = 5270; //default temp of 300gr C instead o 0gr to prevent direct alarm when activating
            raw.at(0) = 0;
            raw.at(1) = temp & 0xFF;
            raw.at(2) = temp >> 8;
            raw.at(3) = 0;
            raw.at(4) = 0;
            id(mcu).set_raw_datapoint_value(121,raw);
    on_turn_on: 
      - lambda: |-
            std::vector<uint8_t> raw(5,0);
            int temp = ((int)id(probe_2_high_temp).state * (9.0/5.0) + 32.0)* 10; //get setpoint from number input
            raw.at(0) = 16;
            raw.at(1) = temp & 0xFF;
            raw.at(2) = temp >> 8;
            id(mcu).set_raw_datapoint_value(121,raw);
    
  - platform: template
    name: "iBBQ Probe 3 Alerts"
    id: probe_3_alerts
    icon: "mdi:thermometer-alert"
    optimistic: true
    on_turn_off: 
      - lambda: |-
            std::vector<uint8_t> raw(5,0);
            int temp = 5270; //default temp of 300gr C instead o 0gr to prevent direct alarm when activating
            raw.at(0) = 0;
            raw.at(1) = temp & 0xFF;
            raw.at(2) = temp >> 8;
            raw.at(3) = 0;
            raw.at(4) = 0;
            id(mcu).set_raw_datapoint_value(122,raw);
    on_turn_on: 
      - lambda: |-
            std::vector<uint8_t> raw(5,0);
            int temp = ((int)id(probe_3_high_temp).state * (9.0/5.0) + 32.0)* 10; //get setpoint from number input
            raw.at(0) = 16;
            raw.at(1) = temp & 0xFF;
            raw.at(2) = temp >> 8;
            id(mcu).set_raw_datapoint_value(122,raw);
  - platform: template
    name: "iBBQ Probe 4 Alerts"
    id: probe_4_alerts
    icon: "mdi:thermometer-alert"
    optimistic: true
    on_turn_off: 
      - lambda: |-
            std::vector<uint8_t> raw(5,0);
            int temp = 5270; //default temp of 300gr C instead o 0gr to prevent direct alarm when activating
            raw.at(0) = 0;
            raw.at(1) = temp & 0xFF;
            raw.at(2) = temp >> 8;
            raw.at(3) = 0;
            raw.at(4) = 0;
            id(mcu).set_raw_datapoint_value(123,raw);
    on_turn_on: 
      - lambda: |-
            std::vector<uint8_t> raw(5,0);
            int temp = ((int)id(probe_4_high_temp).state * (9.0/5.0) + 32.0)* 10; //get setpoint from number input
            raw.at(0) = 16;
            raw.at(1) = temp & 0xFF;
            raw.at(2) = temp >> 8;
            id(mcu).set_raw_datapoint_value(123,raw);

number:
  - platform: template
    name: "iBBQ Probe 1 High Temperature"
    id: probe_1_high_temp
    unit_of_measurement: "°C"
    icon: "mdi:fire-alert"
    optimistic: true
    min_value: 0
    max_value: 300
    step: 1
    on_value:
      then:
        - delay: 3s 
        - lambda: |-
            std::vector<uint8_t> raw(5,0);
            int temp = ((int)id(probe_1_high_temp).state * (9.0/5.0) + 32.0)* 10; //get setpoint from number input
            if (id(probe_1_alerts).state) raw.at(0) = 16;
            else  raw.at(0) = 0;
            raw.at(1) = temp & 0xFF;
            raw.at(2) = temp >> 8;
            id(mcu).set_raw_datapoint_value(120,raw);


  - platform: template
    name: "iBBQ Probe 2 High Temperature"
    id: probe_2_high_temp
    unit_of_measurement: "°C"
    icon: "mdi:fire-alert"
    optimistic: true
    min_value: 0
    max_value: 300
    step: 1
    on_value:
      then:
        - delay: 3s 
        - lambda: |-            
            std::vector<uint8_t> raw(5,0);
            int temp = ((int)id(probe_2_high_temp).state * (9.0/5.0) + 32.0)* 10; //get setpoint from number input
            if (id(probe_2_alerts).state) raw.at(0) = 16;
            else  raw.at(0) = 0;
            raw.at(1) = temp & 0xFF;
            raw.at(2) = temp >> 8;
            id(mcu).set_raw_datapoint_value(121,raw);
 
  - platform: template
    name: "iBBQ Probe 3 High Temperature"
    id: probe_3_high_temp
    unit_of_measurement: "°C"
    icon: "mdi:fire-alert"
    optimistic: true
    min_value: 0
    max_value: 300
    step: 1
    on_value:
      then:
        - delay: 3s 
        - lambda: |-
            std::vector<uint8_t> raw(5,0);
            int temp = ((int)id(probe_3_high_temp).state * (9.0/5.0) + 32.0)* 10; //get setpoint from number input
            if (id(probe_3_alerts).state) raw.at(0) = 16;
            else  raw.at(0) = 0;
            raw.at(1) = temp & 0xFF;
            raw.at(2) = temp >> 8;
            id(mcu).set_raw_datapoint_value(122,raw);

  - platform: template
    name: "iBBQ Probe 4 High Temperature"
    id: probe_4_high_temp
    unit_of_measurement: "°C"
    icon: "mdi:fire-alert"
    optimistic: true
    min_value: 0
    max_value: 300
    step: 1 
    on_value:
      then:
       - delay: 3s
       - lambda: |-
            std::vector<uint8_t> raw(5,0);
            int temp = ((int)id(probe_4_high_temp).state * (9.0/5.0) + 32.0)* 10; //get setpoint from number input
            if (id(probe_4_alerts).state) raw.at(0) = 16;
            else  raw.at(0) = 0;
            raw.at(1) = temp & 0xFF;
            raw.at(2) = temp >> 8;
            id(mcu).set_raw_datapoint_value(123,raw);
    
sensor:
  - platform: wifi_signal
    name: ESP BBQ WiFi Signal
    update_interval: 12h
    internal: true
  - platform: tuya
    name: "iBBQ Probe 1 Temperature"
    id: probe_1_temperature
    icon: "mdi:thermometer"
    sensor_datapoint: 107
    unit_of_measurement: "°F"
    device_class: "temperature"
    state_class: "measurement"
    accuracy_decimals: 1
    filters:
      - lambda: |-
          if (x < 100000) return x * 0.01;
          else return {};
  - platform: tuya
    name: "iBBQ Probe 2 Temperature"
    id: probe_2_temperature
    icon: "mdi:thermometer"
    sensor_datapoint: 108
    unit_of_measurement: "°F"
    device_class: "temperature"
    state_class: "measurement"
    accuracy_decimals: 1
    filters:
      - lambda: |-
          if (x < 100000) return x * 0.01;
          else return {};
  - platform: tuya
    name: "iBBQ Probe 3 Temperature"
    id: probe_3_temperature
    icon: "mdi:thermometer"
    sensor_datapoint: 109
    unit_of_measurement: "°F"
    device_class: "temperature"
    state_class: "measurement"
    accuracy_decimals: 1
    filters:
      - lambda: |-
          if (x < 100000) return x * 0.01;
          else return {};
  - platform: tuya
    name: "iBBQ Probe 4 Temperature"
    id: probe_4_temperature
    icon: "mdi:thermometer"
    sensor_datapoint: 110
    unit_of_measurement: "°F"
    device_class: "temperature"
    state_class: "measurement"
    accuracy_decimals: 1
    filters:
      - lambda: |-
          if (x < 100000) return x * 0.01;
          else return {};
          
  - platform: tuya
    name: "iBBQ Battery"
    id: battery
    sensor_datapoint: 101
    unit_of_measurement: "%"
    device_class: "battery"
    state_class: "measurement"
    
binary_sensor:
  - platform: template
    name: "iBBQ Charging"
    id: charging
    icon: "mdi:battery-charging"
  - platform: template
    name: "iBBQ Probe 1 Presence"
    id: probe_1_present
    icon: "mdi:connection"
  - platform: template
    name: "iBBQ Probe 2 Presence"
    id: probe_2_present
    icon: "mdi:connection"
  - platform: template
    name: "iBBQ Probe 3 Presence"
    id: probe_3_present
    icon: "mdi:connection"
  - platform: template
    name: "iBBQ Probe 4 Presence"
    id: probe_4_present
    icon: "mdi:connection"
  - platform: template
    name: "iBBQ Probe 1 Alert"
    id: probe_1_alert
    icon: "mdi:alert"
  - platform: template
    name: "iBBQ Probe 2 Alert"
    id: probe_2_alert
    icon: "mdi:alert"
  - platform: template
    name: "iBBQ Probe 3 Alert"
    id: probe_3_alert
    icon: "mdi:alert"
  - platform: template
    name: "iBBQ Probe 4 Alert"
    id: probe_4_alert
    icon: "mdi:alert"

select:
  - platform: tuya
    name: "iBBQ Units"
    enum_datapoint: 19
    optimistic: true
    options:
      0: Celsius
      1: Fahrenheit
      
  - platform: template
    name: "iBBQ Screen Timeout"
    id: screen_timeout
    icon: "mdi:power-sleep"
    optimistic: true
    restore_value: true
    options:
      - "15 Seconds"
      - "30 Seconds"
      - "1 Minute"
      - "5 Minutes"
      - "15 Minutes"
      - "30 Mintues"
      - "1 Hour"
      - "Never"
    on_value:
      - lambda: |-
          switch(i) {
            case 0:
              id(mcu).set_integer_datapoint_value(102,15);
              break;
            case 1:
              id(mcu).set_integer_datapoint_value(102,30);
              break;
            case 2:
              id(mcu).set_integer_datapoint_value(102,60);
              break;
            case 3:
              id(mcu).set_integer_datapoint_value(102,300);
              break;
            case 4:
              id(mcu).set_integer_datapoint_value(102,900);
              break;
            case 5:
              id(mcu).set_integer_datapoint_value(102,1800);
              break;
            case 6:
              id(mcu).set_integer_datapoint_value(102,3600);
              break;
            case 7:
              id(mcu).set_integer_datapoint_value(102,3600); //3601 and 0 not working on this unit
              break;
          }

Hope it helps others. It was a fun project to do, and many thanks to @kcraig73 for making the fundament for this.

I just logged in to say thanks to @kcraig73 and @demitrix for all the heavy lifting.
I’ve received a new revision with a Realtek module; replaced it with an ESP-12 and flashed Tasmota on it with some magic to decode the payload in Home Assistant, of which all important steps are described here: Inkbird IBBQ-4T - Hackerspace ACKspace (it still has rough edges but good enough to work with)

1 Like