top of page

Building a Custom SwiftUI Application for my Bluetooth Meat Probe

  • Feb 22
  • 4 min read

This Thanksgiving, it was my first time ever cooking a turkey. Our old meat thermometer that's been with us for years now is finally starting to go, and we wanted to upgrade it, especially with Thanksgiving coming around.



We bought this meat thermometer on Amazon and everything worked great.. except for the app (and it was no secret, either) - it was not well received by the community at all.


To highlight a few fun reviews of this app:


"Only works sometimes" (1/5 stars)


"Have a brisket in the smoker and the probe won't even connect to my phone" (1/5 stars)


"Constantly disconnects and probe is not accurate" (1/5 stars)


"Won't connect" (1/5 stars)



It would've been nice to know these things before ordering, but the Amazon listing has 4.7 stars so it can't really be that bad, right?



Myth #1: The Probe Temperature Readings are Inaccurate


We tossed the probe into some ice water and boiling water, because we know very precisely what those temperatures should be without any equipment. Our ice water experiment pictured above wasn't perfect, I would have loved to fill the glass with tiny chunks of ice to get the water as close to 0C as possible, but it sufficed for our comparison. We did the same thing with boiling water, placing both our old meat probe and the new bluetooth probe in the boiling water to check how close not only they would get to each other, but also to 100C (we're in Boston at sea level).


Ice Water Results:

Our old probe: ~1.9 C

New bluetooth probe: ~2.0 C


Boiling Water Results:

Our old probe: ~96 C

New bluetooth probe: ~98 C


These measurements, along with the probe's listed +/- 1 C tolerance fall right into the expected temperature range. I'd say this probe is close enough!



Myth #2: The App is Hit-or-Miss


I do have to unfortunately agree with many of the reviews on this one.


  • The interface is clunky

  • The probe would frequently disconnect from the app

  • Whenever the probe did reconnect, all of the historical data was completely lost


That last bullet point was probably the worst of them all. I didn't want to be mid-cook of my thanksgiving turkey and lose all my tempearture vs time data (Sure, I could start a secondary timer, but why buy a $40 meat probe if it won't do it for me?).


It looks like I have some key elements I need for my app:

  • Simple, responsive interface

  • Handle probe connection (and potential disconnection) gracefully

  • Save my historical cooking data

My App - Step #1: Figure out the Bluetooth


I used an app called LightBlue, which allows you to scan and connect to any peripheral BLE devices around you. After some trial and error, I was able to connect directly to the probe through LightBlue.


After connecting, I saw that I was getting packets of bytes every ~0.5 seconds, they looked like this:


0x00000014DE00F000E9


I figured this was giving me temperature data from the probe but had no idea how it was structured. I broke it apart and scanned multiple packets while the temperature on the handheld device showed 25C:


Packet #1 00 01 B7 0D 01 FF FF FF

Packet #2 00 00 00 64 F5 00 FB 00 DD

Packet #3 00 01 B7 0D 01 FF FF FF

Packet #4 00 00 00 64 F5 00 FA 00 DD

Packet #5 00 01 B7 0D 01 FF FF FF


It's very clear that packets 1, 3, and 5 are identical and packets 2 and 4 differ only slightly. These packets all happened in the span of ~2 seconds, so it would make sense that the temperature of the probe in my ambient room temperature wasn't changing all that much. I wanted to check to see if any of these bytes are anywhere near my room temp reading of 25C or 77F (I was sitting next to a space heater). Through some trial and error, I was able to determine that I could disregard packets like 1, 3, and 5 as I assumed them to be some sort of status packet (they didn't change), but packets with 9 bytes held my temperature data:


    func parsePacket(data: Data) {

        guard data.count >= 9 else { return }


        // Decode probe temp

        let probeRaw = UInt16(data[4]) | (UInt16(data[5]) << 8)

        let probeCVal = Double(probeRaw) / 10.0


        // Decode ambient temp

        let otherRaw = UInt16(data[6]) | (UInt16(data[7]) << 8)

        let ambientCVal = Double(otherRaw) / 10.0


        // Battery

        let bat = Int(data[8])

        let batteryPct = Int(round(Double(bat)/255.0*100))

If we run through packet #2 as an example:


Index: [0] [1] [2] [3] [4] [5] [6] [7] [8]

Packet #2 00 00 00 64 F5 00 FB 00 DD


Indices [4] and [5]: Probe Temperature in Celsius

Indices [6] and [7]: Ambient Temperature in Celsius

Index [8]: Probe Battery


[4][5]: 0xF5, 0x00 --> Little Endian --> 0x00F5 Hex to Decimal --> 245 --> 24.5C (Probe)

[6][7]: 0xFB, 0x00 --> Little Endian --> -0x00FB Hex to Decimal --> 251 --> 25.1C (Ambient)

[8]: 0xDD --> Hex to Decimal --> 221 --> (221/255) * 100% = 86.7%


My App - Step #2: Make a UI and Test


I made this UI the night before our Friendsgiving (November 16) and wanted it to just.. work. I always wanted to know the status of the probe connection, so I put the status in the top right which updated continuously.


Pros:

  • The probe literally never disconnected. I didn't do anything special with my BLE protocol class, but it worked flawlessly

  • My temperature vs time graph continuously updated and was really cool to see throughout the cook

  • Logged my cook time perfectly


Future Improvements:

  • Would love to have a widget or live activity with live data updates, but Apple doesn't let you update widgets or live activities that frequently

  • Estimate time to cook completion based on historical data

  • Alarm that goes off when thermometer hits a specified temperature

  • Graph ambient temperature alongside probe temperature




 
 
 

Recent Posts

See All
SwiftUI Custom Units / Dimensions

Creating custom Units and Dimensions in SwiftUI is super easy. In this tutorial, we use the UnitMomentum as an example for setting these up.

 
 
 
SwiftUI TextField with Numbers Only

SwiftUI allows for numerous different types in TextFields. In this tutorial, we'll explore adding TextFields that only support numbers.

 
 
 

1 Comment


studyllama
Feb 22

So cool! Looks like the turkey turned out well!

Like
bottom of page