- Inkbird IBT-2X
- Inkbird IBT-4XS
- EasyBBQ FCCID: FCC ID 2AI4MPRO3 (SHENZHEN HYPERSYNES CO.,LTD Smart Wireless Thermometer PRO3)
- @ConnectTimeout: 60 seconds
- @BatteryPollingInterval: 5 minutes
The iBBQ is a Bluetooth LE Gatt Device
@DeviceName: iBBQ
Standard Descriptor:
- @ClientCharacteristicConfigurationDescriptor: @uuid16{0x2902}
Its main service at @uuid16{0xfff0} contains the following characteristics:
- @SettingsResult: @uuid16{0xfff1} @notify returns results from control messages
- @AccountAndVerify: @uuid16{0xfff2} @write deals with the pairing process
- @HistoryData: @uuid16{0xfff3} @notify is not yet properly documented
- @RealtimeData: @uuid16{0xfff4} @notify returns the results from probes
- @SettingsData: @uuid16{0xfff5} @write is where control messages are sent
Here are some hardcoded messages:
- @CredentialsMessage: (:byte[]) { 0x21, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0xb8, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00}
- @RealtimeDataEnableMessage: (:byte[]) { 0x0B, 0x01, 0x00, 0x00, 0x00, 0x00}
- @UnitsFahrenheitMessage: (:byte[]) { 0x02, 0x01, 0x00, 0x00, 0x00, 0x00}
- @UnitsCelsiusMessage: (:byte[]) { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}
- @RequestBatteryLevelMessage: (:byte[]) { 0x08, 0x24, 0x00, 0x00, 0x00, 0x00 }
A message exists to set target temperatures (low and high, the closest of which will be displayed on the screen)
- @SetTargetTempMessage: (:byte[]) { 0x01, probe-nr, low[0:7bits], low[8:15bits], high[0:7bits], high[8:15bits] }
- probe-nr is the probe to select, from 0 onwwards
- low is a int16 signed integer for the lower temperature, in 10^-1 Celsius
- high is a int16 signed integer for the higher temperature, in 10^-1 Celsius
initiate-login ::
write @CredentialsMessage to characteristic @AccountAndVerify
enable-realtime-data ::
enable notifications on @ClientCharacteristicConfigurationDescriptor
write @RealtimeDataEnableMessage to characteristic @SettingsData
set-unit :: Fahrenheit or Celsius
when Fahrenheit write @UnitsFahrenheitMessage to characteristic @SettingsData
when Celsius write @UnitsCelsiusMessage to characteristic @SettingsData
request-battery-level ::
write @RequestBatteryLevelMessage to characteristic @SettingsData
set-target-temp ::
write @SetTargetTempMessage to characteristic @SettingsData
When settings are written to @SettingsData, results are received on @SettingsResult
When request-battery-level has been sent, @SettingsResult will receive data which can be parsed as:
struct BatteryLevels {
header: uint8 = data[0], // header == 0x24
currentVoltage: uint16 = data[1] | data[2] << 8, // up to maxVoltage
maxVoltage: uint16 = data[3] | data[4] << 8, // if 0 maxVoltage is 6550
}
When enable-realtime-data has been sent, @RealtimeData will receive data which can be parsed as:
num_probes = sizeof(data)/sizeof(uint16)
probes: uint16[num_probes]
with 0 < i < num_probes:
probes[i] : uint16 = data[2*i] | data[2*i + 1] << 8
There's an history data end-point, but the format is unknown.
- Device: IBT-4XS
- @SilenceAlarmMessage: (:byte[]) { 0x04, 0xff, 0x00, 0x00, 0x00, 0x00 }
Device alarm can be silenced by sending @SilenceAlarmMessage to the @SettingsData characteristic.
When an alarm is silenced by the user pressing the device's button, @SilenceAlarmMessage is received on @SettingsResult
Okay, answering my own question:
The second byte of the SetTargetTemp message is the probe number, counting from 0.
Bytes 3-4 are the lower end of the target temperature range, as a little-endian signed integer of the temperature in degrees Celcius multiplied by 10. If just a single target temperature is desired then -3000 is passed (i.e. -300°C).
Bytes 5-6 are the top end of the target temperature range, encoded in the same way.
The screen of the device will display whichever end of the range is closest to the current temperature, hence my earlier confusion.