Withings Pop Activite: Electronic Analysis

IMG_20170306_195524907.jpg

There are two big electronic questions to answer about the activite:

  • Why does the double tap not work well?
  • What are all the parts on the board?

I have decided to document my answers to these questions in a narrative form because I think it is interesting to hear how people solve problems.

Double Tap:

It is pretty obvious that the double tapping is going to be sensed by the accelerometer, the ADXL362.  Unlike the popular STM accelerometers, the ADXL362 does not have built in double tap detection, although it does have a acceleration-based interrupt that can wake it from sleep.  In order to see how the accelerometer was configured, I stuck a logic analyzer on the SPI lines of the device and took several logs of the device in various states, especially power-on and while being tapped.

I used a saleae logic 8 and the logic software to capture the logs and exported them to text files to be parsed by python, which was necessary since each log was several hundred entries long.  This made it easy to distinguish between things that are important that change across logs from things that are not important, like slightly different acceleration readings.

The gist of what the device does on startup is:

  1. turn on the device and read some ID registers
  2. reset
  3. Read the registers again, set some thresholds*
  4. Set up FIFO and FIFO watermark
  5. Read status until FIFO watermark overflow
  6. Read FIFO
  7. Reconfigure FIFO**

The asterisk-ed items are the most interesting, since these are he registers that carry over to the actual operation of the device, and to detecting taps.  Since they get reconfigured several times, we need to look at the register states at the very end of all the configuration.  It is odd to me to see that the ID registers are poked so much and that the device is reset and read again-it makes it seems like they are using some boilerplate library to set this up.

The relevant registers that have been touched are:

THRESH_ACT_L 0XC7: sets the activity threshold to 199, which is about 1/4 g when FILTERCTL is set to 0x41

FIFO_CONTROL 0X0A: puts the device in stream mode, set MSB in FIFO buffer watermark, and turns off temperature measurement

INTMAP1 0X24: interrupt on pin 1 on either hitting the inactivity threshold or the fifo watermark

INTMAP2 0X10: interrupt pin 2 on hitting the activity threshold

FILTERCTL TO 0X41: +/-4 g range, at 25 hz, half bandwidth off

POWER_CTL 0X02: put into measurement mode

So there you have it- the device is just waiting for a signal of more than a quarter g, and then it will signal the host processor that an activity occurred.  But lets dive in a little deeper on the double tapping to see how it is detected.

Capture.PNG

 

In practice, we can investigate this behavior with the logic analyzer by sniffing the SPI lines as well as the output pin to the minute hand that moves when you double tap.  Circled in blue is the minute hand moving, and in red is the SPI transaction that detected the double tap.  The block of transactions in the middle is very similar to the power on interactions mentioned above.  I saw this pattern in several instances about 5 seconds before the minute hand ticks.

Capture2.PNG

 

zooming in on the red part shows us that there is a long time where the chip is selected, but not being read.  Lets take a look at the accelerometer data from that read.

tap 2 total.png

 

The pink line is the total acceleration, and the horizontal line is the activity threshold, which the tap threshold is probably greater than.  The blue line is the “break” in reads.  Since the total number of reads is the whole FIFO, it seems like the window for tapping is at most about 6.8 seconds (512 records % 3 * (1/25Hz)).  Likely the window is smaller than that- assuming the break happens right when the second tap is detected, we can see that the window from the last tap to the previous one is about 25 records, or about 1 second.

tap 1 total.png

 

This is backed up by a second reading, in which the second tap was detected after about 25 readings.  This very strict timing and a relatively high acceleration threshold is probably why it is so hard to get the double tapping to work.  One thing that could make it easier on users is having a larger or more permissive window, but to only sense tapping one direction- it would reduce the cost of having to sift through 1/3 of the data, and it would probably be less prone to noise.  Or you could just do what I did and bang on the face of the watch all day.

What is on the board?

img_0137

