Projects Research & Design Hub

Bluetooth-Enabled ECG Monitor

Written by Brian Millier

Using the Cypress PSoC 6 MCU

Brian has written articles in the past about projects that use Cypress Semiconductor’s PSoC MCUs, including his most recent piece about the variable frequency drive project he built using the SoC5LP MCU. This month, he explores the latest offerings from this MCU family, the PSoC6 5LP MCU. In this project article, Brian selects the Cypress CY8CPROTO-063-BLE to build a Bluetooth-enabled ECG monitor.

In my last column, I described a Variable Frequency Drive project I built, using Cypress Semiconductor’s PSoC 5LP microcontroller (MCU). Although I had used PSoC MCUs in the past, I was quite impressed by the advances they had made in both the hardware and in the Creator 4.2 application that you use to develop code for the PSoC family. I decided to continue to explore this family of devices while things were still fresh in my mind.

I looked at the latest offerings in the PSoC family—the PSoC6. Earlier generations of the PSoC family were each based on a specific central processing unit, beginning with a very limited custom CPU used in the PSoC1 family, advancing to an 8051 derivative (PSoC3), and then finally an Arm Cortex M3 in the PSoC 5LP. What differentiates each PSoC device in a given generation are the number and scope of the custom analog and digital blocks contained in that device.

The PSoC 6 is the first PSoC generation to contain any wireless connectivity. The higher-end PSoC 63/64 models have Bluetooth Low Energy (BLE) functionality built in. The PSoC 60,61 and 62 contain a similar CPU, but no BLE function. All devices in the PSoC6 line are dual core, containing both a Cortex M4 and a low-power Cortex M0+. If you check out the Cypress development board offerings for the PSoC6, it appears that Wi-Fi is also supported in some PSoC 6 devices. This is not true, however. The PSoC 6 development boards that feature Wi-Fi do so via a separate CYW4343W Wi-Fi/BT daughterboard. I wasn’t interested in such a development board, because I generally use the Espressif Systems ESP8266 or ESP32 modules for anything I do that requires Wi-Fi.

In the past, I always liked the way that Cypress designed its development boards. It has the usual assortment of larger boards containing a variety of peripherals, including displays and cap-sense buttons. However, Cypress also made small modules containing only the bare-essential components, in DIP-style packages on 0.1″ centers. These suit my construction practices perfectly. As a bonus, such boards for the PSoC3,4 and 5LP families (which I had used in the past), were priced between $5 and $15. These are obviously sold below cost, since the parts on the snap-off programmer alone (included with these modules), would cost more.


I chose Cypress’s low-cost CY8CPROTO-063-BLE for this project. The daughterboard containing the PSoC6 MCU/BLE antenna, (right), and the snap-off programmer (left) are shown in Figure 1.

FIGURE 1 – The Cypress CY8CPROTO-063-BLE prototype board that I used for this project. Note the snap-off programmer on the left.

I’ve created many Wi-Fi projects in the past, using the ESP8266 or ESP32 modules and compatible Arduino IDE/libraries. Most of the complexity of the Wi-Fi protocol is hidden by the Arduino Wi-Fi library, and it’s not that hard to get a project up and running (at least at the security level imposed by a WPA2 connection to a home wireless router). Programming the MCU firmware is a lot more complicated in the case of Bluetooth.

The simplest approach to a Bluetooth-enabled peripheral uses a Bluetooth module containing a standard UART interface. I’ve used the Adafruit “Bluetooth LE UART Friend” module. It’s easy to use because Adafruit supplies not only a sample Arduino sketch written for it, but also matching iOS and Android smartphone apps.

However, once you go beyond a simple BLE-to-UART bridge device, things become much more complicated. BLE is loosely based on a Server/Client concept, though what you might normally consider a server may not be how it is defined in the BLE environment. The basic concept is that one BLE device can pair up to another, completely unknown BLE device, determine which of the Bluetooth SIG profiles that BLE device emulates, and then communicate measurements/commands back and forth.

The BLE devices don’t need advance knowledge of anything specific about each other, since all the relevant measurement/control parameters are defined in whatever Bluetooth profile(s) the device is emulating. For example, a smartphone BLE app that monitors a person’s heart rate, should work properly regardless of what brand of heart rate monitor it pairs up with. While this works in theory, I suspect that manufacturers often tailor the BLE profiles enough so that their smartphone apps only work with their own hardware devices.

While this capability is very useful, it results in a complicated communications protocol—much more complex than what you would come up with, if you were designing a custom device for a specific, dedicated purpose.

I generally peruse datasheets, specifications, and so on before getting too involved in a project. I examined the Bluetooth SIG documentation early on, but found it hard to understand. I’m quite familiar with Wi-Fi IP #s, MAC #s and SSIDs, but the many UUID numbers involved in BLE are much more elaborate. They’re basically 128-bit values, but are expressed as long, hyphenated ASCII strings: not at all like the “standard” notations used for IP and MAC addresses. Furthermore, the common BLE SIG profiles use “shortened” 16-bit values, which are concatenated with a common base value to provide the full 128-bits. You will have to get used to typing these long UUIDs into your programs without errors, or nothing will work!


Advertise Here

The PSoC Creator 4.2 program contains support, in the form of a component configuration “wizard,” to help you write a BLE-enabled application. If you have used the PSoC Creator application with earlier PSoC devices, you’ll be familiar with dragging the required hardware “components” onto your “schematic” workspace (the TopDesign tab). Then, when you invoke the Build -> Generate Application option, Creator will add the various “.h” and “.c” files needed to implement comprehensive APIs for each hardware component that you added to your design. To be clear, these hardware components are the built-in peripheral blocks contained in the PSoC device, itself. You don’t have to figure out what driver files are needed, since Creator software adds them all for you.

Double-clicking on a “component” brings up a graphical configuration “wizard,” used to define the initial configuration of the hardware component. This “wizard” is as simple or complex as needed to configure the hardware component that it serves.

To add the BLE function, the same process is followed. You first add the BLE component from the Component Catalog window to the right of the Creator screen (in the Communications folder). When you double-click on the BLE component, a complex configuration “wizard” appears (Figure 2). In the “General” tab, you select whether your PSoC device will be a Peripheral, Central, Broadcaster or Observer. For this project, a Peripheral is the right choice.

FIGURE 2 – The main page of the BLE component’s configuration wizard, part of Cypress’ Creator IDE application. Many of the important settings are exposed in the GATT Settings tab.

Here you also decide how you want the BLE functionality to be implemented—either using both the CM0+ and CM4 Arm cores of the PSoC, or just one of them. I readily admit I don’t know all the pros and cons of this choice, apart from knowing that for low-power applications, it makes sense to use the CM0+ core to handle the low-level BLE functions. The CM4 core is powered up only to handle application-specific code, when needed. I found that the Cypress sample programs generally used both processors.

Most of the BLE configuration is done using the GATT tab. This is where you specify what BLE SIG profile you want the device to emulate. You can either pick one of the standard profiles (Health Thermometer, Heart Rate, Cycling Speed, and so on), or define your own custom one.

This is where I found things got complicated. It seems very difficult to come up with a custom profile, yourself, unless you are well-versed in BLE standards and other subjects. Speaking as a BLE novice, it’s my opinion that you stand virtually no chance of developing your own custom BLE device, if you try to design both the device hardware/software and the smartphone app by yourself, from scratch. Instead, I followed this procedure:

1) Program the PSoC 6 with one of the sample BLE programs that comes with the Creator software package. From the Creator “Start” page, this is done by clicking on “Find Code Example.”
2) Test for connectivity/functionality using Cypress’s CySmart application (iOS app for iPhone/iPad, Android app or PC application).
3) Examine the PSoC sample program closely, and modify it to perform the task you have in mind. At this stage, you may or may not be able to use the CySmart app for testing, if you are not using a custom profile or one of the standard profiles for which Cypress has provided a sample.
4) Write a custom smartphone app to handle the required function(s).


Advertise Here

To be honest, I didn’t have a specific project in mind at the outset. I just wanted to learn how to use the PSoC 6 in a BLE application. Even starting with this “clean slate,” I didn’t find many of the Cypress BLE sample programs to be very relevant to me. I settled on the “PSoC 6 BLE Multi-Slave” sample program. This implements a BLE multi-slave functionality containing the following services:

1) Device Information Service
2) Health Thermometer Service
3) Custom service controlling an RGB LED
4) Custom service performing a 128-bit read/write
5) Custom notification service.

Although this sample program is named “Multi-Slave,” it actually implements a multi-master, multi-slave device, acting as a Peripheral and containing the above five GATT servers. Among those servers were one that sent out data (Health Thermometer) and one that received data (RGB LED control). Those were the ones I figured I could modify to fit my own tasks, regardless of whether those tasks were monitoring data or controlling something connected to the PSoC 6. I didn’t have any use for either the device information service or the custom notification service. While the custom 128-bit read/write service looked useful, I could only figure out how to write the 128-bit data, not read it, so I left it alone.

When I later decided that I wanted to build a BLE-enabled ECG (electrocardiogram) monitor, I knew I’d be sending the ECG data from the PSoC 6 device, and there would be no need to control anything on the PSoC 6 from the iPad app.

I chose to modify the Health Thermometer Service. Doing so would allow me to monitor the values sent from my PSoC 6 firmware using the CySmart iOS app provided by Cypress. That is, I wouldn’t need to have my own iOS app ready to go in order to test the PSoC6 firmware I was trying to write.


Advertise Here

The “.c” and “.h” files from the Cypress “multi-slave” sample are shown in Figure 3. Many more files are needed for this program than those shown here. Most of the other files are generated automatically by the Creator application (for the specific hardware components that you have added to your “schematic”). These are the files I needed to work with to customize the “multi-slave” sample for my own purposes.

FIGURE 3 – Many files are generated after you design the PSoC6 “hardware” aspects of your project and click on “Build Application.” The ones shown here are a subset of those, and contain the files where I made modifications/additions to the sample program to match my project.

I added the “ECG.h” and “ECG.c” files, myself. Actually, the sample program originally contained “temperature.h” and “temperature.c” files. I renamed these two files, and modified them to replace the original code, which read a thermistor via the PSoC6 SAR ADC, with code that measured the voltages coming out of the ECG amplifier module. In both cases, the ADC used the 3.3 V Vcc as a reference, so no change was needed there. However, Cypress used what they call “Double Sample Correlation” to measure both the voltage across the thermistor and across a 10 kΩ 1% reference resistor, to determine the thermistor resistance. This value was then converted to temperature using the Steinhart-Hart equation.

I changed this code to take just one ADC reading of the voltage directly from the ECG amplifier. Note that the code in the “ECG.c” file merely takes an ECG reading. It must be called at a specific sample rate to be useful. That is done in the “ble_application.c” file. Specifically, in this file, the function, “ble_ProcessEvent” is a loop that constantly handles BLE events. When I say I take one ECG reading, the “ADC_GetResult16” function that I use takes 256 samples and averages them. The PSoC6’s SAR ADC is very fast, so in the ADC setup wizard, the ADC is configured to take an average of 256 settings, which minimizes noise.

Within that loop, the original Health Thermometer readings were taken at a 4 Hz rate, triggered by the PSoC’s watchdog timer. To be useful, ECG readings should be taken 30+ times per second. I added a PWM component to my “schematic,” set for a 40 Hz rate. In past PSoC projects, I would merely wire an “Interrupt” component to the PWM output. Creator would then generate all the needed code for this interrupt, including an “isr.c” file–where you would add the code you wanted executed when the interrupt occurred. This method no longer works with PSoC6 BLE applications. For PSoC6, Cypress now uses the Peripheral Driver Library, which is part of what they call “middleware,” because it includes drivers from other sources, including RTOS and emWin. I couldn’t quickly figure out the new way to handle a PWM interrupt, so I wired the PWM output to Port 10.5, and tied that to Port 10.4 (set as an input). I could poll the state of the P10.4 pin, giving me a way to pace the ECG readings at a 40 Hz rate. When I get more time, I’ll try to figure out how the PSoC6 firmware handles interrupts.

The BLE standard Health Thermometer Service is defined to output the temperature as an IEEE-11073 format floating-point number. However, the PSoC’s “C” compiler uses the standard IEEE-754 single-precision format. Therefore, a format conversion was done in the original sample program. For my purposes, I chose to take four, sequential 8-bit ECG readings and pack them into the 4-byte IEEE-753 floating point variable originally used for the temperature value. I did this so that only one-quarter as many BLE packets needed to be transmitted.

When I later wrote the iOS app, it was only possible to receive and graph the ECG data at a 40 Hz sample rate, by sending the data in this “packed” format. With only one ECG value per BLE packet, my iOS program routinely failed to collect all the data, when pushed up to the 40 Hz rate. In Figure 4, you can see the iOS app running. Here, the PSoC6 is sending out a triangle waveform via BLE. The data loss without data-packing, mentioned above, was clearly visible here, when it occurred. After implementing the 4 sample/BLE packet protocol, the data were received properly, as shown here.

FIGURE 4 – My techBASIC program running. At this stage, the PSoC6 was generating a triangle “test” wave, rather than an actual ECG signal.

In summary, the ADC reading of the ECG waveform takes place in the “ECG.C” file. The processing of BLE events, including the 40 Hz sampling of the ECG signal and its transmission, takes place in the “ble_application.c” file. The entire, complex, BLE communications protocol is handled transparently by routines in other files, most of which were added automatically by the Creator application when the BLE component was added to the “schematic.”

When I first ordered my CY8CPROTO-063-BLE board, I considered also purchasing the Cypress CySmart USB dongle for testing the BLE link on my PC. However, Cypress also provides an iOS app that I could use on my iPad, so I didn’t think I would need the dongle. It turns out that the CySmart iOS app can handle the various BLE profiles used by some, but not all, of their sample programs, nor can it handle a custom profile that you design yourself. You would need the CySmart USB dongle for this. I have since purchased the dongle, but unfortunately, I didn’t have it while designing this project.

I first downloaded the Cypress multi-slave firmware to the PSoC6, and tested it using the CySmart iOS app on my iPad. All of the BLE Services included in this sample showed up in the CySmart app and worked properly. I didn’t have the thermistor/feed resistor hooked up, as would have been the case on the Cypress development board to which this sample is targeted. As a result, I got random temperature readings that varied at the 250 ms watchdog period.

As mentioned earlier, I had decided to modify the standard Health Thermometer profile to handle the ECG data. When I started making changes to the sample program to read the ECG signal, I initially didn’t pack 4 ECG samples into one BLE packet. I also did not trigger the ADC at the 40 Hz rate, but rather, at the much slower rate of 4 Hz used in the original sample. I also sent the ECG readings to the PSoC6’s UART Tx port for debug purposes. Doing it this way allowed me to see the “Raw” ADC readings taken from the ECG amplifier, and to monitor them in the Health Thermometer section of the iOS CySmart app. Whatever the ADC value was, it showed up identically in both the UART output stream and in the CySmart app’s Health Thermometer window.

The Cypress multi-slave sample program is written to send out many debug status messages during the various phases of a BLE link connection. By default, however, these messages are turned off. I would not have known how to enable them, had I not watched the “PSoC6 101” series of YouTube videos by Cypress’s Alan Hawse (which I thoroughly recommend). You must edit the uart_debug.h file and change line 75 to read:

#define UART_DEBUG_ENABLE (true)

Once done, you can use statements, such as the following in your code for debugging:

DebugPrintf(“S2= %d \r\n”,temperature);

Once I had my program working properly with the CySmart app’s Health Thermometer window, I was free to change my code to:

  • 1) Pack 4 ECG samples per BLE packet.
  • 2) Increase the ADC sample rate to 40 Hz.
  • 3) Scale the ADC values down to 8-bits.

The next step was to write a custom iOS program for my iPad, to receive and graph the ECG data.

Writing native apps for iOS devices is not an easy job, unless you plan to do it regularly. Native apps must be written in Apple’s Objective C or Swift languages, and the Xcode development platform for these languages will only run on a Mac computer. I have an older Mac Mini as a spare computer, but I believe its OS is too old to run current versions of Xcode. Also, you must register as an Apple developer to be able to download your app to the iOS device during testing. I had checked out Objective C several years back, and found it to be hard to work with. Granted, I wasn’t as experienced with the C language then as I am now.

As previously noted, if your BLE application is not too specialized, an Adafruit BLE “Friend” module, which acts as a BLE-to-UART bridge, might be your best bet. Adafruit provides a “generic” iOS app called Bluefruit, which communicates with their modules. Another choice for Adafruit BLE “Friend” modules is an iOS app called Blue Remote (Figure 5). This app acts like a TV remote control, and sends out an ASCII string descriptor via BLE for each button pushed. The data communication is unidirectional—from the iOS device to your BLE “Friend” module.

FIGURE 5 – While not connected with this project, this is a screenshot of the Blue Remote iOS app. It can be used for certain BLE projects, without the need for you to write your own custom IOS app.

Getting an iOS application to communicate with the PSoC 6 BLE module was more involved. I had ruled out writing a native app in either Objective C or Swift, for the reasons mentioned above. The Adafruit apps only work with their BLE “Friend” modules. Some software companies produce development software that allows you to write iOS apps without using Apple’s development languages. I looked at them briefly, but felt they wouldn’t be a good match for my PSoC 6 BLE device.

I had earlier installed an iOS app called techBASIC, by Byte Works, on my iPad. It consists of a BASIC interpreter that runs on the iOS device. You write your application in BASIC, and run this application when you need to use it. techBASIC contains all the expected BASIC functions, and provides high-level APIs to access most of the I/O devices found in iPhones and iPads. You can access an iOS device’s WiFi connection, Bluetooth LE, the various gyro, accelerometer and magnetometer sensors, and the display and touchscreen features, to name a few. Due to the many high-level functions available, it’s possible to write powerful graphical programs with modest amounts of coding. techBASIC runs on iOS version 8 and above. I am still using iOS 9, so I haven’t tried it on newer iOS versions.

There are a few caveats. When you open this app, you are presented with a list of sample programs provided with the app plus those that you have written (Figure 6). You must choose the applicable app to start it running. If you’re both the developer and end-user, this is straightforward, but other users wouldn’t find it quite as transparent as a “dedicated” app. Also, there doesn’t appear to be a way either to protect your code from being modified by the end-user, or to prevent end users from viewing the source code.

FIGURE 6 – The opening screen for the techBASIC IOS app. The user must choose a program from the folders listed at the left. Once selected, the program will run automatically.

To overcome these limitations, Byte Works sells a techBASIC App Builder program that converts your techBASIC source code into a form that Apple’s Xcode development platform can compile. This application—just like the Xcode development software—must be run on a Mac computer, and you must be registered as an Apple developer. I don’t have any experience with this particular program.

I followed the same procedure for the iOS app that I used in writing the PSoC 6 BLE firmware. That is, I used one of techBASIC’s sample programs (designed for the Texas Instruments SensorTag BLE modules), and incrementally began modifying it to match the UUIDs and data structure present in my PSoC 6 project’s firmware. I must admit that even though the documentation that comes with techBASIC is very good, the BLE functionality is quite complex. I doubt I could have written a working app, had I been forced to write it from scratch.

Since techBASIC runs on iOS devices, even when using an iPad there is a limited amount of screen space to run a development IDE. Byte Works gets around this by providing three discrete “views”:

  • 1) Source View—where you compose/edit your source code.
  • 2) Console View—basically for debugging. Anything that your program outputs using the PRINT command is shown in the console view.
  • 3) Graphics View—the graphic user interface that the user interacts with during program execution.

During program execution, you can switch between the Graphics and Console views. Thus, if you sprinkle your program with PRINT statements at strategic spots, you can debug your program easily by switching to the Console view to see how things are going. When I was modifying one of the sample programs to suit this PSoC6 project, I placed a lot of PRINT statements in the source code, to see how the BLE discovery/pairing process was proceeding.

Before I describe my source code, I’ll mention that techBASIC’s Bluetooth API makes heavy use of “callback” functions. These functions are not a part of your program’s “main” loop, but instead are invoked automatically by techBASIC when specific BLE messages are received by the iOS device. Since such messages can arrive at random times, it makes your program is much easier to write if you don’t have to check for all the different types of BLE messages from within your program’s main loop. Instead, you add your application-specific code to the body of the various callback functions, and techBASIC acts as a “traffic cop” by sending the numerous BLE messages to the correct callback function.

In simple terms, a BLE program must:

  • 1) Perform a “scan” to discover any BLE devices within range.
  • 2) Determine what BLE profile(s) this device supports.
  • 3) Tell this device to start sending the desired data (if it is not broadcasting that data by default)
  • 4) Add your own code to the appropriate callback function(s), to receive and process that data.

techBASIC supports breakpoints. Even if you are not using breakpoints, you should be aware of them, because it’s easy to place them by mistake. In the Source View, there is a narrow pale blue region to the left of your code window. If you touch this area of the screen, a blue right-arrow will appear, and a breakpoint will be placed at that line. Program execution will halt when this line is reached. You can remove this breakpoint by tapping on the blue arrow.

techBASIC’s program structure is somewhat different from what you might expect if you program in C. There you would expect a “main” function that generally contains an endless loop. That loop will either invoke actions or monitor something continuously. Alternately, it could contain an empty loop, leaving callback functions to do the actual work. Similarly, in the Arduino environment, you would have a “setup” function to initialize peripherals and such, and a “loop” function to handle ongoing events.

In techBASIC, you define all your variables and objects at the top of the program, and place your initialization code next. There is no “loop” function as such. Your program’s various functions immediately follow. They are generally callback-type functions or functions that are themselves called by those callback functions.

The initialization portion of my program consists of only a few lines:

services(1) =”1809”: ! Health thermometer
PRINT “Program Start”

First, I define the profile name that I’m expecting the PSoC 6 project to support. I use the standard BLE “Health Thermometer” profile used in a PSoC 6 BLE sample program, modified to handle my ECG data stream. The “serverFound” flag variable is initialized to zero. Once a BLE connection is made to a device with the name “PSoC 6,” this will be set to 1, and other BLE callback functions will be enabled.

BLE is the class supporting BLE operations, and the “startBLE” function enables it. The “startScan” function scans for BLE devices within range, and the UUID of the discovered device is stored in the “uuid” string variable.

The SetupGUI function defines and places the various buttons, labels and other graphics elements on the graphics screen. The GUI for this project is pretty basic—a graphics area in which to plot the ECG data, a QUIT button to exit the program, and a “progress bar” indicating the status of the various stages of the BLE scan/discovery process.

Beyond the initialization functions described above, all other program activity is handled by callback functions, which are triggered by the arrival of BLE data from the PSoC 6 BLE device.

The Cypress BLE sample program that I modified for this project contains five services (listed earlier). Although I only used the “Health Thermometer” profile, modified for the project, the PSoC 6 still “advertises” the other profiles, and the diagnostic PRINT statements in my techBASIC app displays these other profiles, even though they are not used in any way.

After the program starts the BLE scan, the callback function “BLEDiscoveredPeripheral” is triggered for each BLE device that the iPad finds within range. I check for a “peripheral.BLEname” equaling “PSoC 6” (the name I assigned in the PSoC 6 BLE component’s name variable). When found, for diagnostic purposes I PRINT out the UUID of the device and its name (PSoC 6). I set the “serverFound” flag variable to 1 and perform a BLE.stopScan.

The next callback function invoked is “BLEPeripheralInfo.” It provides an INTEGER variable, “kind.” If “kind” equals 1, the program calls the “discoverServices” function, which requests the available services that the paired BLE device provides. If “kind” =4, that indicates that the device is responding with the services it provides. For diagnostic purposes, the program then PRINTs out the UUIDs of any services that the device has indicated it provides. It then calls the “discoverCharacteristics” function (for each discovered service), which queries the BLE device for the UUIDs of the characteristic(s) of the various service(s) it has found.

The callback function, “BLEServiceInfo” gets invoked by the above sequence of events. It returns the INTEGER variable “kind.” If “kind” equals 1 then the BLE device is returning characteristic information. For diagnostic purposes, the program PRINTs out the various characteristic UUIDs that have been reported.

All the UUIDs that are PRINTed out by the above routines can be checked against the UUIDs defined in the Creator IDE’s BLE component’s setup wizard for the project’s PSoC 6 firmware. This setup wizard was described earlier (Figure 2).

While the “BLEServiceInfo” function is returning characteristic information, the program checks for the following condition:

IF service.uuid = “1809” AND characteristic(i).uuid = “2A1C”

When both of these conditions are met, we know that the “Health Thermometer” profile and its “temperature” characteristic have been found. The program then invokes the “peripheral.setNotify” function, which tells the PSoC6 to start transmitting ECG data.

As noted above, my PSoC6 program will be sending out ECG data as if it were temperature. I pack four, 8-bit ECG readings into each 4-byte packet (originally defined as the floating-point Temperature variable).

The last piece of the puzzle is handling the data that the BLE device is sending. This is done by the “BLECharacteristicInfo” function. This returns an INTEGER variable, “kind.”

  • If “kind” equals 1, then we are receiving a description of the characteristic. For the Health Thermometer profile, the characteristic’s description would be “temperature.” I didn’t bother to PRINT this to the console.
  • If “kind” equals 2, then we are receiving the actual data, in the 4-byte format described above. This is obtained using the “characteristic.value” function. I extract the four, 8-bit ECG values from the 4-byte “value” array. The Health Thermometer BLE packet is actually 5 bytes, but the first byte is the “Centigrade/Fahrenheit” flag, which I don’t use.

