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 a Product Strategist at Orion Health, Chair emeritus of HL7 New Zealand and 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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s

%d bloggers like this: