This Proof-of-Concept demonstrates how low-cost devices can be made for remote health monitoring and integration with healthcare systems. Intended applications are:
- Ad hoc remote monitoring of at-risk patients, such as suspected COVID-19 infections.
- Wearable, wireless monitors for inpatients in large hospitals where existing wireless coverage (wifi, bluetooth) is unreliable due to thick walls and large areas. Low energy devices pose new opportunities for continuous patient monitoring with data integration with existing hospital electronic health record systems (EHR).
- Vital signs monitoring of patients living in nuring homes without the need for traditional wired or wireless infrastructure. Data integration with existing municipal- or primary health record systems.
- Medical- and safety monitoring for homecare patients without the need for installing network infrastructure. City- and nationwide low-power WAN coverage removes existing constraints regarding home health monitoring.
During recent hackathons many projects have focused on detecting COVID-19 symptoms through measurement of SPO2, heart rate, temperature and similar. This project provides a practical solution on how to get this data to healthcare providers so the appropriate measures can be taken. The devices can operate on the same battery for months.
A patient suspecting COVID-19 infection could contact local health services by internet or phone and have a device sent by mail the next day. The patient would then use the device regularily and have measurements sent directly to her electronic health record until she was deemed healthy or in need of medical attention.
With LoRaWAN cities and whole countries can be covered at the lowest investment available today. A single €300 gateway can cover a radius of over 10 km, over 20 km in rural places. I have helped cover my home town with fellow enthusiasts with only a few gateways.
I believe in an open, community built network such as The Things Network. Compared to thousands of individual private networks the possibilities are unparalleled. There are other options for building private networks, such as using The Things Industries Enterprise or other totally custom implementations.
My backgroundI have been a systems developer at a major Nordic Electronic Healthcare Record system since 2004. My special interest later years have been what IoT and low-power, long-range devices can do for telemedicine. These are all my own opinions and body of work.
The problemIntegration: In one country alone you will find hundreds of different healthcare systems spanning private and national hospitals, primary care clinics and municipal services such as homecare, child/family health clinics, as well as numerous different types of specialist clinics. Medical record exchange between these systems and institutions are generally way behind other industries, due to the vast number of health related domains, complexities and differences in organization. Adding to this the complexities of bridging language, culture and traditions of healthcare across countries it soon becomes clear that creating custom integrations that map sensor data to the clinical data structure of every other healthcare system is an impossible task.
Communication: The solutions readily available or posed all rely on communication technology that impose great constraints on the practicalities of the devices. Wifi requires larger batteries than the electronics itself if the devices is to last any longer than a few days. The range reduces usability to indoor applications and even within hospitals infrastructure is complex, costly and coverage is usually not 100%. The largest concern though is how wifi-credentials are handled. I have seen no solution that is suitable for more than a handful of known networks, and offline management is usually not handled at all. Bluetooth/BLE is a huge improvement on battery conservation but severe limits on range reduce usage to pairing with mobile phones. This pairing works for the low percentage of the world population that has such a phone at disposal, but it does not work in a healthcare institution setting. A doctor would in no practical way be able to monitor a constant flow of patients in a clinic or hospital, not to mention for homecare in this manner.
Battery life: Healthcare providers rely on a vast number of different portable devices for monitoring patient's vital signs. Examples are body temperature thermometers, heart rate monitors, ECG monitors, digital sphygmomanometers to name a few. Common for most of them are that they are reliable with heavy use and can last for months without the need for recharge or a change of batteries. They are however not connected with any electronic healthcare record system and rely on measurements being written as notes or into medical charts in the form of ring binders. The measurements will usually be communicated verbally until the end of a nurses shift, when they are finally entered into the electronic records. A connected device has to last comparatively as non-connected devices.
The solutionLuckily for the past decade great efforts have been put into creating standards that can be used to model clinical events, measurements, diagnoses and resources. These standards have different aims:
- Interoperability between similar and dissimilar healthcare systems
- Structured and dynamic persistance of clinical data within a system
The aim of this proof-of-concept is to demonstrate the power of creating new devices that rely on these standards to exchange measurements with established systems, rather than to compare standards. It is my personal opinion that, in a tight spot, it would be better to choose one standard than to rely on custom integrations per system. Mapping from one standard to another is much more attainable, if needed. In this project I am focusing on openEHR and Fast Healthcare Interoperability Resources (FHIR).
Communication: LoRaWAN has been established as a practical, low-cost and independent alternative for long-range, low-power sensor applications. Combined with an appropriate selection of microcontrollers, sensors, actuators and displays battery life of months and years is achievable. NB-IoT is another appropriate choice for this application and you may have a look at my previous project for comparison. The overall architecture of this PoC is agnostic to the choice of communication and I believe the right tool should be selected for individual applications.
Battery life: To achieve months or years of battery life I have carefully chosen the components for this project:
- Microcontroller with ultra-low current consumption.
- lpwan (LoRaWAN or NB-IoT) as data carrier.
- Sensor with low current consumption. The MAX30101 used in this project is an OK choice, the breakout is just for ease of prototyping. The project will probably evolve using even more power conserving sensors.
- eInk display draws no current between updates. The display will persist between usage.
All of the services used in this project can be used for free as long as traffic is low. I think you could deploy enough devices within the free tiers to perform valuable tests.
The architecture and choice of services were the results of the following requirements and desired features:
- lpwan-agnostic: I have so far built devices relying on LoRaWAN, NB-IoT and Wifi. As technology evolves I want to be as open as possible for new types of communication.
- Scalability: I want to be able to change behavior of the system as much as possible without requiring an extensive development cycle. Some parts are less likely to need changes and can be optimized for speed and low resource requirements.
- End-user customization: Allow intended healthcare institutions to customize views, rules and future expanded use. This would be IT-administrators at clinics and hospitals. The actual end-users, clinicians, would not need to know how the data from the devices entered the electronic healthcare record.
- Azure IoT Central: I have used Azure IoT Hub for a few years. Azure IoT Central is built on top of this and I wanted to use this project to see how far I could take this nice abstraction to solve the problems presented. This could speed up implementation time by hiding many of the complexities regarding IoT Hub, Analytics etc.
- Technology-agnostic integrations: As far as possible rely on standards such as REST/JSON, gRPC/Protocol Buffers, MQTT, OAuth for integration with each external healthcare system. This would provide the best options for reaching as many types of systems as possible, and always leave the option open for providing intermediary integrations for legacy systems that will not support these standards.
This would be the standard flow in the device made for this iteration of the project:
1. Device authenticates and sends measurements via LoRaWAN to The Things Network (TTN).
2. TTN application transforms the data to a structured message and calls a webhook hosted in Azure.
3. A hosted Azure IoT Central bridge lives as a Azure Functions HTTP-trigger (App Service) and extracts relevant information from TTN. This information is then published as a message to Azure IoT Central.
4. In IoT Central I can transform the data and make nice templates for further processing, filtering and rules. From here I can define rules for different classes of devices and have new messages trigger webhooks for integrations to external healthcare systems.
5. The webhooks are serverless Azure Functions that transform the values from the device messages into openEHR or FHIR formats. These formats are then posted to external electronic healthcare record systems with specific authorization and other requirements. In this project I have used an instance of Google Healthcare API as an example of an external EHR for an imaginary hospital.
6. The external EHR receives the measurements in openEHR or FHIR format and processes it as needed.
7. Clinicians at external healthcare providers will now find the measurements in the patient's records.
In a previous project I have demonstrated how points 1 and 2 can be substituted with NB-IoT and some mobile operator provided platform.
Point 4 presents a possibility for providing a pub/sub broker external systems could subscribe to, but I have yet to explore this.
Azure API for FHIRWhy did I not use Azure API for FHIR over Google Healthcare API? The last time I setup an instance of Azure API for FHIR my monthly allotted Azure credits were spent within two weeks. I believe this to be a bug in the service processes. I would have preferred to use both in this project, to further prove my point. Due to time constraints and uncertainties regarding the server resource usage I chose the safe option. I will make a new attempt as soon as time permits.
I would also have preferred to demonstrate integration with an openEHR interface. I have yet to find a provider of a free-to-test instance. I am however planning to integrate this PoC with the openEHR interface provided by the EHR system I develop professionally.
ImplementationHardwareBOM- Low-power Arduino compatible development board with LoRa radio
- MAX30101 or similar break-out
- 3.6 volt battery
- eInk display
- Prototyping breadboard
- Prototyping jump wires
These are optional but increadibly handy for debugging and understanding what is going on between the components and for optimizing energy conservation
- Reliable non-switching lab power supply
- Oscilloscope with serial decode
- Otii Arc energy consumption profiler
The code for the device can be found in the repository. It is split into two files, where secrets.h
contain network and application keys for The Things Network.
To omit adding private credentials to the source code repository I did the following: I created a file secrets.h and added placeholder keys. I then commited and pushed this to the repo. Further I ran the following command to ignore further changes:
git update-index --assume-unchanged secrets.h
Now I can add real keys without git nagging about unstaged changes. Of course, now the values only live on your local machine. I have yet to find a great way to work with public repositories while still keeping some secrets, please suggest solutions.
Instead of listing the whole Arduino code for the device I will point out some areas of interest and leave you with the source code.
Grasshopper development boardThe Grasshopper is a nice choice for a low-power development board with integrated LoRa radio. Comprehensive description, setup instructions, example code and ordering information can be found here. You can produce the PCB your self and assemble the components.
For soldering headers to PCBs with parallel pinout rows I recommend using a breadboard for alignment. Just don't heat the pins for too long, or the sockets on the breadboard will melt.
For alternative development boards and indepth guide on low-power development have a look at my project regarding a mail box sensor.
eInk displayThe display code was a quick job once I had figured out the wiring. I relied on this excellent guide over at Adafruit. To figure out the wiring I had to read the schematics of the Grasshopper to find the SPI pins. I used my oscilloscope with SPI serial decode function to verify I had the right pins and that the signals looked within bounds.
SPI decoding requires 4 channels on the oscilloscope.
MAX30101 sensorReading the SPO2 and pulse sensor was based on the examples and library provided by the SparkFun break-out board.
I had to move a zero-ohm SMD jumper to switch power from 5v to 3v. A fine soldering iron tip is very helpful to accomplish this.
The MAX30101 uses I2C for communication. This requires an appropriate pullup resistor value. I have covered pullups in my previous guides, check them out for more details. Through experimentation and measurements using the oscilloscope I settled with 1k2 Ohm values.
The sensor is very sensitiv to the placement and pressure of the finger measured. This is the reason you will see obviously erroneous readings in most of the screenshots. To rectify this an appropriate enclosure fixed to the finger has to be made.
Custom enclosure and next steps in prototypingI have done a number of similar prototypes recently and I decided that I would skip the next step in creating a more production-ready prototype. This would involve moving from the breadboard to a soldered sandwich of PCBs that could fit inside a 3D printed enclosure. I don't think I would learn enough of this step to justify the effort and instead start planning creating a singe-board PCB, replacing the break-outs. I will also get started with SLA 3D printing to create a more professional looking enclosure that is even closer to a final product. This will be the next phases in this ongoing project.
Power conservationDue to time constraints I did not complete the process of power optimization. When the electronics for this device is finalized for a single-board PCB I will optimize the code and share it on the repository. Here is a list of previous projects of mine that demonstrates how months- and years long batterly lifetime can be achieved:
- Mail box sensor, including The Things Virtual Conference 2020 workshop
- Soil sensor
- Bed occupancy monitor
See specific details on low-power usage for the Grasshopper.
ServicesThe Things NetworkFollow this guide to create a new application at TTN. We will assume you have existing LoRaWAN coverage or have setup your own gateway through TTN. Register your device so that you can access the necessary keys. I use OTAA as authentication method.
const char *appEui = "70B3D57ED00xxxxx";
const char *appKey = "9F147263E75F162820F6FF8890Bxxxxx";
In the TTN application we will need to define a script for decoding the bytes transmitted from the devices over LoRa. Find the code in the repo and enter it under Payload Formats (Custom).
function Decoder(bytes, port) {
var decoded = {};
if (port === 1)
{
decoded.spo2 = bytes[0];
decoded.hr = bytes[1];
decoded.devtemp = (bytes[2]<<8 | bytes[3])/100;
decoded.voltage = (bytes[4]<<8 | bytes[5])/1000;
}
return decoded;
}
We will have to add an integration, a webhook, that will be called for each message received in TTN. The url for this webhook is created in the step under Azure IoT Central Bridge for The Things Network.
Azure IoT CentralI recommend this guide to get a new blank IoT Central application running.
I created a new template for my heartrate/SPO2 devices, adding capabilities as needed. You can find the template in the repo and import it in your own application.
Two cloud properties have been created to enable user association. When issued to patients the devices need to be linked somehow. For this PoC this is done directly in the IoT Central custom forms, but the cloud properties are available to manipulate via the IoT Hub API. The Encounter ID is a shortcut that was needed in the FHIR data store, but would be replaced by an algorithm for finding the most appropriate open encounter, or creating a new. The Patient ID is a UUID that would be mapped to the patient in external systems via Social Security Numbers or via other criteria. Non-patient identifying IDs will almost never be identical between systems so this poses a complexity. These issues are a large part of what I work with professionally and are solvable, I have chosen to simplify them in this PoC.
Once the IoT Central application is running it's time to bridge it with The Things Network. Previously I had to do this from scratch, making a MQTT client that would forward device messages to Azure IoT Hub. Microsoft now provides a nice deployment script from the repo https://github.com/Azure/iotc-device-bridge.
Find your Scope ID and SAS key in the IoT Central Administration tab.
The only problem I ran into was that the npm install
either seems to have stalled or did not finish within a few hours of running. With no error feedback I continued the process and received some strange runtime errors from the js-script responsible for mapping device messages.
So, make sure the npm install
reports success, however long it takes. Yeah, and make sure to navigate into the correct folder in the console before running it!
A few more steps need to be taken to accommodate messages from TTN into the underlying IoT Hub. This guide explains all you need to do.
The result of all of this is a HTTP endpoint that our TTN applicatin can call for each message received.
Hopefully you now have messages flowing from your device via TTN into IoT Central. Azure has a lot of tools for looking at metrics and errors to find out where something is failing. There is a lot of ground to cover and depending on your background, expect to spend a while learning the different mechanisms.
Use Pipedream (previously RequestBin) as a debugging tool to see what your webhooks are posting and to help deserialize JSON payloads.
Finally we will create a dynamic rule in IoT Central that passes on every message to an instance of Google Healthcare API. I use this as a placeholder for an actual external electronic healthcare record system running at a hospital, one that conforms with FHIR. In a production setting one could create one rule for each external system, or add one Action for each integration. This is a question of practicalities when scaling and not the main focus of this PoC. See my previous writeup on how to create an instance of Google Healthcare API.
The FHIR DataStore we will be targeting needs to be populated. This is out of scope of this writeup but the repository contains PowerShell scripts for creating patients and encounters, as well as listing, creating, patching and deleting observations. The documentation explains different ways of creating authentication tokens.
To massage the messages from each device into FHIR format (or openEHR) we will create a serverless Azure Functions. This project only supports a few types of clinical observations, expanding this is trivial for simple sensors. My previous project regarding a bed occupancy sensor demonstrates how to do this with different types of states for a resource instead.
To create the function I used Visual Studio with c#.net core 3 as language and runtime.
We need a HTTP-trigger that can be called from an external webhook. In contrast an IoT Hub-trigger is a function that responds to published messages on the IoT Central's underlying IoT Hub. Writing this function benefits from a bit of c# and.net experience or similar, but you might get away with copying the code from the repo and experimenting.
To access the appropriate Google Cloud APIs we need to add a dependency via NuGet; Google.Apis.CloudHealthcare
(at the time of writing v1beta1
and v1
seems to be in sync).
To be allowed to call the web API the client needs a way to provide credentials. I created tokens in Google Healthcare console, downloaded the JSON-file and embedded it in the project. A more proper and secure method would be to store the token in an Azure Key Store and have the application retrieve it. The IoT Central Bridge demonstrates how this is done. This file is obviously omited from the repo.
Notes on Google Healthcare APIThis API automatically generates different flavors of programming languages. For the c#.net version I think I must have been an early adapter because fundamental features do not work. ReadRequest, CreateRequest and PatchRequest are all broken in the official client library. You can find error descriptions and workarounds in Issues I have posted on the repository (#1522, #1595).
In short, to have a CreateRequest work I ended up with the following code:
string parent = $"projects/{ProjectId}/locations/{Location}/datasets/{DatasetId}/fhirStores/{FhirStoreId}";
Data.HttpBody createBody = new Data.HttpBody();
var createRequest =
cloudHealthcareService.Projects.Locations.Datasets.FhirStores.Fhir.Create(createBody, parent, "Observation");
JObject response = createRequest.SendAsJObjectAsync(createBody).Result;
where SendAsJObjectAsync is an extension method:
public static async Task<JObject> SendAsJObjectAsync(this ClientServiceRequest<HttpBody> request, HttpBody requestBody)
{
var httpRequestMessage = request.CreateRequest();
httpRequestMessage.Content = new StringContent(requestBody.Data);
httpRequestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/fhir+json");
var httpResponseMessage = await request.Service.HttpClient.PostAsync(httpRequestMessage.RequestUri.AbsoluteUri, httpRequestMessage.Content);
httpResponseMessage.EnsureSuccessStatusCode();
var responseText = await httpResponseMessage.Content.ReadAsStringAsync();
return JObject.Parse(responseText);
}
I used Postman and Fiddler to develop and debug the whole function before finally deploying it to Azure.
In Azure portal you can find the deployed Function and go to Get Function Url.
Lastly in IoT Central we will create a rule that applies to our PulseOximeterTemplate. I call it PulseOximeterIntegrations as I plan to have one rule with several actions per integration. Add an Action, type Webhook, and use the url from the previous step. Note that you have to add each property and telemetry of the device template as filters and conditions. If not, they will not be part of the payload in the webhook. I can't find this documented and it seems a bit strange if this is intended behavior.
The resultNow, when the device reads measurements it updates the eInk display. Before going to sleep it also sends the values via LoRaWAN to The Things Network. The Azure IoT Central Bridge is called from the TTN integration and publishes a transformed message to the underlying IoT Hub broker. The message is yet again transformed in Azure IoT Central and displayed in different views. A rule is triggered and the message containing the values are transmitted via a webhook to an Azure Functions, one per external integration. This in turn transforms the values into a FHIR message and posts this via a HTTP call to an instance of Google Cloud Healthcare. I did not find the time to make an application on top of Google Cloud Healthcare but this would be the equivalent of a hospitals electronic healthcare record system, such as the one I develop professionally. I intend to integrate this PoC with that system as soon as time permits. In the mean time I have made PowerShell scripts that list out the observations containing the values and metadata from the devices.
This has been the next step in a series of related projects regarding remote patient monitoring using new technologies. The aim of this step was to complete the whole flow of getting clinical measurements from a remote, low-power device to a simulated hospital. I am happy with the results and know what to focus on next.
I did not find time to implement my existing openEHR formatter, but you may read about it in my first patient monitor project using Azure Sphere.
I would have wanted to end this phase with a prototype that could be handled more practically, but in the end decided my time was better spent skipping a step and focus on creating a more integrated electronics board and thus be able to start work on a more presentable device, smaller in size.
I initially intended to add the ability to publish encrypted measurements to IOTA Tangle using Streams. This would just be another Azure Functions, similar to the Google Healthcare integration. Due to time constraints, Streams being in very early development and the fact that I mostly covered this in a previous project I decided to focus on new ground and return to this at a later point.
It requires quite a bit of insight into the current state of electronic healthcare and telemedicine to fully appreciate the implications of an application such as this. I strongly believe this is a path that will gain patients and healthcare providers alike.
Comments