CoderFrank

Reverse engineering a Blood Pressure Monitor’s software

In this blog post, I wanted to write about my adventure of reverse engineering software that accompanied my mother’s blood pressure monitor.

Background

My grandfather passed away this year (April 2017) and mom (technically grandmother — she raised me though) started checking her blood pressure more frequently as of late. During thanksgiving 2017, I had a scare with her blood pressure readings at 196 systolic and I wasn’t sure why it was so high. I called a nurse hotline and didn’t hear back until 2 hours later. It’s 3am as he commanded me to get up and check it again. Well long story short I ended up calling 911. Paramedics arrived and they verified it wasn’t as high. Her blood pressure was at 174 systolic. Her blood pressure was high but her medication had been changed earlier in the week by her doctor. The doctor mentioned it may impact her vitals until her body grew accustomed to the new medication.

We figured it was time to purchase a new blood pressure monitor after the imprecise readings we observed. The new device was accompanied with software to download readings from the device and visualize the data over time.

After realizing software was available an idea was born. The software did as it was designed to download and visualize data. I thought I could ask my father who is currently staying with my mom to export the data and upload to a cloud object storage of some kind. Well, I then thought that wasn’t going to be possible without additional intervention and after a Google search, I discovered the WebUSB API. This web API would allow me to create a website that could communicate with the blood pressure monitor without installing additional software.

You can read about the WebUSB API at developers.google.com.

Next, I needed to know the communication protocol for the device so I could use the WebUSB API to communicate with the device. I first decided to decompile the accompanying software and understand the communication.

Decompilation

I decided to use the Hopper Disassembler to disassemble the accompanying software. Great, I have the at&t assembly and psuedo-code that is provided by Hopper. I have no idea how to continue now. I spent some time trying to read through the result, but it wasn’t very clear. I asked “why not check logs?”, but where? What about a promiscuous mode for USB devices? I Googled, and learned Wireshark does this, woah, but it only worked on Linux and Windows. So not doing that on my Macbook.

After some more time, I thought to try starting the software from my terminal. Huzzah, the software author decided to output detailed information to stdout.

2017-11-29 22:35:13.241 XXX[28423:378061] reading itemNodes count = 40
2017-11-29 22:35:13.241 XXX[28423:378061] new Mutable Array reading for ID 1001
2017-11-29 22:35:13.254 XXX[28423:378061] new Mutable Array reading for ID 1002

I truncated the information and redacted device revealing information.

Next, I searched for output on the information download step and found:

2017-11-29 22:35:42.010 XXX[28423:378061] ok: message panel
getXXXData
[data begin]
2017-11-29 22:35:44.353 XXX[28423:378082] get Data –>  no data

Great! I found getXXXData which is sounded promising. I searched the assembly from the disassembler and found the function definition.

_getBPMCycleData:
00044bd0 push ebp ; CODE XREF=+[XXXModel getXXXData:]+321
00044bd1 mov ebp, esp
00044bd3 push ebx
00044bd4 push edi
00044bd5 push esi
00044bd6 sub esp, 0x35c
00044bdc call _getXXXData+17
00044be1 pop eax ; CODE XREF=_getXXXData+12
00044be2 mov ecx, dword [ebp+0x18]
00044be5 mov edx, dword [ebp+0x14]
00044be8 mov esi, dword [ebp+0x10]
00044beb mov edi, dword [ebp+0xc]
00044bee mov ebx, dword [ebp+8]
00044bf1 mov dword [ebp-0x28c], eax

Searching, I found a _hid_write function call. After a Google search, I found the HID API a C library for communicating with USB devices. hid_write expects a few parameters, the device, a buffer with data, and the length of the data being transmitted.