The board is shockingly bare.  In the upper left hand corner, there is the NRF51288, and its accompanying crystals (silver).  Right below it is an almost invisible ST micro Balun.  Just south of the crystals at 9 o’clock is some kind of mystery part with an inductor, or possibly an antenna.  South of that looks to be a reverse polarity protection diode.  At 6 o’clock is the motor, and at 5 o’clock is the motor driver, marked “1X W48”  At 1 o’clock is the ADXL 362 accelerometer, mounted right next to where the chassis PCB is screwed to the case.  Just CCW of that looks like some antenna balancing circuitry, but it is hard to be sure.

The really interesting thing is what is not on the board.  Very frequently with this kind of product, you will have some kind of voltage converter and a memory chip.  And for the relatively higher-current steppers, I would expect to see some kind of h bridge or FETs.

For the pop, it seems like a voltage regulator (or converter) is not needed since there are so few components, and the two heavy hitters on battery usage- the NRF and the ADXL, need to be on in low power sleep or collecting data all the time.  Since they sleep so much it is possible that the quiescent current to keep the converter running would overcome the savings in power from running at a lower voltage.

As for memory, this is the AA variant of the chip which has the expansive 256 kB flash (twice the AB variant), but only 16 kB RAM (Half the AC variant).  I imagine most of the records are then processed in some way and stored on the chip.  This saves on parts, and power on a second chip, but apparently limits the tracking 38 hours between syncs.

connex1 wheel.png

The motors that drive the hands (the movements) are probably driven from the 5mA high-current outputs on the NRF.  There can be up to 3 at any given time, meaning that a the NRF can dump a shocking 15mA out at a time, at the battery voltage.  This is consistent with reports form users that at the end of the battery life, the hands stop working, even though the watch knows what time it is.  At the end of the battery’s life, the voltage will drop, and less power will be delivered to the motor- some of these motor movements will fail, and the clock face will be wrong.  Above you can see the current output (green line) from driving the motor- the peaks are around 15 mA above the “floor”.

5x press3.png

On the other hand, the motor is too large for the NRF to handle, so some kind of FET arrangement is used to drive it.  It takes 60 mA (peak) from the battery!  That is why setting a lot of alarms is taxing on the device.

Final Thoughts:

This is a pretty simple electronics-wise for such a sophisticated device, which speaks volumes about the team that designed and built it- it is pared down to the absolute minimum complexity, with only the barest of sensor packages.  Yet I feel the device does deliver on what it promises to do.

 

Withings Activite Pop Teardown

IMG_0022.JPG

The best looking wearable I have seen, and this is the cheap version

Shockingly, nobody has taken the time to do a detailed teardown of the engineering marvel that is the Withings Activite.  I snagged the “pop” version for $40 bucks on ebay and wore it for a few days before tearing it down.  I noticed a couple of things in that time:

  • it’s not good at measuring bouldering as activity
  • the double tap function almost does not work
  • the alarm seems like it works pretty well
  • you cant read it in the dark, which makes it pretty useless as a watch

Now for the teardown- this will be the first of several posts.  This just details actually taking the thing apart.  The next two will look at the interesting electrical and mechanical aspects.

Mechanical Teardown:

IMG_0071.JPG

Step one is to pry off the back with a screwdriver using the small divot under the button.  This is the official way to replace the battery, although withings recommends taking it to a professional screwdriver user to do this.  NB if you are not going to destroy your watch, it is probably a good idea to do this with a soft, wide, rounded tool, not a random screwdriver.  This will avoid marring the case.  In the rear case there is a tiny spring plunger, a piece of foam to back up the battery, and an o ring.

Here you can see the back, the battery, and the watch.  Note the holes on the left bottom of the watch.  These cover a programming/debug header.

IMG_0104.JPG

Once the screws are removed, the vibration motor needs to be carefully pried off of the plastic, since it is still attached to the PCB with wires.

IMG_0116.JPG

At last, the PCB is unmasked.  Here you can see the vibration motor (6 o’clock), dome switch (12 o’clock, yellow), debug/programming header (6 o’clock to 9 o’clock), and battery clips.  Also sprinkled throughout (and important) are a number of screw heads (silver) and test points (yellow).

IMG_0125.JPG

With the smallest screws in the world removed, we can now separate the PCB from the movement of the watch.  The V-twin in the middle is from the minute and hour hand, while the single-cylinder looking thing drives the activity meter.  We can also see the front of the PCB for the first time.  Interestingly, the movements are attached electrically to the thin PCB with ENIG plated pads being screwed directly to metal standoffs in the movements.  Behind the movements is a piece of plastic, which serves to help orient the components and seat the vibe motor.

IMG_0137.JPG

Here is the PCB.  Major components include:

-NRF51288 Bluetooth SOC, of course.  This is the WLCSP BGA version, which makes sense because much of the board is covered by the movements. (11 o’clock, rectangle)

ADXL362 accelerometer by analog devices.  A super low power accelerometer.  It will be interesting to think about why they went with that over something cheaper from ST micro. (1:30, square)

-Probably an h-bridge or motor driver (4 o’clock, square)

The rest of the paraphernalia is the usual flotilla of antennas and crystals.  Interestingly, there is a non-pop 8 pin device, 3 pin device, and 2 pin device.  I will have to think about what those might be.

img_0147.jpgThe next step of course is to remove the crystal/glass (in this case, lime glass).  This is so I can take off the hands, which will let me take a closer look at the movements by detaching them from the face of the clock.  I was not sure how everything was held in, but I suspected glue, since a press fit would be a bit risky (and possibly not very water tight).  After evenly heating to about 275 C to soften any glue, I was able to press everything out of the front of the watch with my fingers.

IMG_20170302_171634254

At this point, the hands were still pressed onto the shafts of the movments.  Like most dials, these can be removed with a careful application of tweezers as pullers near the shaft that they are pressed onto.

IMG_20170302_171908229

Here the hands are removed, the double shaft/collar of the hour/minute hand is exposed.  The hour hand rides on the outer hollow shaft, and the minute hand goes on the inside.  The activity hand is much smaller and brass, while the two other hands appear to be aluminum.

IMG_20170302_172454752.jpg

With the hands removed, the movements are no longer constrained to the face of the watch.  Here is the top of the activity movement, showing the output shaft.  Those four silver things are actually tiny machine screws.

IMG_20170302_172617288.jpg

Here is the bottom of the movement.  The four gold colored metal bits are threaded metal standoffs.  Tiny truss head screws pull the traces on the movement into intimate contact with exposed traces on the PCB.  On the reverse side of the copper-coated laminate, the two electromagnets are soldered to copper on the other side, connecting them to the PCB.

IMG_20170302_172704294

A shot from the side showing the internal gearing of the drive.  The complicated stackup of fiberglass, metal, plastic and tiny gears can clearly be seen.

IMG_20170302_180533889.jpg

With the screws removed, I managed a closeup shot of the magnet that drives the mechanism.

IMG_20170302_195146140.jpg

To determine how it worked, I screwed the movement back onto the PCB and connected to the app to “set the time” which works by positioning all the hands to reference positions.  This let me rotate the hand arbitrarily.  By putting a very tiny dot of prussian blue on the magnet, I was able to see that it was in the same spot every other tick- in other words, the magnet rotates 180 degrees per tick.

Final Thoughts:

IMG_0062.JPG

Most fitness trackers are a variant of a PCB in a box with some kind of typical (LCD, OLED, LED) interface.  Even the iconic nike fuelband is just a cool rendition of LEDS inside of a piece of plastic.  The activite is a well-crafted and very complicated departure from what I normally expect from an activity tracker.  It feels a lot more sophisticated and it blends into everyday clothing compared to a colored piece of plastic with a glowing display.  Since it has a primary battery, it does not even intrude in my daily thing-charging rituals.  I think I understand why people would choose this over a fitbit or garmin vivo-thing.

 

 

Thermal Camera Redux

IMG_20160713_231049674

After chilling out in my parts box for a few years, I decided to drag out my old panasonic GRID EYE thermopile array, because lets face it, we all want thermal vision.  I felt like last time I worked with it, I did not do the sensor justice.

So I whipped up a quick model for an enclosure.  I was not sure how I wanted it to look, but I settled with something like a pana-view stero viewer.  The device really does not need to be any bigger.

