+++
date = "2017-01-15T19:31:00+08:00"
title = "OpenPrepPad"
tags = [ "hardware", "bluetooth" ]

+++

Smart electronics and IoT (Internet of Things) are all the rage these days. You
have a lot of companies sprout up trying to make the next big thing, which also
leads to a lot of failures big and small. Pebble, the maker of my smartwatch,
got bought out by Fitbit recently. This left watch owners without any official
support, but thankfully, community members [stepped up][1] to continue
maintaining it.

Another casualty of the IoT boom was the [Orange Chef][2] [Prep Pad][3]. It's a
bluetooth connected weighing scale to make it easy to track your calories and
carb/fat/protein intake. My dad bought it last year only to find out that the
app was incredibly buggy. The search function doesn't work which makes the whole
thing practically useless. I also found out later that you can't even download
the app to use the scale anymore.

**Note** I just found out as I was writing this post that it *may* get supported
by [another company][4].

So the app is useless, but at least you can use it as a scale, right?

![Prep Pad](/galleries/openpreppad/preppad.jpg)

Nope. The device has no display whatsoever. The only controls on it are the
on/off button and a green LED that isn't even that useful at telling you whether
it's on or not. At this point, it's just a giant paperweight.

## Reverse Engineering

Since I essentially had nothing to lose, I tried poking at the thing to figure
out how it works. I didn't really have experience with bluetooth besides trying
to get my bluetooth mouse connected on Linux. The main thing I used then was
`bluetoothctl` which is essentially a CLI for managing bluetooth devices so I
started there.

I started up `bluetoothctl` and turned on the Prep Pad. And it showed up!

```text
[bluetooth]# power on
[CHG] Controller ... Class: 0x00010c
Changing power on succeeded
[CHG] Controller ... Powered: yes
[bluetooth]# scan on
Discovery started
[CHG] Device 1C:BA:8C:21:7C:BB RSSI: -51
[CHG] Device 1C:BA:8C:21:7C:BB Name: CHSLEEV_00
[CHG] Device 1C:BA:8C:21:7C:BB Alias: CHSLEEV_00
```

I then connected to it, which was surprisingly easy.

```text
[bluetooth]# connect 1C:BA:8C:21:7C:BB
Attempting to connect to 1C:BA:8C:21:7C:BB
[CHG] Device 1C:BA:8C:21:7C:BB Connected: yes
[CHG] Device 1C:BA:8C:21:7C:BB Name: CH BTScale_00
[CHG] Device 1C:BA:8C:21:7C:BB Alias: CH BTScale_00
```

Now normally, when you turn the device on, the green light flashes occasionally.
Once I connected to it, the green light stayed on permanently. Clearly, I was
making progress. A lot of services were also discovered but I had no idea what
those things were at that point.

After a lot of poking around, I could check the general device information. You
could get the hardware, software and firmware version. There's also the device
serial number which was nowhere on the actual physical device.

```text
[CHSLEEV_00]# select-attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0010/char0017
[CH BTScale_00:/service0010/char0017]# attribute-info
Characteristic - Firmware Revision String
	UUID: 00002a26-0000-1000-8000-00805f9b34fb
	Service: /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0010
	Value: 0x31
	Value: 0x2e
	Value: 0x31
	Value: 0x33
	Value: 0x41
	Value: 0x00
	Flags: read
[CH BTScale_00:/service0010/char0017]# read
Attempting to read /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0010/char0017
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0010/char0017 Value: 0x31
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0010/char0017 Value: 0x2e
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0010/char0017 Value: 0x31
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0010/char0017 Value: 0x33
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0010/char0017 Value: 0x41
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0010/char0017 Value: 0x00
  31 2e 31 33 41 00                                1.13A.
[CH BTScale_00:/service0010/char0017]#
```

There was also a service which contained Accel Enable, Accel Range, Accel
X-Coordinate, Accel Y-Coordinate, and Accel Z-Coordinate. I guess it stands for
accelerometer, which is probably what it uses to weigh things.

```text
[CHSLEEV_00]# select-attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char0024/desc0026
[CH BTScale_00:/service0023/char0024/desc0026]# read
Attempting to read /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char0024/desc0026
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char0024/desc0026 Value: 0x41
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char0024/desc0026 Value: 0x63
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char0024/desc0026 Value: 0x63
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char0024/desc0026 Value: 0x65
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char0024/desc0026 Value: 0x6c
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char0024/desc0026 Value: 0x20
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char0024/desc0026 Value: 0x45
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char0024/desc0026 Value: 0x6e
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char0024/desc0026 Value: 0x61
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char0024/desc0026 Value: 0x62
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char0024/desc0026 Value: 0x6c
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char0024/desc0026 Value: 0x65
  41 63 63 65 6c 20 45 6e 61 62 6c 65              Accel Enable
```

I couldn't read from any of the Accel Coordinates. It kept saying permission
denied. I could however, notify on them. But that didn't yield anything as well.
What I *could* read was Accel Enable, which was set to 00. I guess that means it
was off. After writing 01 to Accel Enable, I found I could get values out of
Accel X-Coordinate! Also, the green LED which was permanently on turned off.

```text
[CHSLEEV_00]# select-attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char0024
[CH BTScale_00:/service0023/char0024]# write 01
Attempting to write /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char0024
[CH BTScale_00:/service0023/char0024]# select-attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char002a
[CH BTScale_00:/service0023/char002a]# notify on
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char002a Notifying: yes
Notify started
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char002a Value: 0x5b
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char002a Value: 0xa3
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char002a Value: 0x02
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char002a Value: 0x00
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char002a Value: 0x55
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char002a Value: 0xa3
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char002a Value: 0x02
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char002a Value: 0x00
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char002a Value: 0x59
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char002a Value: 0xa3
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char002a Value: 0x02
[CHG] Attribute /org/bluez/hci0/dev_1C_BA_8C_21_7C_BB/service0023/char002a Value: 0x00
```

I tried pressing the scale down a few times, and the values changed accordingly.
Now, I just had to figure out how to convert the values into grams. It looked
like the values were 32-bit integers sent as 4 bytes. In the above example it
would be `0x0002a35b`, `0x0002a355`, `0x0002a359` or 172891, 172855, 172899. The
values also decrease as you exert more effort on the scale. So assuming you take
the initial value as *tare*, you simply subtract any succeeding value from that
*tare* and you get the "weight".

The values I got didn't seem to be in grams though. After weighing some things
on an actual scale and comparing the values I got, I found I can just divide the
values by 14 and get something in grams. That 14 is entirely a magic number
though and I have no idea whether other Prep Pad's would have the same constant.

## OpenPrepPad

With all that figured out, I went ahead and made a [simple CLI application][5]
to interface with the Prep Pad. Ironically, node was the simplest thing I found
that had [nice bluetooth library support][6] so that's what I wrote it in. I
also added most of the technical details in the README for that as well.

While this is all well and cool, I doubt the intersection of Linux users and
people who <s>got ripped off</s> bought the Prep Pad is anyone besides me. In
light of that, I'm in the process of making a React Native version of the app,
but that's still a work in progress. Who knows, if the new owners of Prep Pad
are good, I might not even need to finish it.

[1]: http://rebble.io/
[2]: http://theorangechef.com/
[3]: https://www.amazon.com/Orange-Chef-Smart-Scale-Silver/dp/B00KFW8L90
[4]: http://www.prnewswire.com/news-releases/perfect-company-acquires-orange-chefs-prep-pad-related-ip-continues-momentum-in-the-connected-kitchen-300383178.html
[5]: https://github.com/thatsmydoing/openpreppad
[6]: https://github.com/sandeepmistry/noble
