More FHIR on the pi…

So this is in the nature of the ‘I’m doing it this way just because I can!’

The background is that I want to create a series of time lapse pictures from a property overlooking a tidal river that doesn’t have internet access, and to be able to access those images remotely. (The property is in Waipu – rural New Zealand!)

I’m planning on using a raspberry Pi (with a camera) to take the photos, then upload them to a server from where I can generate the video sequence. (Because I have to use a cellular connection to access the net, I can’t host the images directly off the pi – which would have been my preferred option).

My original thought was to set up a simple hosted server and database (likely node.js and mongoDb) and have the pi regularly send the images there using HTTP, and then I thought – why not use FHIR? The server side then becomes easy as I can use an instance of a reference server (likely the HAPI CLI server) to receive and store the images. There was an added bonus that I could get to play with python a bit – and see how it handles sending FHIR resources.

Now, I’m the first to admit that this is hardly the intended use of FHIR – and that there are much better solutions you could use, but the opportunity to experiment with the pi as a FHIR client is certainly useful as I can imagine that as a real use case – imaging the pi acting as a home-based hub sending patient observations from devices to a FHIR server in the cloud.

As it turned out, it was really easy to do this – at least in a prototype fashion.

Here the code for the python script (and I’ll use cron to call it on a regular basis):

import json
import base64
import requests
import time
from datetime import datetime
import picamera
from sense_hat import SenseHat
sense = SenseHat()

url = "http://fhir.hl7.org.nz/dstu2/Media"
fileName = "images/pic"+str(time.time())+".jpg"

# colours for the sense hat
colour_red = [255, 0, 0]
colour_white = [255, 255, 255]
colour_blue = [0,0,255]
colour_green = [0,128,0]
colour_yellow = [255,255,0]

# image dimensions
image_height = 620
image_width = 480

sense.clear(colour_yellow)

with picamera.PiCamera() as camera:
   camera.resolution = (image_height,image_width)
   camera.start_preview()
   # Camera warm-up time
   print('Waiting for camera')
   time.sleep(2)
   camera.stop_preview()
   camera.capture(fileName)

sense.clear(colour_blue)      #to indicate picture saved

# load the file and send
with open (fileName, "rb") as myfile:
      pic = myfile.read()
# code for FHIR object
      reference = {}
      reference['reference'] = "Location/hayWaipu"
      extensions = []
      extensionLocation = {}
      extensionTime = {}
      extensionLocation['url'] = "http://fhir.hl7.org.nz/StructureDefinition/locationOfMedia"
      extensionLocation['valueReference'] = reference
      extensions.append(extensionLocation)
      extensionTime['url'] = "http://fhir.hl7.org.nz/StructureDefinition/locationOfMedia"
      extensionTime['valueDateTime'] = datetime.now().strftime("%Y-%m-%dT%H:%m:%S")
      extensions.append(extensionTime)

      data = {}
      data['extension'] = extensions
      data['resourceType'] = 'Media'
      data['type'] = 'photo'
      data['deviceName'] = 'rpi'
      data['height'] = image_height
      data['width'] = image_width

      content = {}
      content['data'] = base64.b64encode(pic)
      content['contentType'] = 'image/jpg'
      data['content'] = content
      json_data = json.dumps(data)

headers = {'content-type':'application/json+fhir'}
print('Sending picture to ' + url)

r = requests.post(url=url, data=json_data, headers=headers)
if (r.status_code == 201):
      sense.clear(colour_green)
      print(r.headers['location'])
      print('Image saved')
else:
      sense.clear(colour_red)
      print(r.json())

It’s actually longer than it needs to be as I decided to use the ‘sense hat’ as a visual indicator of where the script was in the overall process (I had it lying around – a simple LED might be better) – although the fact that it has a number of other sensors (temperature, humidity etc.) does suggest that I could collect other observations.

It starts by importing the various libraries it needs (I’m using the requests library to make the actual REST call) and then activates the camera, waits a couple of seconds and takes a photo – saving it to a file with a name constructed from the current time (that way I have a local copy of the images – though I’ll need some way to ensure they don’t fill up the pi!)

Next it constructs a Media resource to contain the actual image as a base64 encoded attachment.

I added a couple of extensions – one to record the date the image was taken, and the other so I could have it reference a Location resource (the closest existing match was ‘specimen’ which didn’t feel quite right…). I still have to create the extension definitions (StructureDefinition resources) but I will!

So this all works, though there are a few enhancements that come to mind:

  • Better logging to assist troubleshooting is likely to be required when I deploy for real
  • The use of the sense hat is a bit overkill – perhaps a multi colour LED that does the same thing and maybe a small LCD screen showing the last operation?
  • Rather than storing the image as a base-64 encoded attachment (with associated overhead), I might store it as a binary resource referenced from the Media – or maybe an Observation might be better?
  • I could add other observations (temperature, humidity & such like)
  • I should really shift the resource creating stuff to a separate function – maybe a separate module, just to keep the main script tidier

Anyway, a fun little experiment exploring FHIR on the raspberry pi!

About David Hay
I'm an independent contractor working with companies like Orion Health and Rhapsody, HL7 Fellow, Chair emeritus of HL7 New Zealand and a co-chair of the FHIR Management Group. I have a keen interest in health IT, especially health interoperability with HL7 and the new FHIR standard.

4 Responses to More FHIR on the pi…

  1. Jens Kristian Villadsen says:

    What a somewhat special way to use FHIR on. Way to go! I gave it a try to see if I could decode the base64 encoded pictures but no luck. Taking the content of the value of eg. http://fhir.hl7.org.nz/dstu2/Media/93349 and decode it gave something that could look like some binary data, it was not png content (ÿØÿácúExif MM *) {binary chars omitted}

    • David Hay says:

      Oops – that what comes of posting before the job is complete! I’ll see what is going wrong…

      • Jens Kristian Villadsen says:

        Shit happens when you publish it on the web … let me know when its working. I would love to see some pictures from ‘down under’ on a FHIR server – and hey – it would probably be the first of its kind (the FHIR media server hosted on the continent of Oceania).

    • David Hay says:

      so there were 2 issues:

      First – I had the mime type as image/png – and it is actually image/jpg
      Second – my python file read was not correct as it didn’t specify a binary file.

      I’ve corrected the post, and the image at http://fhir.hl7.org.nz/dstu2/Media/93389 works for me (it’s a picture out of my office window at dusk!) – no guarantees that this image will remain (in fact I’ll certainly delete it in the not too distant future) – but all you need is a pi and camera to duplicate it, so I’m not too worried about that 🙂

      thanks again for pointing this out!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: