If you are developing a small, low-power peripheral, the odds are good that you are considering Bluetooth as your communication layer.
As the standard has evolved, Bluetooth-connected devices have proliferated. Hardware modules have been introduced that provide a complete SOC (system on chip) for almost any peripheral design. The Nordic nrf52840 is a good example. The system core is a 32-bit processor and multi-protocol Bluetooth radio. This core is surrounded by every interface you might need; GPIO, PWM, USB, SPI, I2S, and many more.
The downside to this powerful SOC, and others, is complexity. There is a significant learning curve and development time required to create and integrate the necessary embedded software for these SOCs.
For some of the most common types of peripherals, however, there are simpler alternatives. If your peripheral is primarily a remote sensor or a remote control, with a limited number of channels (analog and digital), you can probably meet your goals with the RN487x module manufactured by Microchip.
This family of two Bluetooth modules provides multiple, concurrent, bi-directional digital and analog channels. The module is tailored to specific designs just by storing a little bit of configuration in the module NVRAM. Despite the simplicity, the module still conforms to the BLE (Bluetooth low-energy) standard, so it is compatible with the newest Bluetooth clients, such as modern smartphones.
In this series, we will demonstrate using the RN487x module. We will create a functional Bluetooth-connected peripheral for each of the four tasks; a digital sensor, a digital control, an analog sensor, and an analog control. Each example includes a nominal circuit, and the configuration necessary on the RN487x. A simple application script for exercising the peripheral is also provided. The interface points in the application script are well documented; maximizing the potential to incorporate these peripherals as subsystems in larger designs.
Let’s look at a graphical breakdown for the ‘analog sensor’ example, with key interfaces in red.
A representation of a Linux system communicating with a peripheral
There are three system elements that we will create. For the peripheral, we must create hardware that provides our sensor signal; the transducer in this example. It generates a varying analog voltage. For the user, we will create an application to present the data; the Python script in this example.
The script must use the GATT API to transfer information to and from the peripheral. We will also need to create some configuration in the RN487x module. This configuration will control the flow of data between the application and the hardware.
There are two modules in the RN487x family; the RN4870 and the RN4871. They differ in the number and type of pins provided for I/O. You can find these constraints in the datasheet and user guide, but it is somewhat scattered. Below is a reorganization of the pin budget for the two chips. This arrangement should make it easier to see which chip you need for your peripheral.
There are three I/O pin types available on the c modules:
|ADC||Analog input: An input signal level is converted to a numeric range.|
|PWM||Analog output: A pair of numbers is converted to a square wave. The numbers control the frequency and duty-cycle of the square wave.|
|DIO||Digital input or output: For digital input, the high or low signal level represents 1 or 0 respectively. Vice-versa for digital output.|
And here are the pin budgets for the two modules:
Image from the RN487x datasheet
Image from the RN487x datasheet
These tables should make it clear that if you do not need PWM, and you only need to manage one or two signals, use the 4871. This will save resources. If you need PWM, or if you want to manage more than two signals, then you need the 4870. For our analog sensor, we have only one analog input so the 4871 would be sufficient. We would connect the signal to the P1_2 pin of the module.
GATT (Generic Attribute) Profile Layer of the BLE Protocol Stack
We’re almost ready to implement some real examples. But in order to write the user application, we need a more precise understanding of the api we will use when we talk to the peripheral.
All Bluetooth Low Energy devices use the Generic Attribute (GATT) Profile for exchange of structured data. In this model, the peripheral is organized as a server that holds a simple database. The database in turn holds a number of variables that represent the useful data. Applications such as our Python script are organized as clients that use the GATT API to make name-based queries into the database. The API can be used to read values from the database, and to write values into the database.
As a preview, here are two useful GATT API method calls in Python:
gatt_rq.connect() gatt_rq.write_by_handle(vh_light, str(bytearray()))
The first is used to establish a connection to the peripheral. The second writes some values to the database; these values are immediately expressed as digital outputs on the peripheral. The first argument to the ‘write’ method is a handle that is specific to the digital output or outputs we want to control. There are just a few more lines necessary for a functionally complete example.
This API is exposed as part of Bluetooth services in iOS, Android, Windows, and Linux. The examples in this article are written in Python, and will run in many common Linux distributions.
The ‘vh_handle’ parameter in the script method above leads us to the final element in this design pattern. The parameter is a reference in the software for a specific physical-level signal on the peripheral. But how is this data path completed? There are two pieces of configuration in the NVRAM of the RN487x that will do this; characteristic definition and pin binding.
- Characteristic definition: Commands that allocate space for values in the database, and that give each value a unique identifier for reference by client applications.
- Pin binding: Scripts that transform physical signals into database values, and vice-versa.
RN487x configuration is loaded into the module with a custom command language. See the appendix for a general guide on making a console connection to the module, and on issuing configuration commands.
After we’ve issued these commands, the non-volatile configuration within the RN487x will look something like this:
Note that all the data values exist within a two-level hierarchy of ‘services’ and ‘characteristics’. This organization is more than we need, but becomes useful for complex systems where multiple sensors and controls would exist within logically separate services.
This concludes part one of our three-part series on the RN487x module. Parts 2 and 3 will utilize the same design pattern to create a digital input, a digital control, an analog sensor, and an analog control.
Part 4 also includes some topics for further study that apply to all the examples.
Below, you will find an Appendix of information for configuring your modules and using them before each of the project descriptions that will follow in other articles.
Using a Console Connection to Configure the RN487x
To configure the RN487x, first supply power to the module and establish a serial connection between the module and a workstation. Each of our demonstration circuits has a 3-pin ‘prog’ jumper that exposes the necessary RX, TX, and GND signals. If you are unsure of how to make this connection to your workstation, an excellent guide is available here.
Start a terminal emulator on your workstation. One emulator that is particularly well suited and widely available is the ‘miniterm’ utility that is included in most Python environments. In a workstation shell, it can be started like this:
python -m serial.tools.miniterm --eol LF /dev/ttyUSB0 115200
This example shows a port name (/dev/ttyUSB0) that is appropriate for a Linux system. The form of the port name will be different for other operating systems. The default baud rate for the module is 115200 and there is little reason to change it.
The RN487x module is in DATA mode by default. We need COMMAND mode. This mode is activated by typing three dollar-sign characters (‘$$$’) in the terminal emulator application. You will not see the characters in the terminal window, but the result should be a command prompt returned by the RN4871:
Try pressing the 'd' key followed by 'enter' key. You should see something like this as a result:
CMD> BTA=D88039F80080 Name=RN_BLE Connected=no Authen=2 Features=0000 Services=00 CMD>
This is a minimal description of the module state. Notice that the command interpreter did not display the character you typed (‘d’). You should turn on 'echo' to fix this. Do this by pressing the '+' key followed by the 'enter' key. The device should respond like this:
CMD> ECHO ON CMD>
You are now at a point where you can complete various configuration tasks described in the rest of this project. Creating a service, for example is accomplished by simply typing a complete ‘PS’ command in one line, followed by the ‘enter’ key. Ex:
CMD> PS,59c88760536411e7b114b2f933d5fe66 AOK
Each of the project configurations also require that a multi-line script be committed to the module NVRAM. This type of multi-line command requires a bit more explanation. You begin script entry with a ‘WW’ command, followed by ‘enter’. Then type each script line. Each script line, including the last line, is also terminated with the ‘enter’ key.
Finally, you commit the script by pressing the ‘esc’ key. Ex:
CMD> ww @CONN |O,08,72 AOK CMD>
Common Initialization of the Module
If you have used an RN487x module for one of the examples in this project, or if you have used the module for some entirely different purpose, and you are about to configure the module for another example, it is possible that the module has some configuration that will conflict with the next intended use.
The following steps should be taken before every example in this project:
- Erase any existing script.
- Enable script processing.
- Erase any existing service/characteristic definition.
- Remove any special functions from the pin we use (P1_2, index 0A).
- Reboot the module.
Use the previous appendix section to reach the module command shell. Then use the following command sequence to accomplish the necessary initialization:
CMD> WC AOK CMD> SR,0040 AOK CMD> PZ AOK CMD> SW,0A,00 AOK CMD> R,1 Rebooting
Linux Setup Required for Running Example Python Scripts
The system running these scripts will of course need hardware that supports Bluetooth Low Energy (BLE). BLE was introduced in version 4, so if your Bluetooth hardware specifies a version below this, it will probably not work for these examples.
This setup has been verified for Debian 10. It should be applicable for Debian-based distributions like Ubuntu. There are two components directly accessed by these examples that are not installed by default in Debian 10;
- The Python module for accessing the Bluetooth stack. This component is available through the package management system, and is named ‘python-bluez’.
- The Python module specifically for Bluetooth GATT calls; ‘gattlib’. This component is available from the Python official package index (via the ‘pip’ utility).
The ‘pip’ installation of ‘gattlib’ builds an ELF library. This process requires some tools and libraries, too.
Putting all of this together, a list of packages must be requested from the system package manager, followed by a single module request to ‘pip’. A script to complete these actions has been provided here. If you choose to run the script, you will need to give it ‘execute’ permissions after download.