After printing out the enclosure and writing some code, it is working!  But I ended up using the lens and the enclosure shape for the gopro viewfinder instead.  I guess I will have to do something else with this sensor.

GoPro Viewfinder

IMG_20160720_165046844

The GoPro is quickly becoming my favorite camera to take hiking.  It is lightweight and unobtrusive, compared to even a small DSLR or compact.  The battery lasts long enough, and with a case on it it can be waterproof.  I actually carry a pair of them, one with a screen and a polarizing filter and an LCD for landscape photography, and one just for pointing and shooting with no filter or LCD.

eyefinder

One issue I have with the LCD is I can’t see it at all in the sun.  Its not particularly high brightness, resolution, or contrast, and if I can’t see it, it is just burning battery.  Not being able to see it also makes it impossible to properly adjust the polarizing filter, which means I end up with blurry, flat photos sometimes.

To resolve this problem, I printed a shroud to go around the back of the camera.  Friction and potentially rubber bands can be used to hold the camera in place.  A lens and an eyeshield make it comfortable to hold up to the eye and see what you are shooting.

IMG_20160720_165129199

It looks like it works so far.  I am excited to give it a test run in the real outdoors, although it may need a few more features before then- like something to hold in the camera, and eyelets for a strap.

Custom Service with SD110 the NRF51288

As a person who makes their living in the IOT world, I decided it would be handy to learn how to get data to and from the venerable NRF51288, which is certainly a contender for the best bluetooth SoC out there.  One annoying thing is that the wonderful Softdevice that Nordic provides does not have a particularly clear English translation, or good pictures in their documentation.  Here I will try to describe with pictures and arrows that my mechE brain is accustomed to, instead of with comments like “Attribute metadata for the User Description descriptor, or NULL for default”.  Hopefully after reading this, you will have a better feel for how to make a custom service, as well as what the more formal terms are for the different parts of the service in the softdevice API.

How to read this:

The 10,000 meter view will start with the softdevce and the GATT server, then at 1,000 meters, we will inspect bas.h to see what the minimum viable functions for a bluetooth service are.  At 100 meters we will look at the softdevice calls that add the information to the GATT server.  at 10 meters we will look at what gets passed to those calls- the real key to understanding how to add things.  At the very end, I will show you how I changed the bas example to report acceleration values.  This is by no means a definitive guide, but hopefully it will give you a jumping off point for adding services to your code.

10,000 Meters: GATT and the Softdevice

path3459-1.png

The thing we want to do is add a new service to our Generic Attribute Profile (GATT) server.  Adafruit does a wonderful job of explaining it here, with pictures.  Once you connect to a BTLE device over a Generic Access Profile (GAP), your bluetooth client, usually something like a phone or computer, can access the GATT server, usually something like a the NRF5.

A GATT server can have characteristics, as well as services.  Services are groupings of characteristics.  Services are distinguished by a 128-bit UUID.  Some of them are reserved for particular types of services, like a heart rate monitor, but we will deal with making sure we don’t use those later.  Two that are always included are the Generic Access Service and The Generic Attribute Service.

Characteristics live inside of services, and have characteristic values.  These characteristics can also have descriptors, which can tell you useful stuff like what the name of the characteristic is.  Characteristics are things that can be read and written to the server.  A client might read log data or a sensor value, and it might write a new state for the device or a device name.

The way that you access the GATT server on the NRF51288 (as well as the NRF52) is through the nordic softdevice (SD), which is a pre-compiled binary that you append to the beginning of your user application code.  It handles all the GATT and GAP stuff, and you need to tell it to do things like add services and characteristics using calls to the softdevice that are provided by nordic.

1000 Meters: What is in bas.h

typedef struct
{
...
} ble_bas_init_t;

typedef struct ble_bas_s
{
...
} ble_bas_t;

uint32_t ble_bas_init(ble_bas_t * p_bas, const ble_bas_init_t * p_bas_init);

void ble_bas_on_ble_evt(ble_bas_t * p_bas, ble_evt_t * p_ble_evt);

uint32_t ble_bas_battery_level_update(ble_bas_t * p_bas, uint8_t battery_level);

Taking a look at an abbreviated version of what is in bas.h will let us know what kinds of things a a bluetooth service might need to do, and what kind of stuff we should have to use the SDK as a template.  Here is a listing of the most important stuff in bas.h

Structs:

ble_bas_init_t- this holds all the data for initializing the service

ble_bas_t – this holds all the data for actually running the service

Functions:

ble_bas_init – this initializes the service, and inserts your vendor specific UUID, your service, and characteristics into the softdevice

ble_bas_on_ble_evt – this is called when the softdevice thinks something interesting has happened over BT that concerns the bas service.  For example, a client might write a new value to the server, or want to read a new value from the server.

ble_bas_battery_level_update – this actually updates the value of the characteristic with the new battery level.

So, all the stuff you need is in ble_bas_init_t and ble_bas_t, for the actual data about the services, and then you call ble_bas_init and it gets added to the softdevice.  After that ble_bas_battery_level update gets called every now and then, and if anything interesting happens bluetooth-wise to the service, ble_bas_on_ble_evt handles it.

100 Meters: Adding a Service and Characteristics

Of these, ble_bas_init and ble_bas_battery_level_update are the most interesting functions, so we should look inside of them to see what is going on.  Here we will be focusing on sd_ble_XXXXXX calls, which actually add things to the Bluetooth Low Energy SoftDevice- hence sd_ble.  later we will go back and look at what we are actually feeding these functions.

ble_bas_init is a small function , but it also calls batter_level_char_add which we will need to take a look at- it is something we did not see in bas.h, but it adds the actual battery level characteristic to the service.  The big ble_sd calls here are:

BLE_UUID_BLE_ASSIGN

this is just a macro to put the BLE_UUID_BATTERY_SERVICE information into a struct for later- if we want to make a custom service we will have to do it a different way, since our custon ble UUID needs to be added to the softdevice.

sd_ble_gatts_service_add

This adds our service.  There are some options here, but as long as you have this you will be able to see a service with the BLE_UUID_BATTERY_SERVICE if you look at your device with something like the nrf master control panel app.

inside of battery_level_char_add, a lot of other ble_sd calls happen- lets take a look!

sd_ble_gatts_characteristic_add

this actually adds our characteristic.  This is pretty useful, since we want it added.

sd_ble_gatts_descriptor_add

this actually adds the descriptor of the characteristic to the characteristic.

10 Meters: What goes into Softdevice BLE Calls

This is the real dive into the nonsense that is the NRF5 SDK.  I think that a flow chart/visual way of organizing this information is nice, although you have to zoom way in to read the text.  This is just another way of organizing the data, but instead of having to jump around a bunch of windows and files, I put it all in one place- like a cheat sheet.

These are meant to be read from the bottom up- look at the “what can you control” bar to see what you can change, the “where it comes from” bar to see how to actually make the change, and then the “where you can put it” bar shows where you need to put the variables in the sd_ble call.

sd_ble_gatts_service_add

text4926-7

This char has three layers explaining the arguments of the sd call.  The bottom layer is what you can change, the middle layer is how to generate the change, and the top layer is the actual argument and type that gets passed to sd_ble_gatts_service_add.

Here you can change the type of service between primary and secondary.  A secondary service is a service within a service, which seems fairly unusual.  You can also set the UUID of the service.  If you want to use a bluetooth SIG approved service, then you want to something like:

BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_BATTERY_SERVICE);

 

which is a macro that will fill the appropriate fields of ble_uuid with the info for a battery service UUID. NB, if you put weird characteristics inside of this service, the client may not be able to find the correct data, and you will not be compliant with the BTLE spec.

If you want to make your own custom service, you should do something like

sd_ble_uuid_vs_add(&acc_acce_uuid, &p_acc->uuid_type);
ble_uuid.type = p_acc->uuid_type;
ble_uuid.uuid = BLE_UUID_TYPE_VENDOR_BEGIN;

 

This will make a new vendor specific (vs) UUID and then you manually stuff the values into ble_uuid before passing it to sd_ble_gatts_service_add.  When the function is called, the softdevice will write to the pointer for the service handle.  This is important for later on, when you may want to modify the service- for example, adding a characteristic.

sd_ble_gatts_characteristic_add

Here you can change the service the characteristic is attached to, the characteristic metadata, and the structure that holds the characteristic value.  Just like sd_ble_gatts_service_add we also pass it a pointer to a handle that gets filled in by this function.  Unlike sd_ble_gatts_service_add there are about a million things that go in here, so each one will need its own diagram.

In the bas service example, we are adding a battery level characteristic to the service, so we should pass in the handle that was written by sd_ble_gatts_service_add.  We could pass in any service handle that was written by the softdevice, and it would add the service to that softdevice.

g6568.png

The characteristic metadata is how you control the properties of the characteristic, such as the read/write permissions and user description (a text description of the characteristic), but nothing to do with the value itself.  It has many many fields and some of those are actually other kinds of metadata structs.  The main groupings of structures are:

First, things that have to do with the properties of the characteristic, such as broadcast, indication, notification and read write permissions, writing with queued request and writing the characteristic user description, as well as the use of bluetooth assigned numbers to indicate the unit, exponent, and format, of the characteristic.  These are all about the actual characteristic and the value that it represents.  This is useful if you wanted to say that the value you are sending to the client is a uint16_t that represents micro-coulumbs for your battery measurement.

Second, the user description- this is really handy if you want to have a string associated with the value that you send that says “battery level”.  Here the important things are a pointer to a string buffer that holds the value, as well as the current length of the value, and the maximum length. Maximum length is important if the descriptor is re-written.  The last part of this grouping is the ble_gatts_attr_md_t, which controls how the description can be read and written.

The final grouping has to do with permissions around turning on and off notifications and indications, which live in the Server Characteristic Configuration Descriptor (SCCD) and Client Characteristic Configuration Descriptor (CCCD).  This post does a decent job of explaining it.

g6742

The last thing that goes into sd_ble_gatts_characterisitc_add is a structure that holds the characteristic value contains two main groupings.  The first is a pointer to a buffer that holds values of the characteristic, as well as information about the max length of the buffer, the size of each record in the buffer and the current offset.  The other grouping contains the metadata about r/w permissions.

sd_ble_gatts_descriptor_add

This is the last softdevicecall, and mercifully it is pretty short.  Just like the other calls, you give it the handle of the characteristic it belongs to, as well as a pointer to store the handle for the descriptor.  The only real data you need to set up for it is a ble_gatts_attr_t which is the same as the one used in the sd_ble_gatts_characteristic_add call.

1 meter view- Custom Service

Lets make a new service- this will document the major changes from bas to a custom accelerometer service with three characteristics that represent x, y, and z accelerations.  For the sake of clarity, we will assume there are functions that do things like get the accelerometer data.  I will show code snippets in the post, but you can also see the whole project here, commit 705deb.

First off, we need to add a custom UUID to our service in ble_bas_init.

ble_uuid_t ble_uuid;
ble_uuid128_t acc_acce_uuid = {{0xAA, 0xCA, 0x55, 0xAC, ..., 0xEF}};

err_code = sd_ble_uuid_vs_add(&acc_acce_uuid, &p_acc->uuid_type);

ble_uuid.type = p_acc->uuid_type;
ble_uuid.uuid = BLE_UUID_TYPE_VENDOR_BEGIN;

 

This adds our new made-up UUID to the softdevice, and then we pack the data into ble_uuid for later use in sd_ble_gatts_service_add.

Next up, we need to make some characteristics to belong to this service!  I will focus on only one characteristic here, because they are all acceleration data (just in different axes).

Compared to the battery level, we will want to make it a uint16_t instead of a uint8_t, give it a text description and add some nice units to our characteristic.

Lets start with making the value a uint16_t.  To change this in the GATT server, we will need to edit the ble_gatts_attr_t that represents the characteristic value that gets passed into sd_ble_gatts_characteristic_add.  In the code, that is called att_char_value.  To make the characteristic value a uint16_t instead of uint8_t, we need to do this:

attr_char_value.init_len  = sizeof(uint16_t);

attr_char_value.max_len = sizeof(uint16_t);

 

This will change the expected size of the record in the characteristic.  Now it is expected to be a uint16_t.  NB this only changes what the softdevice expects, so there are several other places in the code that will need to be changed to use the proper variable size.

Now for the text description.  This is handy, because your app might want to know what each characteristic represents, other than some crazy uuid.  This is pretty easy, since this information lives in the ble_gatts_char_md_t struct that we already passed to sd_ble_gatts_characteristic add.  We just have to change it by actually making a char buffer and passing the information about the user description size and max size into the struct, like this:

uint8_t user_desc[] = "ACCEL";

char_md.p_char_user_desc = user_desc;

char_md.char_user_desc_max_size = 5;

char_md.char_user_desk_size = 5;

 

We also have to give ble_gatts_add_descriptor an idea of the read/write permissions for the description.  We do this with a ble_gatts_attr_md_t

ble_gatts_attr_md_t user_desc_md;

memset(&user_desc_md, 0, sizeof(user_desc_md));

BLE_GAP_CONN_MODE_SET_OPEN(&user_desc_md.read_perm);

 

Although I could have set whatever permissions I wanted.

Finally, this all gets put into sd_ble_gatts_descriptor_add via the attr_char_value that holds all of the data.

Finally, lets add presentation format data.  We make a presentation format characteristic variable and put some values in it:

ble_gatts_char_pf_t accel_pf;

memset(&accel_pf, 0, sizeof(accel_pf));
accel_pf.format    = BLE_GATT_CPF_FORMAT_UINT16;
accel_pf.exponent  = 0;
accel_pf.unit      = 0x2713; //code for acceleration

 

then add it to the characteristic metadata:

char_md.p_char_pf = &accel_pf;

 

With this field filled in, it will automatically be added when the characteristic is added with sd_ble_gatts_add_characteristic.

0 Meters: Inspecting The GATT Server

Screenshot_2016-07-03-15-10-42

To check to make sure the device is working properly, I used the nrf master control panel app to connect to and interrogate the GATT server.  As you can see, everything is working!

“Gravity” app for Misift Flash

After a long trip to CA, I am finally back at working on fun stuff.  First up is an app for the Misfit Flash that lights up the lowest LED on the board.  Source is here, but it is a bit hapazard and documentation is somewhat scarce.  So if you are going to build it make sure you have a few minutes.

The how:

This technique assumes that the board is not accelerating at much more than 1g in any direction- in other words, gravity has to be the dominant force.  It would not work very well if you were in a car speeding up, but it would work ok you were in a car with a constant velocity.

unit circ illus.png

To determine which way is down, I read the accelerometer and look at the X and Y components.  The Z does not have very much information for me, because tilting the board to the same angle in any direction will give you pretty much the same Z value.  The Z value would be good for something like a bubble level, but it is not useful here.  Another way to think about it is that a gravity vector straight down is being decomposed into ijk vectors that are fixed to the reference frame of the board.  We want to know in the ij plane which way is most “in line” with the gravity vector.  The X and Y axes of the accelerometer are collinear with the i and j unit vectors in this case.

The X and Y components form a vector that points in some direction on the face of the board (orange vector above).   Each LED lies along an imaginary circle with a radius of 64 units, although I show it as a unit circle above.  Each of these direction vectors is dotted with the XY vector, which gives a “score” of how much the XY vector is pointing in the direction of the LED.  The vector with the largest score has its LED illuminated.

Other Approaches and improvements:

atan

There are simpler ways to do this with non-integer math and conditionals, but floating point math is apparently “expensive” on a M0.  The easy way to do it with floats would be to make a table of inverse tangents, and find the ratio between the X and Y components.  Then it would be simple to just look up which range you were within- for example the 2 o’clock LED here is between .25 and .66.  There would have to be special provisions if X were ever 0 to avoid a division by 0 error, but that would be pretty simple.

An easier optimization would be to get “hints” from the sign of the XY vector, if optimizing for speed.  right now, each 12 dot product operations and 12 conditionals are evaluated each time.  This could probably be cut down to 4 dot products and 6 conditionals if by looking at the quadrant of to do the dot products in first, depending on the value of the XY vector.  Given that the processor is only doing one thing, it does not seem like it is useful to implement.

Commandeering the Misfit Flash Board

setup w friend1

The flash acting as a thermometer.  Also pictured, FTDI friend and discovery board aka stlink programmer

In my last post about the Flash, I showed all the LEDs lighting up, but I really did not have the board mapped out or the toolchain set up yet.  I just went on mbed, and turned all of the outputs on and off, a condition pretty much guaranteed to blink some LEDs on the board.

I wanted to do something a little more involved with this board, so I took the time to reverse engineer the connections and figure out to talk to the sensors and I/O elements on the board. I have figured out the LEDs, the button, the internal temperature sensors, and the accelerometer.

LEDs

This one was tedious, but not hard to figure out.  I knew all the LEDs would turn on, and that it was not likely to hurt anything if I had all the pins high.  All I had to do here was write a program that turned one pin on high at a time, until I had a map of pins to LEDs.  I was surprised to find out that the NRF was actually not sourcing the current to the LEDs- meaning that when the pin is low, the LED is on.

energizer_chart

For about .5 seconds, the battery can source ~33mA

I am not 100% sure why they did this, but my theory is that they found out that you can sink slightly more than the advertised current into the chip, and that the driving output is pretty limited.  According to the datasheet for the energizer CR3032, it looks like it can provide 33mA for a few seconds before some serious voltage drooping sets in.  It also seems like trying to output more current might drop the output voltage more, while low side drive might guarantee a lower voltage (and therefore a bigger voltage drop, and more current across the LED).

flash button hold2

The current the original firmware drew when doing an LED animation

This is a recording of the current drawn from a 3v power source by the device using the original firmware, with the LEDs on.  You can see that they were very careful to use a lot of PWM (about 50% duty cycle) to keep the average current low, but the instantaneous current got as high as 30mA!

Once I figured out what went where, I made a nice little animation that turns on all the LEDS starting at 12 o’clock, and added them to a custom flashboard.h header so it would be easy to reference and compile everything.  I did this a few too many times and ended up killing my battery!  But I also found out ALL the LEDs can be on.

Button

button0.jpg

What a nice little dome switch

The button was also tedious, and I ended up not figuring it out on the first try.  My initial approach was to run my clock animation any time the button was pressed, pulling the button high, and waiting for it to go low.  Unfortunately, this did not work quite right, because the button was already pulled high by an external pullup.  This made it pretty tricky to find the button by looking for a response when the button was pressed.  Eventually I just looked for connections on the board, and then tested a few likely candidates for voltage drops when the button was pressed.  I also found what looks like a voltage divider, which may be used for measuring battery voltage (but I am not sure yet).

Temperature

temps

It is toasty in there!

The NRF51288 has an internal temperature sensor, which I decided would be fun to print out over the UART.  It seems ballpark reasonable, as it settled on about 42 or 43 C for my body temperate- but it may be generating a bit of heat on its own. It is a cool “free” addition to the board.

Accelerometer

spi

CS: 2 MISO: 5 MOSI: 4 CLK: 5

After a bit of poking at registers, I wrote a little SPI routine to talk to the accelerometer on board.  After I made sure I could read the WHO_AM_I register, it was easy to get the rest of it working.  Figuring out the pins for this was super easy, because they are labeled on the board- I just looked at where they went and I was right on the first try!  I even got CPOL and CPHA right.

Things I might make

Well, now that I have all this stuff working, its time to make this into something else and call it a day.  There are a few ideas I thought would be worth a try:

  1. Digital plumb bob- the bottom most LED would always light up as the device was rotated
  2. Digital marble- simulate a marble or a drop of water that “settles” at the lowest point
  3. Vibration logger- how often are doors opened?  when does the HVAC run? when are toilets flushed?
  4. Height sensor- detect free fall, and count how long before hitting the ground