00044ceb lea edx, dword [ebp-0x110]
00044cf1 xor esi, esi
00044cf3 mov edi, 0x100
00044cf8 mov ebx, edx
00044cfa mov dword [esp], ebx ; argument “b” for method imp___symbol_stub__memset
00044cfd mov dword [esp+4], 0x0 ; argument “c” for method imp___symbol_stub__memset
00044d05 mov dword [esp+8], 0x100 ; argument “len” for method imp___symbol_stub__memset
00044d0d mov dword [ebp-0x2a4], eax
00044d13 mov dword [ebp-0x2a8], ecx
00044d19 mov dword [ebp-0x2ac], edx
00044d1f mov dword [ebp-0x2b0], esi
00044d25 mov dword [ebp-0x2b4], edi
00044d2b call imp___symbol_stub__memset ; memset
00044d30 mov byte [ebp-0x110], 0x14
00044d37 mov byte [ebp-0x10f], 0x12
00044d3e mov byte [ebp-0x10e], 0x16
00044d45 mov byte [ebp-0x10d], 0x18
00044d4c mov byte [ebp-0x10c], 0x22
00044d53 mov eax, dword [ebp-0x230]
00044d59 mov dword [esp], eax ; argument #1 for method _hid_write
00044d5c mov eax, dword [ebp-0x2ac]
00044d62 mov dword [esp+4], eax ; argument #2 for method _hid_write
00044d66 mov dword [esp+8], 0x5 ; argument #3 for method _hid_write
00044d6e call _hid_write ; _hid_write

A look over at the pseudo-code gave me more insight on what is “possibly” going on, but it wasn’t able to produce pseudo-code so I found a similar function name nearby and tried looking at that. I found that before the _hid_write there exists a buffer defined with 5 bytes of data. Obviously, the pseudo-code doesn’t look like an array but I took a guess that given what the function expects as parameters it probably is an array. The values assigned to the array are probably a start sequence.

var_110 = 0x14;
var_10F = 0x12;
var_10E = 0x16;
var_10D = 0x18;
var_10C = 0x22;
eax = _hid_write(var_220, &var_110, 0x5);

This can we be found in the other function definition as well but in assembly.

00044d30 mov byte [ebp-0x110], 0x14
00044d37 mov byte [ebp-0x10f], 0x12
00044d3e mov byte [ebp-0x10e], 0x16
00044d45 mov byte [ebp-0x10d], 0x18
00044d4c mov byte [ebp-0x10c], 0x22

Looks like I’m set, great! So I move on and write some WebUSB API javascript to send the request to the device.

I GOT DATA, but not sure what it means. I split the data into three cases to attempt decoding without reading too much assembly.

Case 0: No measurement exists on the device.
Result: No data is returned.

Case 1: 1 measurement exists on the device.
Measurement 1: 164 SYS. | 106 DIA. | 69 PUL.  | 11-30-2017 | 9:33 PM
Result:
Sample 1:

0xf3,0x6,0x30,0x30,0x34,0x39,0x39,0x31,0xf7,0x30,
0x31,0x30,0x30,0x30,0x31,0x33,0xf7,0x31,0x33,0x30,
0x33,0x30,0x33,0x32,0xf7,0x30,0x30,0x30,0x30,0x30,
0x30,0x30,0xf7,0x30,0x30,0x30,0x30,0x30,0x30,0x30,
0xf2,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0xf3,0x31,
0x37,0x31,0x30,0x30,0x30,0x30,0xf7,0x31,0x33,0x30,
0x32,0x31,0x32,0x36

Sample 2:

0xf3,0x6,0x30,0x30,0x31,0x41,0x38,0x41,0xf7,0x30,0x31,
0x30,0x30,0x30,0x31,0x33,0xf7,0x31,0x33,0x30,0x33,0x30,
0x33,0x32,0xf7,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0xf7,
0x30,0x30,0x30,0x30,0x30,0x30,0x30,0xf2,0x30,0x30,0x30,
0x30,0x30,0x30,0x30,0xf7,0x31,0x37,0x31,0x31,0x33,0x30,
0x32,0xf7,0x31,0x32,0x36,0x30,0x30,0x30,0x30

Sample 3:

0xf3,0x6,0x30,0x30,0x34,0x39,0x39,0x31,
0xf7,0x30,0x31,0x30,0x30,0x30,0x31,0x33,
0xf7,0x31,0x33,0x30,0x33,0x30,0x33,0x32,
0xf7,0x30,0x30,0x30,0x30,0x30,0x30,0x30,
0xf7,0x30,0x30,0x30,0x30,0x30,0x30,0x30,
0xf4,0x30,0x30,0x31,0x37,0x30,0x30,0x30,
0xf7,0x31,0x31,0x33,0x30,0x32,0x31,0x32,
0xf7,0x36,0x30,0x30,0x30,0x30,0x30,0x30

0x30,0x30,0x30,0x31,0x30,0x30,0x30,0x31,
0x33,0x31,0x33,0x30,0x33,0x30,0x33,0x32,
0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,
0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,
0x31,0x37,0x31,0x31,0x33,0x30,0x32,0x31,0x32,
0x36,
0x30,0x30,0x30,0x30,0x30,0x30

Case 2: 2 measurements exist on the device.
Measurement 1: 164 SYS. | 106 DIA. | 69 PUL.  | 11-30-2017 | 9:33 PM
Measurement 2: 153 SYS. | 98 DIA. | 74 PUL. | 11-30-2017 | 9:35 PM
Result:
Sample 1:

0xf3,0x6,0x30,0x30,0x0,0x0,0xff,0x0,
0xf7,0x30,0x32,0x30,0x30,0x30,0x31,0x33,
0xf7,0x31,0x33,0x30,0x33,0x30,0x33,0x32,
0xf7,0x30,0x30,0x30,0x30,0x30,0x30,0x30,
0xf7,0x30,0x30,0x30,0x30,0x30,0x30,0x30,
0xf6,0x30,0x30,0x31,0x37,0x31,0x31,0x30,
0xf7,0x33,0x30,0x32,0x31,0x32,0x36,0x30,
0xf7,0x30,0x30,0x30,0x30,0x30,0x30,0x34,
0xf7,0x35,0x31,0x41,0x38,0x41,0x34,0x30,
0xf7,0x34,0x35,0x31,0x41,0x38,0x41,0x34,
0xf5,0x31,0x37,0x31,0x31,0x33,0x41,0x34,
0xf7,0x30,0x32,0x31,0x33,0x35,0x30,0x30,
0xf7,0x30,0x30,0x30,0x30,0x30,0x34,0x41,
0xf7,0x31,0x38,0x38,0x39,0x39,0x30,0x34,
0xf7,0x41,0x31,0x38,0x38,0x39,0x39,0x32,
0xf1,0x32,0x31,0x38,0x38,0x39,0x39,0x32

Extracted Data:
0x30,0x30,0x30,0x30,0x32,0x30,0x30,0x30,0x31,0x33,
0x31,0x33,0x30,0x33,0x30,0x33,0x32,
0x30,0x30,0x30,0x30,0x30,0x30,0x30,
0x30,0x30,0x31,0x37,0x31,0x31
0x33,0x30,0x32,0x31,0x32,0x36,0x30,
0x30,0x30,0x30,0x30,0x30,0x30,0x34,
0x35,0x31,0x41,0x38,0x41,0x34,0x30,
0x34,0x35,0x31,0x41,0x38,0x41,0x34,
0x31,0x37,0x31,0x31,0x33
0x30,0x32,0x31,0x33,0x35,0x30,0x30,
0x30,0x30,0x30,0x30,0x30,0x34,0x41,
0x31,0x38,0x38,0x39,0x39,0x30,0x34,
0x41,0x31,0x38,0x38,0x39,0x39,0x32,
0x32

Sample 2:

0xf3,0x6,0x30,0x30,0x38,0x39,0x39,0x32,0xf7,0x30,0x32,
0x30,0x30,0x30,0x31,0x33,0xf7,0x31,0x33,0x30,0x33,0x30,
0x33,0x32,0xf7,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0xf7,
0x30,0x30,0x30,0x30,0x30,0x30,0x30,0xf2,0x30,0x30,0x30,
0x30,0x30,0x30,0x30,0xf1,0x31,0x30,0x30,0x30,0x30,0x30,
0x30,0xf7,0x37,0x31,0x31,0x33,0x30,0x32,0x31,0xf7,0x32,0x36,
0x30,0x30,0x30,0x30,0x30,0xf7,0x30,0x30,0x34,0x35,0x31,
0x41,0x38,0xf7,0x41,0x34,0x30,0x34,0x35,0x31,0x41,0xf3,0x38,
0x41,0x34,0x34,0x35,0x31,0x41,0xf2,0x31,0x37,0x34,0x34,0x35,
0x31,0x41,0xf7,0x31,0x31,0x33,0x30,0x32,0x31,0x33,0xf7,0x35,
0x30,0x30,0x30,0x30,0x30,0x30,0xf7,0x30,0x34,0x41,0x31,0x38,
0x38,0x39

Sample 3:

0xf3,0x6,0x30,0x30,0x32,0x31,0x38,0x38,0xf7,0x30,0x32,
0x30,0x30,0x30,0x31,0x33,0xf7,0x31,0x33,0x30,0x33,0x30,
0x33,0x32,0xf7,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0xf7,
0x30,0x30,0x30,0x30,0x30,0x30,0x30,0xf2,0x30,0x30,0x30,
0x30,0x30,0x30,0x30,0xf5,0x31,0x37,0x31,0x31,0x33,0x30,
0x30,0xf7,0x30,0x32,0x31,0x32,0x36,0x30,0x30,0xf7,0x30,
0x30,0x30,0x30,0x30,0x34,0x35,0xf7,0x31,0x41,0x38,0x41,
0x34,0x30,0x34,0xf6,0x35,0x31,0x41,0x38,0x41,0x34,0x34,
0xf7,0x31,0x37,0x31,0x31,0x33,0x30,0x32,0xf7,0x31,0x33,
0x35,0x30,0x30,0x30,0x30,0xf7,0x30,0x30,0x30,0x34,0x41,
0x31,0x38,0xf7,0x38,0x39,0x39,0x30,0x34,0x41,0x31,0xf6,
0x38,0x38,0x39,0x39,0x32,0x32,0x31

 

Written payload — 0x26 (getDateTime)

Sample 1:

0xf3,0x6,0x31,0x31,0x30,0x30,0xd,0xa,0xf7,0x33,0x30,0x31,
0x37,0x32,0x30,0x32,0xf7,0x31,0x35,0x35,0x35,0x34,0x30,
0x30,0xf7,0x30,0x20,0x30,0x31,0x31,0x38,0x30,0xf7,0x33,
0x30,0x31,0x30,0x30,0x30,0x37,0xf7,0x30,0x30,0x30,0x30,
0x30,0x30,0x30,0xf7,0x30,0x30,0x30,0x30,0x30,0x46,0x46,
0xf7,0x33,0x32,0x39,0x36,0x46,0x30,0x31

Sample 2:

0xf3,0x6,0x31,0x31,0x30,0x30,0x39,0x35,0xf7,0x33,0x30,0x31,
0x37,0x32,0x30,0x32,0xf7,0x31,0x35,0x37,0x30,0x32,0x30,0x30,
0xf7,0x30,0x20,0x30,0x31,0x31,0x38,0x30,0xf7,0x33,0x30,0x31,
0x30,0x30,0x30,0x37,0xf7,0x30,0x30,0x30,0x30,0x30,0x30,0x30,
0xf7,0x30,0x30,0x30,0x30,0x30,0x46,0x46,0xf7,0x33,0x32,0x39,
0x36,0x46,0x30,0x31

Sample 3:

0xf3,0x6,0x31,0x31,0x30,0x30,0x39,0x30,0xf7,0x33,0x30,0x31,
0x37,0x32,0x30,0x32,0xf7,0x31,0x35,0x37,0x32,0x39,0x30,0x30,
0xf7,0x30,0x20,0x30,0x31,0x31,0x38,0x30,0xf7,0x33,0x30,0x31,
0x30,0x30,0x30,0x37,0xf7,0x30,0x30,0x30,0x30,0x30,0x30,0x30,
0xf7,0x30,0x30,0x30,0x30,0x30,0x46,0x46,0xf7,0x33,0x32,0x39,
0x36,0x46,0x30,0x31

Data retrieval requests function call:
_getBPMCycleData: – 0x14, 0x12, 0x16, 0x18, 0x22
_getBPMCycleDataCount: – 0x14, 0x12, 0x16, 0x18, 0x22
_getBPMID: – 0x14, 0x12, 0x16, 0x18, 0x24
_getBPMDate: – 0x14, 0x12, 0x16, 0x18, 0x26
_getBPMVersion: – 0x14, 0x12, 0x16, 0x18, 0x3e

Data transmission function call:
_sendID: – 0x14, 0x12, 0x16, 0x18, 0x23
_setBPMDate: – 0x14, 0x12, 0x16, 0x18, 0x27

How do I know that the data is accurate? The disassembled code has a checksum and I need to port over to Javascript below is the function:

// stack[2049] = arg1;
// stack[2048] = arg0;
// var_14 = arg0;
// var_18 = arg1;
// var_1C = 0x0;
// var_20 = 0x0;
// var_2C = eax;
// if (var_18 < 0x2) {
// eax = var_2C;
// eax = printf(“checkSum error : data length error\n”);
// var_D = 0x0;
// var_30 = eax;
// }
// else {
// for (var_20 = 0x0; var_20 < var_18 – 0x2; var_20 = var_20 + 0x1) {
// var_1C = (*(int8_t *)(var_14 + var_20) & 0xff) + var_1C;
// }
// var_1C = var_1C & 0xffff;
// if (var_1C <= 0xffff) {
// edx = var_2C;
// var_34 = 0x0;
// var_38 = 0x5;
// var_3C = __sprintf_chk(&var_25, 0x0, 0x5, “%04X”, var_1C);
// var_41 = LOBYTE(0x0);
// if (sign_extend_32(var_23) == (*(int8_t *)(var_14 + (var_18 – 0x2)) & 0xff)) {
// var_41 = LOBYTE(sign_extend_32(var_22) == (*(int8_t *)(var_14 + (var_18 – 0x1)) & 0xff) ? 0x1 : 0x0);
// }
// var_26 = LOBYTE(LOBYTE(LOBYTE(var_41) & 0x1) & 0xff);
// if (var_26 == 0x0) {
// eax = var_2C;
// var_48 = printf(“checkSum error res2:%c res1:%c daat2:%c data1:%c\n”, sign_extend_32(var_23), *(int8_t *)(var_14 + (var_18 – 0x2)) & 0xff, sign_extend_32(var_22), *(int8_t *)(var_14 + (var_18 – 0x1)) & 0xff);
// }
// var_D = LOBYTE(var_26);
// }
// else {
// eax = var_2C;
// eax = printf(“checkSum error exceed range\n”);
// var_D = 0x0;
// var_40 = eax;
// }
// }
// eax = var_D & 0xff;
// esi = stack[2044];
// edi = stack[2045];
// ebx = stack[2046];
// esp = esp + 0x6c;
// ebp = stack[2047];
// return eax;

Payload decoding

Well, long story short I wasn’t successful at reversing the checksum. So I tried a different strategy by using a debugger to attach the process and view breakpoints before and after a call. I focused on the smallest piece, a patient ID.

Decoding the Patient ID.

Request: 0x14, 0x12, 0x16, 0x18, 0x24
Debugger Response: 33 31 33 30 33 30 33 32 30 30 30 30 30 30 30 30 30 30 30 30 30 30 00 30
Debugger Output: 31 30 30 32 00 00 00 00 (1002 in ascii) expected output after stepping through with multiple breakpoints.

Expected length matters for Patient ID: Used 24 bytes and received two chunks. Can get away with 48 bytes but didn’t want to spend too much time trying to figure that. Trying to figure out what section actually matters in the response for the Patient ID.

Raw-response: 

,0xf3,0x6,0x33,0x31,0x30,0x30,0xd,0xa,0xf7,0x33,0x30,0x33,
0x30,0x33,0x32,0x30,0xf7,0x30,0x30,0x30,0x30,0x30,0x30,
0x30,0xf7,0x30,0x30,0x30,0x30,0x30,0x30,0x2c,0xf7,0x30,
0x35,0x2e,0x30,0x30,0xd,0xa,0xf2,0x36,0x35,0x2e,0x30,0x30,
0xd,0xa

Split to align with blood pressure monitor software data found in the debugger:

??>>,0xf3,0x6,
IN software data>>0x33,0x31.
??>>,0x30,0x30,0xd,0xa,0xf7,
IN software data>>0x33,0x30,0x33,0x30,0x33,0x32,0x30,
??>>0xf7,
IN software data>>0x30,0x30,0x30,0x30,0x30,0x30,0x30, << I’m unsure if this belongs in the buffer or a following section?
??>>0xf7,0x30,0x30,0x30,0x30,0x30,0x30,0x2c,0xf7,0x30,0x35,0x2e,
??>>0x30,0x30,0xd,0xa,0xf2,0x36,0x35,0x2e,0x30,0x30,0xd,0xa

I noticed after writing the parsed section above using the raw response that the ID is determined by the following character after a 0x30. For example:

33 31 33 30 33 30 33 32 30 30 30 30 30 30 30 30 30 30 30 30 30 30 00 30

Turns into:

31 30 30 32 == 1002 (ignoring all the 30’s)

Trying a different ID 100002

Sample 1:

0xf3 0x6 0x33 0x31 0x30 0x30 0x39 0x46
0xf7 0x33 0x30 0x33 0x30 0x33 0x30 0x33
0xf7 0x30 0x33 0x32 0x30 0x30 0x30 0x30
0xf7 0x30 0x30 0x30 0x30 0x30 0x30 0x2c
0xf7 0x30 0x35 0x2e 0x30 0x30 0xd 0xa
0xf2 0x36 0x42 0x2e 0x30 0x30 0xd 0xa

Sample 2:

0xf3 0x6 0x33 0x31 0x30 0x30 0x39 0x39
0xf7 0x33 0x30 0x33 0x30 0x33 0x30 0x33
0xf7 0x30 0x33 0x32 0x30 0x30 0x30 0x30
0xf7 0x30 0x30 0x30 0x30 0x30 0x30 0x2c
0xf7 0x30 0x35 0x2e 0x30 0x30 0xd 0xa
0xf2 0x36 0x42 0x2e 0x30 0x30 0xd 0xa

Trying to figure out the message decoding procedure for Patient ID:

0x33 0x31 0x33 0x30 0x33 0x30 0x33 0x30
0x33 0x30 0x33 0x32 0x30 0x30 0x30 0x30
0x30 0x30 0x30 0x30 0x30 0x30 0x2c 0x30
0x35 0x2e 0x30 0x30 0xd 0x0a 0x36 0x42

My Version based on the assumption that 0xf7 determines the length of real bytes: 0xf7 -> 0x7 bytes

0x6  <- pesky byte not sure what this is actually for? maybe states the beginning of a new message? It does.
0x33 0x31 0x33 0x30 0x33 0x30 0x33 0x30
0x33 0x30 0x33 0x32 0x30 0x30 0x30 0x30
0x30 0x30 0x30 0x30 0x30 0x30 0x2c  0x30
0x35 0x2e 0x30 0x30 0xd 0xa 0x36 0x42

0x33 0x31 0x33 0x30 0x33 0x30 0x33 0x30
0x33 0x30 0x33 0x32 0x30 0x30 0x30 0x30
0x30 0x30 0x30 0x30 0x30 0x30 0x2c 0x30
0x35 0x2e 0x30 0x30 0xd 0xa 0x36 0x42

The device transmits data in 8-byte chunks and I can decode the data in the 8-byte chunks by using the first byte. The first byte determines the length of actual payload data in the next 7 bytes.

Next device data:

115 Sys, 73 DIA, 125 PUL | 12-03 7:39PM
ByteLength: 66

0x30 0x30 0x30 0x31 0x30 0x30 0x30 0x31
0x33 0x31 0x33 0x30 0x33 0x30 0x33 0x30
0x33 0x30 0x33 0x32 0x30 0x30 0x30 0x30
0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30
0x31 0x37 0x31 0x32 0x30 0x33 0x31 0x39
0x33 0x39 0x0 0x30 0x30 0x30 0x30 0x30
0x30 0x37 0x44 0x31 0x32 0x34 0x37 0x33
0x30 0x37 0x44 0x31 0x32 0x34 0x37 0x33
0x36 0x33

0x37 0x44 0x31 0x32 0x34
0x37 0x33 0x30
0x37 0x44 0x31 0x32 0x34
0x37 0x33 0x36 0x33

2 Measurements:

Byte Length: 98

0x30 0x30 0x30 0x32 0x30 0x30 0x30 0x31
0x33 0x31 0x33 0x30 0x33 0x30 0x33 0x30
0x33 0x30 0x33 0x32 0x30 0x30 0x30 0x30
0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30
0x31 0x37 0x31 0x32 0x30 0x33 0x31 0x39
0x33 0x39 0x0 0x30 0x30 0x30 0x30 0x30
0x30 0x37 0x44 0x31 0x32 0x34 0x37 0x33
0x30 0x37 0x44 0x31 0x32 0x34 0x37 0x33
0x31 0x37 0x31 0x32 0x30 0x35 0x30 0x38
0x31 0x31 0x30 0x30 0x30 0x30 0x30 0x30
0x30 0x34 0x45 0x31 0x41 0x30 0x38 0x46
0x30 0x34 0x45 0x31 0x41 0x30 0x38 0x46
0x31 0x30

3 Measurements:

Byte Length: 130

0x30 0x30 0x30 0x33 0x30 0x30 0x30 0x31
0x33 0x31 0x33 0x30 0x33 0x30 0x33 0x30
0x33 0x30 0x33 0x32 0x30 0x30 0x30 0x30
0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30
0x31 0x37 0x31 0x32 0x30 0x33 0x31 0x39
0x33 0x39 0x0 0x30 0x30 0x30 0x30 0x30
0x30 0x37 0x44 0x31 0x32 0x34 0x37 0x33
0x30 0x37 0x44 0x31 0x32 0x34 0x37 0x33
0x31 0x37 0x31 0x32 0x30 0x35 0x30 0x38
0x31 0x31 0x30 0x30 0x30 0x30 0x30 0x30
0x30 0x34 0x45 0x31 0x41 0x30 0x38 0x46
0x30 0x34 0x45 0x31 0x41 0x30 0x38 0x46
0x31 0x37 0x31 0x32 0x30 0x35 0x30 0x38
0x31 0x38 0x30 0x30 0x30 0x30 0x30 0x30
0x30 0x34 0x34 0x31 0x37 0x43 0x39 0x41
0x30 0x34 0x34 0x31 0x37 0x43 0x39 0x41
0x41 0x43

Byte Length: 162

0x30 0x30 0x30 0x34 0x30 0x30 0x30 0x31
0x33 0x31 0x33 0x30 0x33 0x30 0x33 0x30
0x33 0x30 0x33 0x32 0x30 0x30 0x30 0x30
0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30
0x31 0x37 0x31 0x32 0x30 0x33 0x31 0x39
0x33 0x39 0x0 0x30 0x30 0x30 0x30 0x30
0x30 0x37 0x44 0x31 0x32 0x34 0x37 0x33
0x30 0x37 0x44 0x31 0x32 0x34 0x37 0x33
0x31 0x37 0x31 0x32 0x30 0x35 0x30 0x38
0x31 0x31 0x30 0x30 0x30 0x30 0x30 0x30
0x30 0x34 0x45 0x31 0x41 0x30 0x38 0x46
0x30 0x34 0x45 0x31 0x41 0x30 0x38 0x46
0x31 0x37 0x31 0x32 0x30 0x35 0x30 0x38
0x31 0x38 0x30 0x30 0x30 0x30 0x30 0x30
0x30 0x34 0x34 0x31 0x37 0x43 0x39 0x41
0x30 0x34 0x34 0x31 0x37 0x43 0x39 0x41
0x31 0x37 0x31 0x32 0x30 0x35 0x30 0x38
0x32 0x30 0x30 0x30 0x30 0x30 0x30 0x30
0x30 0x34 0x36 0x31 0x37 0x34 0x39 0x42
0x30 0x34 0x36 0x31 0x37 0x34 0x39 0x42
0x32 0x39

 

After trying to decode data from the CycleData payload, I accidentally found the expected information. Here’s an example:

7D 1247307D12473
125 Pulse |73 Diastolic |115 Systolic|
0
4E 1A0 8F 04E1A08F
78 Pulse | 104 Diastolic | 143 Systolic |
0
44 17C 9A 0 4417C9A
68 Pulse |95 Diastolic |160 Systolic
0
46 174 9B 0461749B29
70 Pulse | 93 Diastolic | 155 Systolic |

Success

Success reverse engineering the bpm.

 

I was successful in retrieving data from the Blood Pressure Monitor using the Web USB API and a lot of time with trial and error. The project code can be found on my Github.

I work @Google

 

Hi,

I haven’t posted in a little while, but I now work at Google. I’m still trying to make time for Scribble during my free time. I’ll attempt to post more often!

Thanks for Reading,
Frank N.

Disclaimer, Opinions expressed are solely my own and do not express the views or opinions of my employer.

Average Hop Count of a Mesh Network

Hi,

I haven’t posted in a while, and I thought I’d update my blog with a fun problem I worked on for my Architecture II course. I present my solution for various reasons, but mainly academic. The problem was to prove the average hop count of a 2 dimensional mesh network is equal to Latex formula, where Latex formula is the width and height of the mesh network and is considered even.

In order to keep this simple I added an image of a Latex formula mesh network. Using Manhattan distance we can determine the distance from a specified node to all other nodes in the network.

Screen Shot 2015-11-23 at 4.56.29 PM