These are all the BLE-related functions. All that remains is to plot the ECG data to the graphics screen. The ECG array (ECGArray) comprises 400 ECG data points. When this array is full, I plot the data. Plotting is done by a high-level plotting routine:

Px.setPoints(ECGArray) REM Px is Graphics.newPlot object

To display the progress of the BLE discovery/pairing process at startup, I have placed a “Progress Bar” on the screen. As various BLE callback functions are executed, I indicate this by incrementally increasing the value of the Progress Bar, using the following function:

p.setValue() REM “p” is the Progress Bar’s name in the program

I have an Exit button on the screen. The callback function “touchUpInside” returns a variable, “ctrl” whenever a button (or other user-activated object) is pressed. If “ctrl” equals Button5 (the name I gave the Exit button), then the program performs a BLE.disconnect(PSoC6) to disconnect from the PSoC6. It then waits 5 s and issues a STOP to end the program. The 5 second wait allows the necessary BLE messages to be exchanged between the iPad and the PSoc6, before I end program execution with the STOP command.

Figure 4 shows my techBASIC program running. While developing the techBASIC software, I was initially unsure how quickly it could plot ECG data and concurrently handle all the BLE message traffic. What you see in Figure 4 is the resulting plot when I modified the PSoC6’s firmware to output a triangle wave—not an actual ECG wave. When I initially sent only one ECG data point per BLE packet, I observed significant data loss at this sample rate. After switching to 4 data points per BLE packet, things looked OK (Figure 4). Figure 7 shows the app running with actual ECG data.

FIGURE 7 – The techBASIC program running with actual ECG data (compare with Figure 4 test run).

Most of my recent projects have been powered by either Arm MCUs or Expressif’s ESP8266/32 Wi-Fi SoCs. In either case, the MCU comes in a small-footprint SMD package. Since I can’t mount these small devices on PCBs of my own design, I have been saved by the abundance of small, inexpensive MCU development boards offered by various manufacturers, and break-out boards for various peripheral chips. For this project, Cypress’ CY8CPROTO-063-BLE, containing the PSoC6/BLE RF sub-system and programmer, was ideal. Its $20 price is much less than that of the individual components I would need to build it myself.

The ECG analog front end is not easy to design from scratch. Luckily, Sparkfun sells a small PCB containing an Analog Devices AD8232 device. This chip is specially designed for ECG and similar low-level biological signals. Sparkfun also sells the stick-on ECG electrodes in packs of 10, and a matching cable (Part numbers are shown on the Figure 8 schematic).

FIGURE 8 – Schematic diagram of the project. The ECG electrodes are labeled as RL (reference lead), RA (right arm) and LA (left arm).

Besides a LiPo battery and a few other components, these two modules are all that is required to implement the project. Figure 8 shows the schematic diagram of the project, and Figure 9 is a photo of the unit in its case.

FIGURE 9 – The unit just before sliding the top panel into place. While not visible, the GND bus of the circuit is wired up to the aluminum case for shielding.

Note that I removed the “snap-off” programmer in the finished unit to save space. Without it, programming can still be accomplished by connecting a 5-wire cable between J5 on the PSoC6 board and J4 on the programmer. Note, however, that when you snap off the programmer board, the Rx and Tx signals from the PSoC6 module are no longer connected to the applicable pins on the programmer board. This means that you can’t use the USB-serial port to send out Debug messages. However, you can run jumper wires between PSoC6’s port 5.1 (Tx) and 5.0 (Rx) to the programmer’s J6 pins 6 and 7, respectively. This restores the USB-serial connection.

I did all my program development with the KitProg2 programmer still attached to the PSoC6 board. In this case, power for the PSoC6 BLE board is provided by the KitProg2 programmer, which contains a 3.3 V regulator for both the programmer’s PSoC 5LP and the PSoC6 on the target board.

