The Wireless Protocol of a Sports Wrist Watch (Part 3)

– Computer, scan all the wireless codes!
– Scanning…, scanning complete.

– Computer, read the scanning summary.
– Tested: 8192 codes. Decoded as valid: 213 codes. Decoded range: from 30 to 239.

– Computer, why are there more valid codes than the range allows?
– There are 3 duplicated codes.

– Computer, list duplicates.
– Listing…

Wireless code:        Should display:     Displays as:
======================================================
1111000000101100110   28                  92
1111000100101100110   92                  92
1111000000101101010   29                  93
1111000100101101010   93                  93
1111000000101111100   31                  95
1111000100101111100   95                  95

Well, it was not exactly like that, but I love the idea: Let a machine do the work for you.


OK, but how it actually was?

From where we left last time, we already had a computer controlled radio Tx. To test all the possible 8192 codes, we can write a script to Tx all the codes one by one, while looking at the number displayed on the wrist watch.

For a solid radio connection, it would be nice to send first a known working code and confirm the displayed number is as expected, then send a known invalid code and confirm the display shows invalid.

Then, send the code we want to test and read the display.

Now, save the results. Job done.


For a computer, the most difficult part is to read the display of the wrist watch, so a compromise was made: No optical recognition of the displayed number, recognize just if the heart symbol is blinking on the display.

If blinking, then we have a valid code, so take a snapshot from the WebCam and save it on the disk. We will look later at all the taken pictures. If not blinking, then the code is invalid, go to the next one.

 


 

Data flow setup:

Python script that parses all the 8192 codes ->
Arduino radio transmitter ->
110KHz radio waves ->
Wrist watch radio receiver/decoder ->
Wrist watch displayed number ->
WebCam streaming the wrist watch display ->
OpenCV video stream preprocessing/filtering ->
OpenCV blinking recognition ->

Snapshot save and text logging of the results.


Hardware setup:


OpenCV and computer vision:

Never did this before, so I went head first. After watching a few OpenCV YouTube tutorials from the sentdex channel – thank you sentdex – it was the time to write some OpenCV based code:

https://github.com/RoGeorge/OpenCV_Controlled_TX_Emulator

# Usage:
#   - plug the USB camera and the Arduino UNO programmed with 'Crivit_ChestBelt_TX_Emulator.ino'
#   - set the 'COM_PORT' number taken by the Arduino UNO
#   - if it's missing, create folder 'captures' near 'OpenCV_Controlled_TX_Emulator.py'
#   - 3 windows will open, 1'st is a color live image, 2'nd a black and white, 3'rd with your selection
#   - put the Crivit HRM wrist-watch in front of the USB camera and set it to HRM monitor mode
#   - in the first 2 windows, adjust the sliders for the camera sensitivity and the black and white threshold
#   - in the 2'nd window, select (by mouse dragging) a blinking area from the blinking heart displayed by the watch
#   - tip: a single point selection (a click instead of a drag) works very good
#   - the selected area will be seen live in the 3'rd window
#   - all the bitstreams written in 'captures/input_bitstreams.txt' will be sent one by one to the radio Tx
#   - the script will look in the 3'rd window if the heart symbol displayed by the wrist-watch is blinking
#   - if blinking, a jpg snapshot will be saved in 'captures'
#   - results will be displayed on the command line, then added to the log file 'captures/working_bitstreams.csv'
#   - NOTE: Do not close the live windows. To exit teh script, press the 'ESC' key.
# Installation:
#   pip install numpy
#   pip install pyserial
#
# to install OpenCV (32-bit) download and unpack, then go to folder
#   'opencv\build\python\2.7\x86\'
#   and copy the file 'cv2.pyd' into folder 'C:\Python27\Lib\site-packages'
#
# To check the OpenCV installation, open Python and type
#   >>> import cv2
#   >>> print cv2.__version__
#   3.2.0
#

This is a print screen taken while adjusting the light and the B&W filter threshold:


After a few days of automated testing, puzzling results:

The encoding scheme has a limited display range, between 30 and 239, and 3 duplicated codes.

An example for a duplicated code snapshot:

I don’t know if this is a bug or a feature, and I must admit that I don’t understand how this encoding scheme was designed, but so far it was a lot of fun looking into it.

:o)




The Wireless Protocol of a Sports Wrist Watch (Part 2)

This post is a continuation of the The Wireless Protocol of a Sports Wrist Watch, and it is all about testing the hypothesis made so far.

axodus, K.C. Lee, killy.mxi (AKA Killy) and Asterek have all contributed to reverse engineer the radio protocol, and Killy came up with the following algorithm:

Encoded sequence has variable lenght. There are some stuffed bits for better signal recovery.

Output sequence:
* in1
* in2
* (in1 nor in2) stuffed if (in1 nand in2)
* in3
* in4
* (in3 nor in4) stuffed if (in3 nand in4)
* in5
* in6
* (in5 nor in6) stuffed if (in5 nand in6)
* in7
* in8
* (in7 xnor in8)
* (in7 nand in8)

Speaking simple, stuffing rule works like this:
* if pair of bits is 00, stuff 1;
* if 01 or 10, then stuff 0;
* if 11, don’t stuff anything here.

More details can be found in the Discussions section of https://hackaday.io/project/13142-sniff-the-wireless-data-of-a-sports-wrist-watch

I want to thank you all for reverse engineering the encoding scheme, great job!

 

A computer controlled radio Tx


Now, it’s time to test the hypothesis.

To do this, it would be helpful to be able to transmit any code, valid or invalid, and see how the receiver will react. The Crivit chest belt can’t do that, so we need to build our own radio transmitter. With a carrier frequency of only 110 KHz, it should be easy to digitally synthesize the entire modulated carrier.

A few lines of code later, it proves out that a simple wire connected to a digital output is good enough as a Tx antenna, and an Arduino UNO is fast enough to generate the carrier, modulate it, and in the same time talk to a computer over the serial port:

This will allow us to put on air any combination of 0’s and 1’s that we might want to test.

 

New findings


  • for an invalid code, the wristwatch will keep displaying the last valid number received, but the heart symbol will stop blinking, just like in the case of no signal
  • there is no handshake protocol, so the watch will display any valid code received, even if the chest belt ID is changed. All the following codes were displayed as one hundred:
    S 111100 0101000100011
    S 111010 0101000100011
    S 111001 0101000100011
    S 110011 0101000100011
    
  • the total number of bits can vary, i.e. the following codes are both displayed as a valid one hundred:
     S 110011 0101000100011
    S 1100100 0101000100011
    
  • so far, the encoding scheme found by @killy.mxi can predict valid codes even for numbers that were out of reach for the original chest belt transmitter. The following codes predicted for numbers between 234..239 were displayed as valid:
    1110010010010
    1110010011100
    1110011001100
    1110011010100
    1110011100100
    1110011111000
    

    Still, for predicted codes corresponding to numbers greater than 239, the blinking heart stops. This might be because the receiver was designed to act like that, but this it’s not yet for sure.

Manually typing each code to be tested proves to be useful, but also very time consuming and prone to errors. Since our radio Tx is now able to transmit any codes coming from the serial port, it will allow us to do automated testing. This will be the next step.




 

The Wireless Protocol of a Sports Wrist Watch

The “Crivit Sports” wrist watch HRM (Heart Rate Monitor)


 

MINOLTA DIGITAL CAMERAThis is a wrist watch that can also display the heart rate BPM (Beats Per Minute).

 

MINOLTA DIGITAL CAMERAThe complete specs and the user manual can be found at http://www.lidl-service.com/cps/rde/xchg/SID-648946E8-5F850FDD/lsp/hs.xsl/product.html?id=47039228

 

MINOLTA DIGITAL CAMERAThe heart beats are detected by a chest strap included with the watch.

The chest strap have two conductive rubber pads that senses the electrical signals produced by the heart beats. No contact gel is required for the rubber pads.

The link between the chest strap and the wrist watch is wireless, by an RF (Radio Frequency) signal.

 

Missing features


Being a very cheap item, it does not have any kind of connectivity, it can not log data, and it can not make charts. It would have been way more interesting to be able to see a ECG (ElectroCardioGram) or a BPM chart for the whole day. Could it be possible?

Couldn’t find any online technical documentation about this ‘Crivit Sports’ model.

 

Reverse engineering the RF signal


 

MINOLTA DIGITAL CAMERATo receive the RF, a one loop coil was wrapped around the chest strap, by simply clipping the oscilloscope GND (GrouND) wire alligator to the tip of the probe, and around the chest strap transmitter.

 

20160813-05-DS1104Z_DS1ZA164658712_2016-08-14_04.18.15This is the signal received from the chest belt HRM for a constant heart rate of 100 BPM. The RF carrier is at 110 kHz, 100% AM (Amplitude Modulated) by a digital signal.

By looking at the received signal for various heart rates, it turns out that the BPM rate is computed by the chest wrap by averaging the last few heart beats, then the resulting number is used to modulate the RF signal like this:

  • chest strap monitor’s RF carrier is at 110 kHz, 100% AM modulated
  • a bit of 1 is made by a 3 ms ON (100% RF) followed by a 4.8 ms OFF (0% RF)
  • a bit of 0 is made by a 7.8 ms OFF (0% RF)

The BPM data is constantly sent about 0.5 to 2 times per second. The delay between two data packets is unrelated with the heart beat. The structure of a packet is:

  • one start sync bit with 5ms ON (100% RF) and 4.8ms OFF (0% RF)
  • next 6 bits encode the chest strap ID (IDentification). The ID changes randomly each time the battery is disconnected. The observed IDs were all bigger then 48 (first two bits 11)
  • the remaining 13 bits encodes the BPM.

This stream of data is received and decoded by the wrist watch, which then display one number, the BPM. Observed BPM displayed range was between 30 and 233. The watch displays the BPM no matter what ID the chest belt randomly picked.

 

I tried to look at the carrier waveform corresponding to a couple of different BPM numbers, by doing push-ups in order to accelerate the heart beat. My intention was to understand the correspondence between the last 13 bits sent by the chest strap and the BPM number displayed by the wrist watch.

Counting bits after making push-ups is not easy. I couldn’t figure out the encoding scheme.

 

MINOLTA DIGITAL CAMERAAnother more rigorous testing way was necessary. Instead of using real heart signals, a signal generator (RIGOL DG4102) was used to simulate the electrical signals coming from the heart. By contrast with a real heart, the generator can produce any BPM number with high accuracy.

The output from the signal generator was connected to the two conductive rubber pads of the chest strap. An one wire loop antenna was wrapped around the chest belt, and the received RF signal was displayed on the oscilloscope (RIGOL DS1054Z).

 

20160813-07-DS1104Z_DS1ZA164658712_2016-08-14_04.17.48Also, another signal was generated by the second channel of the generator, and was added as a grid (the magenta signal), to ease the task of counting consecutive bits of zero. Each magenta spike is now pointing to a zero or a one bit. The yellow signal in this capture is:

S  111001  0101000100011

Where ‘S’ is the Sync bit, ‘111001’ is the chest strap ID and ‘0101000100011’ is the BPM. For this particular signal, the wrist watch will display one hundred BPM.

 

By varying the period of the fake heart beat generated signal, all possible heart rates were browsed.

The following table contains the encoding bits sent by the chest strap for each possible BPM number displayed by the wrist watch.

If someone recognizes this encoding scheme, then please let me know, because I still can not figure out the formula between the last 13 RF bits and the displayed BPM number.
(LATER EDIT: Thank you all for the proposed encoding algorithm, it is described and tested in The Wireless Protocol of a Sports Wrist Watch (Part 2))

 

Oscilloscope last 13 pulses (BPM) Number displayed by Wrist Watch Number displayed (binary)
0010101110010 30 00011110
0010101111100 31 00011111
0011000010011 32 00100000
0011000010101 33 00100001
0011000011001 34 00100010
0011000011110 35 00100011
0011000100011 36 00100100
0011000100101 37 00100101
0011000101001 38 00100110
0011000101110 39 00100111
0011001000011 40 00101000
0011001000101 41 00101001
0011001001001 42 00101010
0011001001110 43 00101011
0011001100110 44 00101100
0011001101010 45 00101101
0011001110010 46 00101110
0011001111100 47 00101111
0011100100110 48 00110000
0011100101010 49 00110001
0011100110010 50 00110010
0011100111100 51 00110011
0011101000110 52 00110100
0011101001010 53 00110101
0011101010010 54 00110110
0011101011100 55 00110111
0011110000110 56 00111000
0011110001010 57 00111001
0011110010010 58 00111010
0011110011100 59 00111011
0011111001100 60 00111100
0011111010100 61 00111101
0011111100100 62 00111110
0011111111000 63 00111111
0100010010011 64 01000000
0100010010101 65 01000001
0100010011001 66 01000010
0100010011110 67 01000011
0100010100011 68 01000100
0100010100101 69 01000101
0100010101001 70 01000110
0100010101110 71 01000111
0100011000011 72 01001000
0100011000101 73 01001001
0100011001001 74 01001010
0100011001110 75 01001011
0100011100110 76 01001100
0100011101010 77 01001101
0100011110010 78 01001110
0100011111100 79 01001111
0100100010011 80 01010000
0100100010101 81 01010001
0100100011001 82 01010010
0100100011110 83 01010011
0100100100011 84 01010100
0100100100101 85 01010101
0100100101001 86 01010110
0100100101110 87 01010111
0100101000011 88 01011000
0100101000101 89 01011001
0100101001001 90 01011010
0100101001110 91 01011011
0100101100110 92 01011100
0100101101010 93 01011101
0100101110010 94 01011110
0100101111100 95 01011111
0101000010011 96 01100000
0101000010101 97 01100001
0101000011001 98 01100010
0101000011110 99 01100011
0101000100011 100 01100100
0101000100101 101 01100101
0101000101001 102 01100110
0101000101110 103 01100111
0101001000011 104 01101000
0101001000101 105 01101001
0101001001001 106 01101010
0101001001110 107 01101011
0101001100110 108 01101100
0101001101010 109 01101101
0101001110010 110 01101110
0101001111100 111 01101111
0101100100110 112 01110000
0101100101010 113 01110001
0101100110010 114 01110010
0101100111100 115 01110011
0101101000110 116 01110100
0101101001010 117 01110101
0101101010010 118 01110110
0101101011100 119 01110111
0101110000110 120 01111000
0101110001010 121 01111001
0101110010010 122 01111010
0101110011100 123 01111011
0101111001100 124 01111100
0101111010100 125 01111101
0101111100100 126 01111110
0101111111000 127 01111111
1000010010011 128 10000000
1000010010101 129 10000001
1000010011001 130 10000010
1000010011110 131 10000011
1000010100011 132 10000100
1000010100101 133 10000101
1000010101001 134 10000110
1000010101110 135 10000111
1000011000011 136 10001000
1000011000101 137 10001001
1000011001001 138 10001010
1000011001110 139 10001011
1000011100110 140 10001100
1000011101010 141 10001101
1000011110010 142 10001110
1000011111100 143 10001111
1000100010011 144 10010000
1000100010101 145 10010001
1000100011001 146 10010010
1000100011110 147 10010011
1000100100011 148 10010100
1000100100101 149 10010101
1000100101001 150 10010110
1000100101110 151 10010111
1000101000011 152 10011000
1000101000101 153 10011001
1000101001001 154 10011010
1000101001110 155 10011011
1000101100110 156 10011100
1000101101010 157 10011101
1000101110010 158 10011110
1000101111100 159 10011111
1001000010011 160 10100000
1001000010101 161 10100001
1001000011001 162 10100010
1001000011110 163 10100011
1001000100011 164 10100100
1001000100101 165 10100101
1001000101001 166 10100110
1001000101110 167 10100111
1001001000011 168 10101000
1001001000101 169 10101001
1001001001001 170 10101010
1001001001110 171 10101011
1001001100110 172 10101100
1001001101010 173 10101101
1001001110010 174 10101110
1001001111100 175 10101111
1001100100110 176 10110000
1001100101010 177 10110001
1001100110010 178 10110010
1001100111100 179 10110011
1001101000110 180 10110100
1001101001010 181 10110101
1001101010010 182 10110110
1001101011100 183 10110111
1001110000110 184 10111000
1001110001010 185 10111001
1001110010010 186 10111010
1001110011100 187 10111011
1001111001100 188 10111100
1001111010100 189 10111101
1001111100100 190 10111110
1001111111000 191 10111111
1100100100110 192 11000000
1100100101010 193 11000001
1100100110010 194 11000010
1100100111100 195 11000011
1100101000110 196 11000100
1100101001010 197 11000101
1100101010010 198 11000110
1100101011100 199 11000111
1100110000110 200 11001000
1100110001010 201 11001001
1100110010010 202 11001010
1100110011100 203 11001011
1100111001100 204 11001100
1100111010100 205 11001101
1100111100100 206 11001110
1100111111000 207 11001111
1101000100110 208 11010000
1101000101010 209 11010001
1101000110010 210 11010010
1101000111100 211 11010011
1101001000110 212 11010100
1101001001010 213 11010101
1101001010010 214 11010110
1101001011100 215 11010111
1101010000110 216 11011000
1101010001010 217 11011001
1101010010010 218 11011010
1101010011100 219 11011011
1101011001100 220 11011100
1101011010100 221 11011101
1101011100100 222 11011110
1101011111000 223 11011111
1110000100110 224 11100000
1110000101010 225 11100001
1110000110010 226 11100010
1110000111100 227 11100011
1110001000110 228 11100100
1110001001010 229 11100101
1110001010010 230 11100110
1110010000110 231 11100111
1110001011100 232 11101000
1110010001010 233 11101001