- I don’t understand the choice of pin assignments for UART, SPI and I2C. Your assigns are perfectly OK, but please look at http://blog.touchstone-labs.com/2015/05/pin-assignments.html At the bottom of the page I suggest assignments that make the software tidy. I’m assuming that if all the pins are going to the same module are adjacent on the uC, it makes the routing cleaner, but I also realize I’m talking out my arse! So maybe the other DW signals should be on pins adjacent to the SPI?
- I was assuming that I’d use an external FT232 (as I have now) to carry debug info to the PC. But it looks like you are incorporating one for charging. That makes my life easier for sure, but does it soak up a lot of board space?
- On the DWM module it looks like p10 and p11 are grounded. I think p9 and p10 should be grounded to hardwire the SPI protocol details.
- Confirm that DW m_sync not connected to anything is OK. I don’t need it.
- As a general rule, it ought to be possible to turn off ALL the LEDs to conserve power, even those not under direct control of the processor. Jumpers, or scratch-out links would be good in places where the software can’t disable them.
- The more stuff we can get off the SPI the better, because the radio may have a limit to how long it can wait after raising an interrupt. So loading stuff (like the IMU) on I2C is slightly preferred.
- For some reason the Nordic standard is active low for buttons. Maybe there is some subtle power-saving reason?
- You mention a pressure sensor in the notes, but it’s not present (which is fine) FYI I think a pressure sensor can be attached to the IMU, rather than hanging off the I2C (or both?). I need to find out what the pros and cons are.
- I’m not sure what the benefit is of having a micro USB connector, and routing to the “USB and UART” J.
- Can you route 2 GPIOs to the SERIAL_COMM connector (nc-a and nc_b) pins to act as CS pins for any device we might want to experiment with. They can also act as triggers for a scope. Maybe they can be jumpered to LEDs by default?
- Maybe route 5V and 3.3V to same J connector too? No idea if this makes sense.
- Nordic pins 17,18,19 are ADC pins. I can imagine they might be useful for power experiments if they were easy to get at.
- You said the microSD card would suck power. We could leave it off. If space becomes an issue before the next rev (unlikely) then I can hang it off a J connector.
- Should the LTC2942 pin be routed to a Nordic GPIO so it can raise an interrupt?
- LTC3127. Should pin3 be under software control? Is it for forcing power-off when all hell breaks loose? What about pin 4. I never understood what it did in TS05
- LTC3105. What does SHNn do? How is it different from LTC3127p3? How would I use PGOOD? Should it be routed to a GPIO?
- Nordic pin24 is labelled PWRON. What does it do?
- Is there a convenient point to measure tiny currents other than with the coulomb counter.
- Need to connect the MPU9250 “INT” pin to the processor.
It was pretty easy to simulate a UART over BLE. Nordic had made an example service, but there was no corresponding “central”.
Lashing together the central was actually pretty easy – I stole some fragments from the web, and it came together quickly, but getting the right mix of components proved to be tricky. Of course the “central” uses a different soft-machine, and has to be matched with the right API so the calls get vectored to the right place.
When eventually it would compile, then it wouldn’t load producing the useful message “ld.exe: region RAM overflowed with stack“. It took me a while to figure this one out. The stack grwos down from the top of RAM, and the rest of the RAM allocations grow up from the bottom of RAM. This message means that there was not enough RAM to run it. So for the last two days I have been trying to figure out how to save RAM.
Simply optimizing the code of course makes no difference because this is data. The obvious wheeze is to get rid of the heap which consumes 2K of RAM and is not used. That seems like an easy thing to do – and it is – just change the value of the compile-time constant: __HEAP_SIZE=0
Problem is that it is not a compile-time constant, it is an assemble-time constant. So I chased by own tail for hours trying to understand why the heap stuck around. Of well – another painful lesson, but at last the program is working.
Here’s a demo of the TS connecting to my Android. It works in both directions echoing back whatever the Android sends it. Also, when the button on the TS06 is pressed it sends a canned string to the Android.
The big triumph is that I can now send strings of characters between TS06 and the Nordic eval board PCA10001. Both are connected to my PC using USART-to-USB adaptors and so in this demo all you can see is two typescript windows showing that TS06 is sending a string to PCA10001. Gives you some idea of the possible data rate.
Explaining the BLE architecture to myself was actually helpful. I worked through the code more carefully and discovered that I didn’t quite appreciate that services, and characteristics all use the same 128 bit base address. I guess that all makes sense because it’s important for listeners to be able to reject radio messages that are not from a service they care about.
So here’s the physical setup: The TS06 is playing the part of the wearable. The Nordic eval board is playing the part of the gateway to the cloud.
The black blob on the end of two black wires is a button I deftly soldered onto the TS06 board. (Exquisite huh?) It is the “sensor”.
The demo has three phases: discovery, connection, and data transfer.
- Phase 1: The TS board starts off scanning for a gateway. It has LED_0 illuminated while it is scanning.
- Phase 2: It discovers the gateway and tries to connect. If successful it turns off LED_0 and turns on LED_1. Now it is ready to transfer sensor data up to the cloud.
- Phase 3: Each time I press the button the TS06 generates a “button-down” or “button-up” event, and sends it up to the cloud.
- When the cloud receives the event it logs it.
- Then is sends an “led-on” or “led-off” event back to the TS06.
- The TS06 receives the event and does the obvious thing.
The latency is awful.
Another week has gone by and I’m still trying to create a sender and receiver for a custom BLE service. Why this is necessary is a lesson in itself, and I can’t believe how hard I’m finding this – but I guess old brain is a factor. It’s all supposed to be so easy, but I seem to find gotchas at every touch and turn. One of the issues is that BLE defines new terms that are subtle and confusing.
I’m trying to do something that’s “Oh so simple” – create a custom profile. A profile defines what a physical BLE device does, i.e. how it behaves. The profile is like a contract between the two ends of a BLE radio link that defines how, and what the two devices are going to communicate. It’s the high-level protocol that allows them to understand the meaning of the information they have agreed to exchange. BLE distinguishes the word profile, from protocol. I can’t really see the difference, but clearly a profile defines higher-level semantics, and protocol lover-level. The protocol might describe addressing a routing issues, whereas the profile describes how the data should be interpreted by the application. And what’s an application…? I’m not going there.
The terms server and client are too rigid for BLE. BLE defines the term role. Although in BLE every connection defines one end as master and the other end as slave, there appears to be no reason why a device cannot be both. It can even be both at the same time. Example roles are “heart rate sensor” and “heart rate collector”. This is all a bit too subtle for now, but it’s good to know that there is this verbal gotcha lurking in the woodwork. Client and server are not terms that crop up much.
So… back to the profile. Each profile defines a set of services. Some services are mandatory across all profiles, some are so common that they have been defined by the BLE standard, and others are specialized to the particulars of the device – a custom service.
- One mandatory service provides the human readable string describing the profile, e.g. “Heart Rate Monitor”, or “HRM”.
- A standard, but non-mandatory service, “BAS” provides the state of the battery – an estimate of the charge left in the battery expressed in % or volts.
- A specialized service might provide the state of some sensor, or actuator, e.g. the rate of flow of water through a valve. TS has a special behavior so it has to define a custom service in order to communicate over BLE.
A profile can combine a mixture of mandatory, standard and custom services. So a profile might combine an HRM service and a BAS service. A more elaborate device might add some proprietary extra service call “FOO”.
Now, for me, it all gets a bit arbitrary. For some reason BLE has made some decisions about what services will be standard. I think some of these decisions are based upon who was at the table when the standard was being written. For example the HRM service is defined in the standard, along with the HTP “Heath Thermometer Profile”, and the HID “Human Interface Device” profile.
This is an important distinction because standard services are allowed to use privileged (and shorter) packets to do their work, thus saving power. A standard part of the traffic in each packet is a UUID. This defines the profile, service and characteristic (part of a service) that the packet is about. A standard UUID is 128 bits long – a substantial fraction of a packet that can only be 128 bytes long, and a lot of overhead when you have a tiny battery. So the standard has decreed that some services can use much shorter UUIDs, and they are not to be used for any other purpose. If you want to define your own shorter UUID then I guess you have to cross the palm of BLE with silver.
Anyone can define a new UUID here. They are generated randomly, and so there is a very small chance that two organizations can be allocated the same UUID.
A characteristic is a fancy term for a blob of data – the stuff we actually care about – a heart-rate measurement, or a range measurement for example. Unless you use the standard characteristic types, then each of these requires 128 bits to describe. Fortunately the standard defines a lot of useful characteristics that can be pressed into service.
Just to make things more confusing, there are standard profiles. But standard profiles don’t have a physical manifestation – they are just a collection of services that appear to have no data structure, or type associated with them, so I don’t even see how something that doesn’t ever exist can be standardized – but hey! I think the point of standard profiles is that a manufacturer can define their own private profile that allows their devices to communicate, and prevents others from inter-operating with them. So TS will probably need a custom profile.
TS has to define a custom profile, and one, or more custom services, and maybe some custom characteristics, all of which carry with them this onerous extra overhead that sucks the battery dry.
My plan is to build a collector service that remotely instructs a TS_ranger to turn on it’s ranger; to range the collector; and then to send the resulting range measurement back to the collector. This is about as simple a program as I can define that will require all parts of the software to fit into memory, and to cooperate. If I can’t make this work, then as I keep saying… “we are done”.
Now there are demo examples of how to setup a standard profile using standard services. I have compiled those examples and satisfied myself that they work OK. Now I am trying to incrementally change them so that they use a custom profile and custom service. It ought to be as simple as swapping some short numbers for some longer ones…
So my example is simple. I have a new stunning service called LEDBUTT. LEDBUTT implements one button and one LED. The objective is that when you press the button, a signal is send to a peer device which in turn sends a message back requesting the sender to illuminate it’s LED. Yup – a light switch! When you release the button, another pair of obvious messages are exchanged and the LED turns off.
How hard can this be… well a full week of fiddling has not brought me much closer to success.
I just read through the Nordic and RayTac manuals and discovered answers to what we were talking about today re. pin assignments for TS06.3
Here are the “standard” pin assignments for SPI, I2C and UART. There are two SPI controllers, two I2C controllers, and one UART controller. It is only possible to have three such “serial” devices active simultaneously. I discuss this more below under “Controller conflicts”
First let’s collect up all the standard pin assignments that the the Nordic software assumes.
Standard Pins Assignments
- either pin 23 or pin 17 for reference voltages.
- pins 9, 10, 18, 19, 20, 21, 22, or 23 as inputs.
Because 9 and 10 are used for the UART, and 20-23 for the SPI0, we are left with 17, 18 and 19.
#define AREF0 17u // ADC reference pin
#define AIN2 18u // ADC sample pin #1
#define AIN3 19u // ADC sample pin #2
It isn’t possible to have all five serial controllers active at the same time because they share address space.
- UART and 2 x SPI, or
- UART and 2 x I2C, or
- UART and 1 x SPI, and 1 x I2C.
Summary of suggested Nordic GPIO pin assignments
#define AREF0 17u // ADC reference pin
#define AIN2 18u // ADC sample pin #1
#define AIN3 19u // ADC sample pin #2
Another minor, yet significant step forward today. I have a trivial program echoing packets between my Android and the TS06.2. The same program (nRF UART) is allegedly available on the iPhone!
- The TS06 sets up all the BLE services, and then advertises its presence to the world.
- The Andoid is told to discover what’s out there so I can select it as the target for echoing packets.
- Then I type in a few words and send them – the TS06 echoes them back.
- Then I disconnect and reconnect to show it wasn’t a fluke (though it probably was!)
It continues to be a long haul. I have installed SDK 8.0.0 and have been slowly recompiling all my various bits of test code. Each little test program reveals some new gotcha that I didn’t quite understand, so I guess it’s kind of useful. Why recompile it all? I guess I want to be sure that everything works as before, in case there are hardware issues.
The good news is that the new SDK has bits of useful support code that I’m able to use, and thus throw away my own code which has a lot less testing.
Another day or so and I should be back to writing new code that matters.
The last two test programs went a lot faster than I anticipated. I’m done. So now I can get back to developing code again.