I expected to be able to connect the 3.7 V LiPo battery to the Vin pin on the PSoC 6 target board. Nothing worked when I did this, and for a few minutes I panicked, thinking that the Vin pin required a regulated 3.3 V. This would have differed from the CY8CKIT-059 PSoC 5LP development board that I used for my last project, which contained its own 3.3 V regulator. In the CY8CPROTO-063-BLE user’s guide, figure A-2 shows regulators on both the programmer board and the PSoC 6 target board. However, once I magnified the schematic, I noticed that the words “No load” became legible next to the U6 regulator. This explained why nothing was working. As a remedy, I installed an external Microchip Technology MCP1700-3302E low-dropout regulator to power both the PSoC6 board and the AD8232 Heart Monitor board. One could populate the PSoC 6 target board with a surface-mount regulator (U6 and associated parts) that it calls for, but I did not have a compatible regulator on hand.

When I first received the CY8CPROTO-063-BLE, it showed up immediately as a valid BLE device when I ran the CySmart app on my iPad. It ran the demo properly. However, it did not show up in the Creator IDE when I tried to edit sample code and download it to the board. This was unexpected, given that I had experienced no such problems with the CY8CKIT-059 PSoC 5LP development module during my last project.

I discovered that the Kitprog2 firmware on the CY8CPROTO-063-BLE development board (the same as what is contained on the CY8CKIT-059 PSoC 5LP board) requires upgrading to Kitprog3 firmware to work with PSoC6 devices. You must use the stand-alone Cypress PSoC Programmer v3.28 PC application to update the Kitprog2 firmware. The PSoC Programmer screen before and after I ran the update procedure is shown in Figure 10 and Figure 11, respectively.

FIGURE 10 – My CY8CPROTO-063-BLE arrived with the programmer containing KitProg2 firmware. This was out of date, as shown in the PSoC Programmer application (part of the Creator IDE install).

FIGURE 11 – The PSoC Programmer screen looked like this when connected to the CY8CPROTO-063-BLE, after the KitProg2 firmware had been updated to KitProg3.

Even having finished this project, I still find BLE to be a complex protocol to handle. I’m certain that if 6 months passed before I used it again, I’d likely follow the same procedure I did here—modify a sample PSoC 6 program to serve my needs and then modify my techBASIC program to serve the new function. The only difference would be that I now have the CY5677 CySmart BLE 4.2 USB dongle on hand (Figure 12). This debugging tool is more versatile than the CySmart iOS app that I used for this project.

FIGURE 12 – The CY5677 CySmart BLE 4.2 USB dongle. I didn’t buy it until after developing the firmware for this project. I expected that the iOS CySmart app would handle debugging, but it turns out that the hardware USB dongle is much more versatile in this regard.

That said, I must give kudos to Cypress for the Creator IDE application. It does a lot to automate the process of incorporating a BLE function into your application—particularly if you can use one of their sample programs as a template for your firmware.

If you need to write a custom BLE app for an iPhone or iPad, and don’t do this for a living, I think the techBASIC app is a smart option to explore. If you are accustomed to programming in Visual Basic, it doesn’t take too long to get used to it. It also contains many library routines to handle most of the internal peripheral functions found on iOS devices (apart from those to which Apple doesn’t permit access).

The PSoC 6 source code is available on the Circuit Cellar website, in what Cypress calls the “archive” format. It contains all the files needed to replicate the project. The techBASIC code is also provided as a text file. Because Apple restricts the loading of program code directly to an iOS device, you need a “trick” to do it. The techBASIC manual covers this in detail, but basically, you must email the source code to yourself, copy it to the clipboard, and then paste it into techBASIC. 

For detailed article references and additional resources go to:

Analog Devices |
Cypress Semiconductor |
Espressif Systems |
Infineon Technologies |
Microchip Technology |


Keep up-to-date with our FREE Weekly Newsletter!

Don't miss out on upcoming issues of Circuit Cellar.

Note: We’ve made the Dec 2022 issue of Circuit Cellar available as a free sample issue. In it, you’ll find a rich variety of the kinds of articles and information that exemplify a typical issue of the current magazine.

Would you like to write for Circuit Cellar? We are always accepting articles/posts from the technical community. Get in touch with us and let's discuss your ideas.

Sponsor this Article
+ posts

Brian Millier runs Computer Interface Consultants. He was an instrumentation engineer in the Department of Chemistry at Dalhousie University (Halifax, NS, Canada) for 29 years.

Supporting Companies

Upcoming Events

Copyright © KCK Media Corp.
All Rights Reserved

Copyright © 2024 KCK Media Corp.

Bluetooth-Enabled ECG Monitor

by Brian Millier time to read: 27 min