Paging A Mobile Phone With A Key Fob Part 3: Forwarding Events To The Cloud


Previously we hooked up a Raspberry Pi to a 433MHz RF receiver so that we can process the key fob’s button events. In this blog post we will look at connecting the Raspberry Pi to an Azure IoT hub.

We have already covered Azure IoT hub and how it works in some of the previous blog posts, so here we will focus specifically on the key fob button events. If you need a bit of a refresher then feel free to read the posts below:

Provisioning a new Azure IoT Device for the Raspberry Pi

Assuming an Azure IoT hub is up and running, we can easily register a new Azure IoT device using Powershell.

PS D:\Workspace\IoTHub> $MyHomeAgent= Add-AzIotHubDevice -ResourceGroupName "your-resource-group-name" -IotHubName "your-azure-iot-hub-name" -DeviceId "MyHomeAgent" -AuthMethod "shared_private_key"
PS D:\Workspace\IoTHub>

Connecting the Raspberry Pi to Azure IoT Hub

To connect to an Azure IoT hub, we need the device connection string. The commands below allows us to retrieve the connection string for the device we created.

PS D:\Workspace\IoTHub> $TempObject = Get-AzIotHubDeviceConnectionString -ResourceGroupName "your-resource-group-name" -IotHubName "your-azure-iot-hub-name" -DeviceId "MyHomeAgent" -KeyType secondary
PS D:\Workspace\IoTHub> $TempObject.ConnectionString
HostName=your-azure-iot-hub-name.azure-devices.net;DeviceId=MyHomeAgent;SharedAccessKey=my-home-agent-shared-access-key
PS D:\Workspace\IoTHub>

We will need to install Azure IoT Device SDK

PS D:\Workspace\IoTHub> pip install azure-iot-device

Now create a class to interface with the Azure IoT hub.

# AzureIotDeviceAgent.py

import asyncio

from azure.iot.device.aio import IoTHubDeviceClient

class AzureIotDeviceAgent:
   def __init__(self, name, connection_string, client = None):
      self.name= name
      self.device_client = IoTHubDeviceClient.create_from_connection_string(connection_string)
      self.client = client

   async def connect(self):
      await self.device_client.connect()

   async def disconnect(self):
      await self.device_client.disconnect()

We will also need to update main() to initiate a connection with the Azure IoT hub.

# App.py

...
from AzureIotDeviceAgent import AzureIotDeviceAgent
 
async def main():
 
   ...
   my_home_agent = None
   my_home_agent_name = "MyHomeAgent"
   my_home_agent_connection_string = "my-home-agent-connection-string"
   ...

   try:
      ...
      my_home_agent = AzureIotDeviceAgent(my_home_agent_name, my_home_agent_connection_string)
      await my_home_agent.connect()
 
   finally:
      pass

For now that is all the changes we need to connect to the Azure IoT hub.

If we run the following Powershell command while App.py is running we should see that it is connected. Likewise, when App.py is not running we should see that it is not connected.

PS D:\Workspace\IoTHub> Get-AzIotHubDevice -ResourceGroupName "your-resource-group-name" -IotHubName "your-azure-iot-hub-name" -DeviceId "MyHomeAgent" | Select-Object -Property Id, ConnectionState
Id          ConnectionState
--          ---------------
MyHomeAgent    Connected
PS D:\Workspace\IoTHub>

Sending data to Azure IoT Hub

So far we have only implemented ButtonObserver.py to print out if button A or B is pressed. What we want to do now is indicate to the Azure Iot hub that Assistance requested when button A is pressed and Assistance request cancelled when button B is pressed.

# App.py

...

async def main():
  
   ...
 
   try:
      ...
      observer.set_agent(my_home_agent)
      my_home_agent.set_client(observer)
  
   finally:
      pass

# ButtonMonitor.py
 
...
import asyncio
 
class ButtonMonitor:
   ...

   async def button_pressed(self):
      if self.observer is None:
         pass  # No observer so do nothing
      else:
         # Create new asyncio loop
         loop = asyncio.new_event_loop()
         asyncio.set_event_loop(loop)
         future = asyncio.ensure_future(self.__execute_when_pressed__(self.button_name)) # Execute async method
         loop.run_until_complete(future)
         loop.close()

   async def __execute_when_pressed__(self, source):
      await self.observer.handle_button_pressed(source)

# ButtonObserver.py
 
...
from pb_Parcel_pb2 import pb_Parcel
 
class ButtonObserver:
   ...

   def __pack_button_event__(self, assistance_requested):
      parcel = pb_Parcel()

      # We don't need to populate domain and domain agent, that will
      # be done by the domain agent.
      parcel.source.name = "My Pager"
      parcel.source.local_id = parcel.source.name

      # We only need to indicate destination endpoint name here.
      # Other info will be derived by backend services.
      parcel.destination.name = "My Mobile"

      parcel.type = "ASCII"

      # In this case, the content is just a string. However if content
      # were a protobuffer object, then we will need convert it to a string
      # as SerializeToString() returns a byte array.
      # e.g. parcel.content = str(content.SerializeToString(), 'utf-8')
      if assistance_requested:
         parcel.content = "Assistance requested"
      else:
         parcel.content = "Assistance request cancelled"

      return parcel
 
   async def handle_button_pressed(self, source):
      if self.agent is None:
         return

      assistance_requested = False

      if source == "A":
         assistance_requested = True

      parcel = self.__pack_button_event__(assistance_requested)
      await self.agent.send_parcel(parcel)
 
   ...

# AzureIotDeviceAgent.py

...
from pb_Parcel_pb2 import pb_Parcel

class AzureIotDeviceAgent:
   ...

   def set_client(self, client):
      self.client = client

   async def send_parcel(self, parcel):
      # Populate source domain and domain agent
      parcel.source.domain_agent = self.name
      parcel.source.domain = "Device Domain"
      parcel = parcel.SerializeToString()

      await self.device_client.send_message(parcel)

We extended the existing code (as shown above) to do two main things:

  • Encapsulate the button event inside a pb_Parcel protobuf object and serialises it.
  • Send the serialised pb_Parcel to the Azure IoT hub.

What’s next?

At this point, we have only sent the messages to the hub so they’re just sitting there in the message queue at the moment.

In the next blog post, we will create a serverless function to process the hub messages and forward them to a mobile number.

Categories: Azure, Internet of things, Python, Raspberry PiTags: , , , ,

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: