Anderes

My toothbrush streams gyroscope data

Since a few month I own an electrical toothbrush from Phillips called Sonicare HX992B. It connects to an mobile App using Bluetooth that tells you how well you brushed your teeth, shows the orientation in real-time, and also notifies you when to change the brush for a new one.

Phillips Sonicare

I was curious how this exactly works. Since the application uses Bluetooth Low Energy (BLE), I used the nRF Connect app for Android to get a list of advertised services.

Screenshot of nRF connect

Now I had a list of 12 BLE services of which only four are standard services described on bluetooth.com. In addition to well-known services like Generic Access, Generic Attribute, Device Information and Battery Service, also eight custom services exist, that were created by the manufacturer. Every one of the first seven service UUIDs starts with 477ea600-a260-11e4-ae37-0002a5d5000 and ends with a number from 1 to 8 with number 3 missing. An additional eight service has a completely different prefix than the others.

UUID Service
0x1800 Generic Access
0x1801 Generic Attribute
0x180a Device Information
0x180f Battery Service
477ea600-a260-11e4-ae37-0002a5d50001 Unknown Service 1
477ea600-a260-11e4-ae37-0002a5d50002 Unknown Service 2
477ea600-a260-11e4-ae37-0002a5d50004 Unknown Service 4
477ea600-a260-11e4-ae37-0002a5d50005 Unknown Service 5
477ea600-a260-11e4-ae37-0002a5d50006 Unknown Service 6
477ea600-a260-11e4-ae37-0002a5d50007 Unknown Service 7
477ea600-a260-11e4-ae37-0002a5d50008 Unknown Service 8
a651fff1-4074-4131-bce9-56d4261bc7b1 Unknown Other Service

By switching the device on and off, changing the strength and mode and also by changing the brush back and forth, I was able to identify some of the characteristics of the six services. By reverse engineering the code of the Android app using jadx I found information about the session storage and many other things.

Service Suffix Actions Description
Handle (1) 4010 RWN Handle State: Off(0), Standby(1), Run(2),Charge(3), Shutdown(4) Validate(6), LightsOut(7)
  4020 R– ?
  4022 R– ?
  4030 R-N ?
  4040 RW- ?
  4050 RW- Current Time (Unix Timestamp)
Brushing (2) 4070 R-N Current session id
  4080 R– Mode (0x0=Clean, 0x2=White+, …)
  4082 RWN State (0=Ready, 1=Active, 2=Resume)
  4090 R-N Active time in seconds
  4091 R-N Mode 2 (0x78=Clean, 0xa0=White+, 0xc8=Gum Health, 0xb4=Deep Clean+)
  40a0 R-N ?
  40b0 R-N Strength (0,1,2)
  40c0 R-N ?
Session Storage (4) 40d0 R-N Last session id (e.g. 0x2c05 = 1324)
  40d2 R– ?
  40d5 RW- Session Type
  40e0 -WN Request session (write session id, write 0x00 to 4110, get notification with data here and at 4100)
  4100 –N ?
  4110 -WN Session Access Control Point
Orientation (5) 4120 RW- ?
  4130 –N Gyroscope Data
  4140 -WN ?
Brush (6) 4210 R– NFC Tag Version
  4220 R– ? INT8
  4230 R-N Brush Serial
  4240 R– Brush Manufacturing Date?
  4250 R– ?
  4254 R– ?
  4260 R– ?
  4270 R– ?
  4280 R– Brush Lifetime (Seconds)
  4290 R– Brush Usage (Seconds)
  42a0 R– Brush Type (None, Adaptive Clean, Adaptive White, Tongue Care, Adaptive Gums)
  42a2 R– ?
  42a4 R– ?
  42a6 RWN ?
  42b0 R– NFC Payload (always phillips.com)
  42c0 R– ? INT16
7 4310 R– ? INT16
  4320 R– ? INT16
  4330 R– ?
  4360 R– ? (Only uses second byte)
8 4410 RW- ? INT16
  4420 RW- ? INT32

According to the application sources, the last unknown service is called “Moonshine Streaming” and is used to update the firmware. I agreed with myself to not touch this one, since I could damage the firmware and would need to brush my teeth by hand again.

Since I could see a very course orientation displayed in the app during a cleaning session, I expected orientation data somewhere in the characteristics. I found them in the service 5. Once you enable notifications for the 4130 service, you get a data stream to byte arrays. It turns out, these are indeed the values of the gyroscope.

Gyroscrope

I created a python library to interface with the toothbrush in Python. Feel free to experiment, find the meaning of the missing services and submit pull requests: https://github.com/joushx/python-sonicare