In Part 1 of the iTracker Series we saw the basic setup to get started on developing your own BLE peripheral based on the awesome RAK iTracker module.
If you missed it, here is the link: https://www.hackster.io/naresh-krish/getting-started-with-rak-itracker-module-and-arduino-ide-b78c0f
This tutorial we will learn about the BLE protocol, the nuances of sending data via BLE and to connect to peripherals.
HardwareThe iTracker module is a very unique board. It hosts a plethora of connectivity options like:
- Nb-IoT provided by the Quectel M35 chipset
- Bluetooth provided by the nordic nrf52832 chipset
- GPS provided by the Quectel L70-R chipset
Not just that a host of on board sensors are available as well.
With the Nrf52 chipset at its heart, the iTracker really is a versatile chip for development as well as providing diverse means of communicating the telemetry data from its sensors to the cloud.
BLE protocolBluetooth Low Energy (Bluetooth LE, BLE, formerly marketed as Bluetooth Smart[) is a wireless personal area network technology designed and marketed by the Bluetooth Special Interest Group (SIG) aimed at novel applications in the healthcare, fitness, beacons,[2] security, and home entertainment industries.Compared to Classic Bluetooth, Bluetooth Low Energy is intended to provide considerably reduced power consumption and cost while maintaining a similar communication range.
With the Adevnt of BLE 4.0 and above (4.0, 4.1 and later) the mobile devices have entered an era of low power devices that are capable of communicating sensors data to nearby devices over a secure as well as power efficiant channel, However the protocol also significantly differs in its implememntaton from Bluetooth 2.0.
Lets see in Detail.
Data Throughput and RangeThe modulation rate of the Bluetooth Low Energy radio is set by the specification at a constant 1Mbps (one mega bit per second). This, of course, is the theoretical upper limit. In practice, you can expect between 5-10 KB per second, depending on the limitations of the devices used. As far as range goes, BLE is focused on very short range communication. It’s possible to create and configure a BLE device that can reliably transmit data 30 meters or more line-of-sight, but a typical operating range is probably closer to 2 to 5 meters. Of course, the higher the range the more battery consumption, so take care when trying to tweak your device for higher range.
The BLE device architecture:BLE is organized in 3 major building blocks:
- Application
- Host
- Controller.
Application block is, as the name says, the user application which interfaces with the Bluetooth protocol stack. The Host covers the upper layers of the Bluetooth protocol stack. And the Controller covers the lower layers. The Host can communicate with the BLE module with an addition of something we call HCI - the Host Controller Interface. The purpose of HCI is, obviously, to interface the Controller with the Host, and this interface makes it possible to interface a wide range of Hosts with the controller.
BLE communicationA BLE device can talk to nearby devices in one of two ways:
- Broadcasting
- Connections.
Broadcasting is the act of sending data out to all the listening devices. When talking about Broadcasting, we define two roles: Broadcaster and Observer. The Broadcaster sends non connectable advertising packets periodically to anyone who is willing to accept them. While the Observer repeatedly scans the area in order to receive the packets. Then, when the Observer receives the Advertising packet, it can request the Scan Response Data. It is important to note that Broadcasting is the only way a device can transmit data to more than one peer at a time
.A Connection is a permanent, periodical data exchange of packets between two devices. The master (central device) scans the frequencies for connectable advertising packets, and when suitable, initiates a connection. Once the connection is established, the central device manages the timing and initiates the periodical data exchanges.
When talking about the BLE peripheral mode. We need to understand that advertising as a peripheral is done by establishing something called the GATT server internally in the chipset.
So whats a GATT server?
The Generic Attributes (GATT) define a hierarchical data structure that is exposed to connected Bluetooth Low Energy (LE) devices.
GATT profiles enable extensive innovation while still maintaining full interoperability with other Bluetooth® devices. The profile describes a use case, roles and general behaviors based on the GATT functionality. Services are collections of characteristics and relationships to other services that encapsulate the behavior of part of a device. This also includes hierarchy of services, characteristics and attributes used in the attribute server.
GATT is built on top of the Attribute Protocol (ATT) (see Bluetooth Core System Architecture for block diagram and explanations), which uses GATT data to define the way that two Bluetooth Low Energy devices send and receive standard messages. Note that GATT is not used in Bluetooth BR/EDR implementations, which use only adopted profiles.
Each GATT server profile consists of various services. These services can be visualized a s the exact same equivalent of a web servers web services. They relay important information when a client connects to them
The top level of the hierarchy is a profile, which is composed of one or more services necessary to fulfill a use case. A service is composed of characteristics or references to other services. A characteristic consists of a type (represented by a UUID), a value, a set of properties indicating the operations the characteristic supports and a set of permissions relating to security. It may also include one or more descriptors—metadata or configuration flags relating to the owning characteristic.
GATT groups these services to encapsulate the behavior of part of a device, and describes a use case, roles and general behaviors based on the GATT functionality. This framework defines procedures and formats of services and their characteristics, including discovering, reading, writing, notifying and indicating characteristics, as well as configuring the broadcast of characteristics.
So here is a brief description of the building bocks of a GATT server:
- GATT Services: Services are collections of characteristics and relationships to other services that encapsulate the behavior of part of a device.
- GATT Characteristics: Characteristics are defined attribute types that contain a single logical value. All Assigned Numbers values on this page are normative.
- GATT Descriptors: Descriptors are defined attributes that describe a characteristic value.
Once we understand this data hierarchy, the BLE peripheral library should be easy to understand.
Now lets dig into the library.
The library:What better way discuss the usage of the library with a nice little example sketch:
#include <SPI.h>
#include <BLEPeripheral.h>
//custom boards may override default pin definitions with BLEPeripheral(PIN_REQ, PIN_RDY, PIN_RST)
BLEPeripheral blePeripheral = BLEPeripheral();
// uuid's can be:
// 16-bit: "ffff"
// 128-bit: "19b10010e8f2537e4f6cd104768a1214" (dashed format also supported)
// create one or more services
BLEService service = BLEService("fff0");
// create one or more characteristics
BLECharCharacteristic characteristic = BLECharCharacteristic("fff1", BLERead | BLEWrite);
// create one or more descriptors (optional)
BLEDescriptor descriptor = BLEDescriptor("2901", "value");
void setup() {
Serial.begin(115200);
#if defined (__AVR_ATmega32U4__)
delay(5000); //5 seconds delay for enabling to see the start up comments on the serial board
#endif
blePeripheral.setLocalName("local-name"); // optional
blePeripheral.setAdvertisedServiceUuid(service.uuid()); // optional
// add attributes (services, characteristics, descriptors) to peripheral
blePeripheral.addAttribute(service);
blePeripheral.addAttribute(characteristic);
blePeripheral.addAttribute(descriptor);
// set initial value
characteristic.setValue(0);
// begin initialization
blePeripheral.begin();
}
void loop() {
BLECentral central = blePeripheral.central();
if (central) {
// central connected to peripheral
Serial.print(F("Connected to central: "));
Serial.println(central.address());
while (central.connected()) {
// central still connected to peripheral
if (characteristic.written()) {
// central wrote new value to characteristic
Serial.println(characteristic.value(), DEC);
// set value on characteristic
characteristic.setValue(5);
}
}
// central disconnected
Serial.print(F("Disconnected from central: "));
Serial.println(central.address());
}
}
Now into the code:
#include <SPI.h>
#include <BLEPeripheral.h>
This tells the Arduino IDE to install the BLE Peripheral library the SPI dependencies into the sketch.
BLEService service = BLEService("fff0");\
The Ble Service is what we saw earlier as the GATT service object that will contain the characteristics and descriptors. We need to give the service a name, here it is fff0.
BLECharCharacteristic characteristic = BLECharCharacteristic("fff1", BLERead | BLEWrite);
Here we create a BLE charcateristics, which allows both read and write. Charcateristics can also be read-only to protect users from accidently overwriting the contents. this can be true for sensors readinf characteristics as they are only read and not written to.
BLEDescriptor descriptor = BLEDescriptor("2901", "value");
Here we create a descripor object ot be added to the characteristic.
blePeripheral.setLocalName("local-name"); // optional
blePeripheral.setAdvertisedServiceUuid(service.uuid()); // optional
Here we initiate the bleperiheral with a local name which will be advertised and add the service UUID that we created a while back.
blePeripheral.addAttribute(service);
blePeripheral.addAttribute(characteristic);
blePeripheral.addAttribute(descriptor);
This adds the service, the characteristcis and the descirptor to the BLE peripheral:
characteristic.setValue(0);
Here we set an initial value for the characteristics, this is essential as you don't want empty values to be returned from a sensors which doesn't make any sense:
blePeripheral.begin();
Here is the where the ble magic happens and all our setup gets reflected in the NRF52 chipset.
The loop function is pretty straightforward and helps us output the debug messages to the Console. Also it shows how to dynamically set the characteristics value using the setValue function like so:
characteristic.setValue(5);
Basically one can set any value (read from a sensors or from a digital pin) to this function. So the charcateristics value would dynamically change.
Now run the program above on the itracker using the JLink device and then on android or iOS open the nrf toolbox/connect app and you should see your device discovered and showing the various services and chacateristics we defined in our code.
NRF52 Serial messages via JTAGFor those who try to debug by printing messages on to the usb console. The Serial.println function would not cut it through. Instead, we may need to use a special kind of terminal called the Real time terminal API
Debugging with Real Time TerminalAdding RTT files to projectAs with the previous tutorials, this tutorial will be based on the ble_app_uart project, but any other project can be used.
- Download the zip file with the required RTT files as linked, and unzip it
- Include SEGGER_RTT.h to the project by writing #include "SEGGER_RTT.h" near the top of the *.ino file in the project
You should now be able to send simple strings through the RTT interface. In the main function in the main file, add the following line right before the main loop:
SEGGER_RTT_WriteString(0, "Hello World!\n");
The first argument is which channel to write the text to. You can read more about channels on the SEGGER webapge, but the default channel is 0. Compile the code to make sure everything went ok.
Opening a Real Time TerminalNow that the code is sending output to our RTT, we have to be able to read it. There are several ways this can be done, as summarized on the segger webpage. The easiest way is to use the J-Link RTT Viewer that comes with the J-link software package.
- Open J-Link RTT Viewer. You will then see the image below. If you have more than one device connected, check "Serial no." and enter the serial number of the device you wish to connect to.
- Click Ok. The screen below will then appear.
Now, load the code you compiled above onto your device, and you should see the text "Hello World!" appear. Notice if you open Termite you will also see the text "Start.." which is still printed on the serial port as before. You can now use the ble_app_uart project as intended, but also send text to RTT in order to debug. Keep in mind that SEGGER_RTT_WriteString() is much faster than printf, so you can safely call this function without it affecting the real time properties of your application.
Sending text to targetYou can also use RTT to send text to your device, as an alternative to UART. Modify the main loop in the ble_app_uart project so it looks like below. Remember to also include nrf_delay.h to use the delay function.
void setup(){
char c = 0;
for (;;)
{
c = SEGGER_RTT_WaitKey(); // will block until data is available
if(c == 'r'){
SEGGER_RTT_WriteString(0, "Resetting..\n");
nrf_delay_ms(1000);
sd_nvic_SystemReset();
}
//power_manage();
}
}
- Compile and flash the code onto the device
- In RTT Viewer, go to Input -> Sending and click Send on Enter. (Otherwise it sends on every key you press, which can be annoying)
- Write an 'r' in the textfield and hit enter. You should now see the board resetting.
There you go debug messages are not available to you over the JLink SWD interface as well.
In Part 3 we will see the ultimate use of the itracker...TO SENSE the Environment. We will see how to get the sensors working and the libraries to use to read data from the sensors.
Comments