An assumption is made to ignore the node used to compute the total hop count. The total hop count per row can be computed by adding the hop count on both sides of the specified node. Using mathematical notation the following function, Latex formula, is used to compute the total hop count of a row relative to a node.
Latex formula
Next we consider the rows above and below the node. As the image suggests, rows above and below is the total relative rows multiplied by k and added to Latex formula. This is expressed as the function Latex formula.

Latex formula

Adding Latex formula and Latex formula together will provide us with the TotalHopCount for specified node. Now we must average over all possible node pairs, Latex formula, and as assumed we ignore hop counts between a node and itself, Latex formula, therefore the total pairs we average over is expressed as Latex formula. Using Latex formula we compute the average hop count of a 2 dimensional network with an even k.

Latex formula
derivation of Latex formula is omitted due to length.

Latex formula
Latex formula
Latex formula
Latex formula
Latex formula Latex formula

My Programmer and Life Mantra

  1. Make sure whatever the tangible idea is, fits like a puzzle piece.
  2. Don’t complicate the idea with creating simply for yourself.
  3. Build it for free.
  4. Make it easily attainable.
  5. Allow transparency.
  6. Don’t extreme yourself to either side of a situation…Find a middle path.
  7. Explain the complexity of a problem with the conciseness of a children’s book.
  8. Find a new path to the answer and manipulate the problem to find it.
  9. Anything is possible, and nothing is 100%.
  10. Anything is breakable, and can also be fixed.
  11. Be concise.
  12. Be strong.
  13. Be real.
  14. Life is just but a journey, and we are a blip through the universe. Like the a shooting star. We last for a second, but we shine brightly through the night. We are beautiful.
  15. Work hard all the time and relax whenever you want to. Keeping everything under your time constraint will work in favor of relaxation whenever needed.
  16. Believe in yourself.

Wrong.

Hi Everyone,

So tonight as I go on about my three days of rebuilding Scribble I have been able to map out the road map for this monster of a mess.

You can’t build it in 7 days…

This old saying has tracked its way back into my head. Over the course of a few weeks, since I posted my last blog, I discovered the harshness that is software development. I built this software Scribble on an idea of a bottleneck waiting to happen. Then I tried lovely words of faith. In the end, I don’t want to finish it overnight because it doesn’t make sense to mess up twice. Here may be a pivotal point where I could let my software head into another dark age, which may occur, but I want to do my best not to allow that.

My travels take me to many places, and sometimes it’s back to my childishness that is my love for programming.

I want to present a good product, and not something you can put together in one night.

“Rome wasn’t built in a day.”
Frank N.

 

Finalizing Scribble 2.0 Tonight!

Hi,

So the past several months have gone by and I have yet to release the patches to several issues that are occurring currently with Scribble v1.0. I’m going to get it pumped out tonight because several users have been jamming me about issues haha.

I love that people actually use the software so I want to give back as much time as I possibly can tonight to make it all happen.

Thanks for reading,
Frank N.

Working on content

Hi Everyone,

I’ve been working on filling my personal site and project Scribble with more content. I’m trying to get a rhythm to keep producing daily as much as possible. I want to keep a close connection between me and my active areas, because I believe that’s the best way for everyone to see what I’m up to in these areas.

I started a twitter account and added a feed for my Spotify account. I want to just feed my sites with good amount of actual content and not garbage.

This is more of an update :D

Frank N.

Full time developer!

I’m finally a full time developer and titled as such. This new title comes with much responsibility. I’m trying to get to get used to it but more than that I’m getting used to the sleeping schedule. I program way late in the day and now I’m programming way early in the morning. I have to say though its pretty sweet waking up having so much to do, and eating breakfast again is pretty sweet! Well this is day 3 let’s see how it continues!

Frank N.

Developing Scribble

Hi Everyone,

Over the past few months I’ve developed this service called Scribble. It’s a html5 websocket service in hopes of making development easier for the younger crowd. I’m currently 23 and I’ve been developing my skills over several years just to be able to attempt this scale of a project. You can check it out here: http://scribble.franknatividad.com.

Thanks for reading!
Frank N.

New Layout

Hi Everyone,

I’ve decided to move onto a new layout and use WordPress for managing the site. This design change is very apparent, and for the most part everything is still available. I’m still actively working on the website, and I’m trying to bring a more up-to-date website about what’s going on.

I’ll be updating the content over the next few days until it meets my standard of being stable.

Thanks for reading,
Frank N.