Pozyx looks like an obvious repackaging of the EVK1000.  It amazes me that DW didn’t do this themselves at the start.  They could have had a whole bunch of people developing stuff a year ago if they had.
I found it useful to measure pozyx and BeSpoon against our requirments, to see how much further along they are than us.
I think this raises the short-term bar for us quite a bit, but doesn’t change anything other than the speed with which I need to get stuff working.  
The holy grail right now is “wearability”, followed by ease of installation, and maintenance.   If we can really make something that is wearable for long periods, then I think we still could have something unique and valuable – but the race is on.  Only if we can get below the “24×7 wearable” threshold do we (and others) get to run experiments that will uncover non-obvious opportunities that I’m sure are there for the picking.
  • Wearable => small, nay tiny!
  • 24×7 => ultra-low-power => clever protocols
  • Easy/convenient to install => battery powered, or energy harvesting (or doesn’t hog sockets)
  • Easy to maintain => batteries don’t go flat, and doesn’t get confused by being moved
  • Can reliably communicate with support systems (apps, database, cloud, …)  in close to real time
  • I’d like to say “inexpensive”, but all hardware in inexpensive eventually
  • Non-stigmatizing – better improve your image, rather than v.v.
  • UI has to be physically easy to use (feedback is obvious, and input is easy to generate with high confidence)
  • Calibrated => the raw data has to have meaning after the hardware is defunct
  • Software makes it easy for the average programmer to write an analyzer
  • Private and secure
  • Sticky – does something indispensable
I couldn't resist trying to get the temperature right.  It led me to write a fair bit of code to print out all of the DW registers.  I expect that will turn out to be pretty useful soon.

There are two weird things though:
I kept getting 0 for the calibration data.  So I ran the register dump to find out if I was read any registers, and sure enough it all looked kosher, and indeed the temp. calibration was 0x7A.  After some fiddling around I discovered that I couldn't read that register unless a read a whole bunch of others.  I have stopped digging into it, because I suspect it's some kind of weird timing bug.  Maybe the DW has to be in some special state to read the OTP.  It can wait.
The other thing is that if I plugged in the calibration value it didn't make much difference, which I suppose makes some kind of sense.  But the temp went up!  And the T is measured in C, not F, so I'm pretty confused.
Temp = 88.14(uncalibrated) 86.84(using calibration).  Volts =  2.99 

//Mik

A little bit of progress.  One TS06 sent a packet and the other noticed it.  The recieved packet was flagged as having all sorts of errors, which is appropriate because it seems of be full of junk.

Recieved something with status SYS_STATUS_IRQS SYS_STATUS_RXSFDD SYS_STATUS_LDEDONE SYS_STATUS_RXPHD SYS_STATUS_RXPHE SYS_STATUS_RXFCG SYS_STATUS_RXRFSL

But the good news is that something got transmitted.

Top window is the sender, and bottom the receiver.

And a DW packet?

The next example shows output from packets sent by the DW EVK1000 which seems to have much better status.

Recieved something with status SYS_STATUS_IRQS SYS_STATUS_RXSFDD SYS_STATUS_LDEDONE SYS_STATUS_RXPHD SYS_STATUS_RXPHE SYS_STATUS_RXSFDTO SYS_STATUS_HSRBP

Just taking a break from porting the DW code over to the nRF51822.  I’m finding it a steep learning curve, but mostly it makes sense.  There seems to be a lot of flexibility, which in turn means that there is a lot to configure, which in turn means there’s a lot to  understand about the configuration: channels, packet sizes, speeds, preambles and so forth.  And it all has to be perfect to work.  Of course the example code provides one set of parameters that clearly work, so that’s a comfort.

I think the code was originally written to run on a PC, because it is been written to drive multiple EVK1000s from a single processor – I suspect via multiple Cheetah boxes they talk about in one of the manuals. It looks like it has been hacked, and hacked because there is a lot of redundant code, and comments that clearly don’t make sense (well to me anyway).  I guess after the PC they ported it to the ARM m3 without cleaning it up, so there is a lot of extra complexity that we don’t need, and also buffers for the data for multiple devices, each of which is huge, so I don’t think we’d have enough RAM.
the good news is that the documentation is better than the code.  I read the documentation and didn’t understand it all.  Now I am looking at the code and not understanding it all, but the documentation starts to make more sense 😀
Of course they also run the demo nodes in either anchor, or tag mode exclusively, and we need to run it in either mode on demand, so the code is going to be quite a bit different anyway.  But right now I’m simply trying to get a one-byte packet from one TS to other, and I have not even got all the code ported yet.
I notice that a bunch of config parameters are allegedly set up in the factory and stored in the OTP memory of the chip.  I sure hope that they setup some decent default parameters in each module. 
As I get bits of code running I print out diagnostics to see if it makes sense. 
I notice that our two modules have LF clocks that tick at slightly different rates as you might expect (see below).  I guess a difference of 3/1555ths is < 0.2%. But that’s ~3 mins/ day – is that respectable?   Of course it’s calibrated against the high speed crystal which may be wrong too I guess.  Why is a HF crystal more trustworthy?  It wanders around too. 1555 became 1559 very quickly.
low-power osc calibration 1555; blink time 5s; blinktime counter 3.012500
low-power osc calibration 1552; blink time 5s; blinktime counter 2.989700
I think it will take another day or so to laboriously work through the code and understand what needs to be ported, clean it up, and port it over.  then I can start to debug it.
The good news is that so far the code is about 72K, and the data is about 4K, so we are still well above water.  But of course I have not linked in all the DW library yet, so it could suddenly mushroom.
Onwards…  need to sharpen my pickaxe.

//Mik

I had a few minutes of uninterrupted time to begin to scrutinize the DW code.   The code base is pretty big.  The actual demo “application” code, (not even the libraries) are extensive.  See ->

I printed out just the demo part of the code so that I could start to understand how they set up the radios for TOF calculations.  Now it got depressing.

The following code translates TOF to a distance.  It appears that the hardware can produce TOF results that are negative!  OK, I can see how that can happen, but simply turning it into a positive number seems like a odd solution.  I wonder why that is the right thing to do?

Then I notice that the code assumes two complete round trips, i.e. 4 transmissions, and averages them.
Then if the result is less than -50cms discard it – which is odd because the previous line of code should have guaranteed that the TOF is positive.
And then if the distance is still negative then the sign is inverted –  after all the previous work, how can it be negative anyway?
Finally, just to be sure, they generate a moving average of the last 8 estimates.

So each report is actually the average of 32 TOF estimates.   That can’t be good for our power budget!

void reportTOF_f(instance_data_t *inst)
{
        double distance ;
        double tof ;
        int32 tofi ;

        // check for negative results and accept them making them proper negative integers
        tofi = (int32) inst->tof32 ;                          // make it signed
        if (tofi < 0)                          // close up TOF may be negative
        {
            tofi *= -1 ;                       // make it positive
        }

        // convert to seconds (as floating point)
        tof = convertdevicetimetosec(tofi) * 0.25;          //this is divided by 4 to get single time of flight
        distance = tof * SPEED_OF_LIGHT;

        if ((distance < -0.5) || (distance > 20000.000))    // discard any results less than <50 cm or >20km
            return;

#if (CORRECT_RANGE_BIAS == 1)
        distance = distance – dwt_getrangebias(inst->configData.chan, (float) distance, inst->configData.prf);
#endif

        distance = fabs(distance) ;                         // make any (small) negatives positive.

        inst_idist = distance;

        inst->longTermRangeCount++ ;

        inst->adist[inst->tofindex++] = distance;

        if(inst->tofindex == RTD_MED_SZ) inst->tofindex = 0;

        if(inst->tofcount == RTD_MED_SZ)
        {
            int i;
            double sumdiff, avg;

            avg = 0;
            sumdiff = 0;
            for(i = 0; i < inst->tofcount; i++)
            {
                avg += inst->adist[i];
            }
            avg /= inst->tofcount;

            inst_adist = avg ;

        }
        else
            inst->tofcount++;
    return ;
}// end of reportTOF_f

This is my first program that actually needed to know the speed of light.

Glorious fudge factors (pulled out of rectal orifice) require floating point.  N.B. No FP hardware on the Nordic.


//assume PHR length is 172308us for 110k and 21539us for 850k/6.81M
if(instance_data[instance].configData.dataRate == DWT_BR_110K)
{
msgdatalen *= 8205.13f;
msgdatalen += 172308;

}
else if(instance_data[instance].configData.dataRate == DWT_BR_850K)
{
msgdatalen *= 1025.64f;
msgdatalen += 21539;
}
else
{
msgdatalen *= 128.21f;
msgdatalen += 21539;

}

Antenna delay

               //MP bug – TX antenna delay needs reprogramming as it is not preserved
                dwt_settxantennadelay(inst->txantennaDelay) ;

So we have to figure out the antenna delay.

Here’s a useful bit of code for later

int instance_starttxtest(int framePeriod)
{
//define some test data for the tx buffer
uint8 msg[127] = “The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the l”; 
//NOTE: SPI frequency must be < 3MHz

// the value here 0x1000 gives a period of 32.82 µs
//this is setting 0x1000 as frame period (125MHz clock cycles) (time from Tx en – to next – Tx en)
dwt_configcontinuousframemode(framePeriod);

dwt_writetxdata(127, (uint8 *)  msg, 0) ;
dwt_writetxfctrl(127, 0);

    //to start the first frame – set TXSTRT
dwt_starttx(DWT_START_TX_IMMEDIATE); 

//measure the power 
//Spectrum Analyser set:
//FREQ to be channel default e.g. 3.9936 GHz for channel 2
//SPAN to 1GHz
//SWEEP TIME 1s
//RBW and VBW 1MHz
//measure channel power

return DWT_SUCCESS ;
}


This looks like code for using a spectrum analyzer to trim the crystal.


//NOTE: SPI frequency must be < 3MHz
//reduce the SPI speed before switching to XTAL
//
// reset device 
//
dwt_softreset();
//
// configure TX channel parameters
//
configTx.PGdly = txSpectrumConfig[chan].PGdelay ;
//Assume smart power is disabled – not relevant for XTAL trimming as CW mode
    pow = txSpectrumConfig[chan].txPwr[prf – DWT_PRF_16M] & 0xFF ;
configTx.power = (pow | (pow << 8) | (pow << 16) | (pow << 24));
dwt_configuretxrf(&configTx);
dwt_configcwmode(chan);
for(i=0; i<=0x1F; i++)
{
dwt_xtaltrim(i);
//measure the frequency
//Spectrum Analyser set:
//FREQ to be channel default e.g. 3.9936 GHz for channel 2
//SPAN to 10MHz
//PEAK SEARCH
}

I googled your landscape lights idea, and didn't find anything that quite matched.  I saw lots of lame blinkys that were either not solar powered, or wireless.

My two conclusions were:
  • RELIABLE zero/low-power person detection is required to avoid annoying operational behaviors.
  • the value of various animation effects is subjective, and probably minimal
In detail:
If the lights are stretched in a straight line down a path, then that's a special degenerate case of a mesh that we'd have to recognize.
I was thinking about high-value usage cases.  Basically all I could come up with was lights that came on when you started to walk down the path, and went off when you went away.   Maybe for airplane-style "emergency egress".  The easiest way to do that would be to put a PIR on every light.   That would allow multiple subjects to activate their own pools of light – but why?   Most of the time you want the whole path to turn on, and turn off when you leave.   All can be done with local PIR-kind of things that detect movement for a couple of light-radii..
The dynamic effect employed to turn them on/off is mere fluff with no real value that I can think of.  That could all be done with one BLE broadcast, and a watchdog timer to make sure they turn off.
The simplest animated effect would be to slowly fade up/down lights in sequence.  To do more elaborate effects (which I think would be irritating after a while) more careful timing, taking into account spacing would be required, and of course an accurate clock to sync them.  All pretty straightforward I think.
To detect where a person is on the path would require a person sensor on each light.  Just putting person sensors on the path ends may be cheaper but then you have two kinds of light to install, and perhaps to "aim"  which makes it a slightly more complicated process for the average punter.
One way to do proximity detection might be simply to look for changes in RSSI between two gatepost lights.  That would probably be unreliable, and would probably be triggered by waving trees or shrubs.
In summary, it all looks pretty feasible to me, but the hard bit is coming up with a cheap, simple and above all reliable, person-proximity detector, and the rest is straightforward, and can be enhanced with software to do very creative things.


//Mik
I have the basic comms for a mesh working (no actual packet forwarding, but the basic IO is working, i.e. a TS can be in listen, and broadcast mode.
I have rigged up push-buttons to simulate some IMU event.   So when a node senses it has moved  it would fire off a ranging sequence to recompute the revised topology of the mesh.   
So now I can press a button on either node and the other node get's told about it – that's the big breakthroug.  It provides the basic transport mechanism for propagating topology changes throughout the mesh.
So far so good, but of course…
Obviously a node will not advertise changes unless it has some to share, so that side of things should not consume much power.
On the other hand, listening out for advertisements is something you have to do all the time.  After some experimentation I begin to see that the listen overhead is kind of fixed.
If an advert occurs once every 10 ms, then you have to listen out for 10 ms to be sure of hearing a packet go by.  Of course you don't have to listen every 10ms (i.e. continuously) because you could just decide that you are not going to try and catch every advert, and will just listen for one in ten. So you listen for 10ms every 100ms.  On the other hand it requires that the advertiser sends the same packet 10 times at regular intervals.
The consequence of this that you get to choose the trade off between time spent listening, and time spent advertising.   You can advertise once an hour if the receiver listens continuously for an hour. You can listen for just ten minutes continuously if the advertiser sends the same packet 6 times an hour.  What that split should be is determined by the anticipated frequency of transmissions, and the power-budgets.  I'd like to measure thos values.  Maybe we are in the toilet already.
The other consequence of the choice is latency.  If you only design to hear one out of 10 advertising packets, then that means on average you will miss 5 before you hear the first.
So the important thing to decide for our application is how long the application can tolerate to wait before it should hear updates.   
I have paramterised my code so that I can choose any average latency from a few milliseconds out to 5 seconds (i.e. a worst case wait of 10s naively assuming no packet losses).   The longer the latency, the less time spent listening, and that's going to be the battery killer.
It should also be noted that the latency is for one hop.  If it takes 3 hops to get to the edge of the network and thus to the cloud then there could be a 30s wait.

//Mik
I have made some good progress with the BLE side of things.  I am getting to the point where I am fairly confident that I know what minimal BLE functionality we will need in out device, and more importantly, what size it's footprint will be 56K + 88K for softmachine = 144K Flash and 2K+8K for RAM.  I'm pretty sure the DW code will fit in the remaining space, but it uses an (as yet unknown) bunch of RAM for buffers.

But I have also discovered a snag with BLE.  It's too popular!  I have a bunch of Apple devices within wireless earshot (apartment below), all of which implement Apple's iBeacon protocol (based on BLE).  They are issuing unwanted advertising packets that wake me up many times a minutes, just so that I can ignore them and go back to sleep.

There is a mechanism, called a "white list" that allows you to register the MAC addresses of devices in which you are interested, so that the radio controller can quickly ignore unwanted devices without bothering the processor.  The problem is that that list can only be 8 items long.   It also means that you have to tell each TS the identity of all the other TSs.  So if an unknown wearable suddenly arrives, it won't be in the white list and will get ignored.  But if you don't have a white list, then you get  a storm of advertising packets.
The other problem is that iBeacons appear to keep changing their MAC address so you can't ignore a particular MAC address for very long anyway.   
Below, a list of packets I flagged as unwanted in just 60 seconds.  
60 second tick interrupt
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(0)
on_ble_evt BLE_GAP_EVT_ADV_REPORT – BLACK 88(1)
60 second tick interrupt