Skip to content

Commit 916e364

Browse files
committed
Adding files for raspberry pi azure project
Adding files for the Raspberry Pi Azure IoT Hub guide. Folders for the Raspberry Pi and devices that connect to IoT Hub
1 parent 20a187d commit 916e364

File tree

8 files changed

+43505
-0
lines changed

8 files changed

+43505
-0
lines changed

Raspberry_Pi_Azure_IoT_Hub_Dashboard/azure_pi/OstrichSans-Heavy-60.bdf

Lines changed: 14727 additions & 0 deletions
Large diffs are not rendered by default.

Raspberry_Pi_Azure_IoT_Hub_Dashboard/azure_pi/OstrichSans-Heavy-88.bdf

Lines changed: 20577 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
#!/usr/bin/env python3
2+
3+
#SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries
4+
#
5+
#SPDX-License-Identifier: MIT
6+
7+
# pylint: disable=unused-import
8+
import json
9+
from subprocess import Popen, PIPE
10+
from datetime import datetime, timezone
11+
import requests
12+
import displayio
13+
import adafruit_imageload
14+
from adafruit_display_text import label
15+
from adafruit_bitmap_font import bitmap_font
16+
from adafruit_display_shapes.circle import Circle
17+
from azure.iot.hub import IoTHubRegistryManager
18+
from azure.iot.hub.models import Twin, TwinProperties, QuerySpecification, QueryResult
19+
from azure.eventhub import EventHubConsumerClient, TransportType
20+
from blinka_displayio_pygamedisplay import PyGameDisplay
21+
22+
# ***
23+
24+
# UPDATE THESE VARIABLES WITH YOUR IOT HUB INFORMATION
25+
26+
# event hub compatible end point from the built-in endpoints page
27+
event_connection = "YOUR-EVENT-HUB-CONNECTION-STRING-HERE"
28+
# Primary Connection String with registry read and service connect rights
29+
# managed on shared access polices page
30+
status_connection = "YOUR-STATUS-CONNECTION-STRING-HERE"
31+
32+
# iot hub subscription ID
33+
# found on the overview of your iot hub
34+
# format: ########-####-####-############
35+
subscription_id = "YOUR-SUBSCRIPTION-ID-HERE"
36+
37+
# device id's (device names in iot hub)
38+
qt_py = "YOUR-QT-PY-DEVICE-HERE"
39+
tft_feather = "YOUR-TFT-FEATHER-DEVICE-HERE"
40+
s3_feather = "YOUR-S3-FEATHER-DEVICE-HERE"
41+
42+
# ***
43+
44+
# array of devices
45+
devices = [qt_py, tft_feather, s3_feather]
46+
47+
# create display
48+
display = PyGameDisplay(width=1920, height=1080)
49+
50+
# open bitmap background with imageload
51+
bitmap, palette = adafruit_imageload.load(
52+
"/home/pi/azure_pi/piBG_0.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette
53+
)
54+
55+
background = displayio.TileGrid(bitmap, pixel_shader=palette)
56+
57+
# load font files
58+
font_file = "/home/pi/azure_pi/OstrichSans-Heavy-88.bdf"
59+
smallFont_file = "/home/pi/azure_pi/OstrichSans-Heavy-60.bdf"
60+
font = bitmap_font.load_font(font_file)
61+
small_font = bitmap_font.load_font(smallFont_file)
62+
63+
# text objects
64+
# co2 data
65+
co2_text = label.Label(font, text = "500", color = 0xFFFFFF, x = 611, y =800)
66+
67+
# living room data
68+
lr_temp = label.Label(font, text = "72°F", color = 0xFFFFFF, x = 90, y = 424)
69+
lr_humid = label.Label(font, text = "52%", color = 0xFFFFFF, x = 405, y = 424)
70+
lr_press = label.Label(font, text = "1005", color = 0xFFFFFF, x = 715, y = 424)
71+
72+
# bedroom data
73+
bd_temp = label.Label(font, text = "72°F", color = 0xFFFFFF, x = 1050, y =424)
74+
bd_humid = label.Label(font, text = "52%", color = 0xFFFFFF, x = 1365, y =424)
75+
bd_press = label.Label(font, text = "1005", color = 0xFFFFFF, x = 1675, y =424)
76+
77+
# cost, timestamp and last device check-in text
78+
timestamp_text = label.Label(small_font, text = "00:00AM 00/00/00",
79+
color = 0xFFFFFF, x = 1216, y =886)
80+
lastDevice_text = label.Label(small_font, text = "device",
81+
color = 0xFFFFFF, x = 1450, y =805)
82+
cost_text = label.Label(small_font, text = "$0.00",
83+
color = 0xFFFFFF, x = 1511, y =734)
84+
85+
# status circles, defaults to white
86+
qt_status = Circle(908, 695, 22, fill=0xFFFFFF)
87+
lr_status = Circle(908, 52, 22, fill=0xFFFFFF)
88+
bd_status = Circle(1869, 52, 22, fill=0xFFFFFF)
89+
90+
# array of status circles
91+
status_circles = [qt_status, lr_status, bd_status]
92+
93+
# array of display objects
94+
display_objects = [background, co2_text, lr_temp, lr_humid, lr_press,
95+
bd_temp, bd_humid, bd_press, timestamp_text, lastDevice_text,
96+
cost_text, qt_status, lr_status, bd_status]
97+
98+
# display group
99+
main_group = displayio.Group()
100+
101+
# add all display objects from array to the display group
102+
for x in display_objects:
103+
main_group.append(x)
104+
105+
display.show(main_group)
106+
107+
# convert UTC time to local timezone
108+
def utc_to_local(utc_dt):
109+
return utc_dt.replace(tzinfo=timezone.utc).astimezone(tz=None)
110+
111+
# function for an incoming event from one of the devices
112+
# pylint: disable=too-many-locals
113+
def on_event_batch(partition_context, events):
114+
# gets bearer token from azure cli in terminal
115+
cost_json_index = 0
116+
output = Popen(['az', 'account', 'get-access-token'], stdout=PIPE)
117+
# parses the output into a json and grabs the bearer token
118+
bear = json.loads(output.communicate()[0].decode('utf-8').strip())
119+
# creates headers to include with GET HTTP request for cost data
120+
headers = {'Authorization': 'Bearer ' + bear['accessToken'], 'Content-Type': 'application/json'}
121+
# updates request URL with your subscription ID
122+
# pylint: disable=line-too-long
123+
url = "https://management.azure.com/subscriptions/{}/providers/Microsoft.Consumption/usageDetails?api-version=2021-10-01".format(subscription_id)
124+
# makes HTTP request
125+
response = requests.get(url, headers=headers)
126+
# packs the HTTP response into a JSON
127+
feed = response.json()
128+
# grabs the cost per day from the JSON feed
129+
cost = feed['value'][cost_json_index]['properties']['costInBillingCurrency']
130+
# the JSON index for cost can move depending on the data recieved
131+
# adjusts index until a value is recieved
132+
while cost == 0:
133+
cost_json_index += 1
134+
cost = feed['value'][cost_json_index]['properties']['costInBillingCurrency']
135+
# iterates through incoming events
136+
for event in events:
137+
# gets timestamp for event
138+
clock = utc_to_local(event.enqueued_time)
139+
# calculates the month to date cost based on the date
140+
month_cost = cost*((float(clock.strftime("%d")) - 1))
141+
round(month_cost, 2)
142+
# updates cost text
143+
cost_text.text = "$%.2f" % month_cost
144+
# updates timestamp text
145+
timestamp_text.text = clock.strftime("%I:%M%p %m/%d/%y")
146+
# gets the incoming event as a JSON feed
147+
telemetry = event.body_as_json()
148+
# grabs the device ID from the JSON
149+
device = event.system_properties[b'iothub-connection-device-id']
150+
# converts the device ID to a string
151+
string_device = device.decode("utf-8")
152+
# updates last device text
153+
lastDevice_text.text = string_device
154+
# if the device is the qt_py
155+
if string_device == qt_py:
156+
# update co2 text
157+
co2_text.text = str(telemetry['CO2'])
158+
# if the device is the tft feather
159+
if string_device == tft_feather:
160+
# update living room sensor text
161+
lr_temp.text = "%s°F" % str(telemetry['Temperature'])
162+
lr_humid.text = "%s%%" % str(telemetry['Humidity'])
163+
lr_press.text = str(telemetry['Pressure'])
164+
# if the device is the s3 feather
165+
if string_device == s3_feather:
166+
# update bedroom sensor text
167+
bd_temp.text = "%s°F" % str(telemetry['Temperature'])
168+
bd_humid.text = "%s%%" % str(telemetry['Humidity'])
169+
bd_press.text = str(telemetry['Pressure'])
170+
# iterate through the status circles
171+
for d in range(0, 3):
172+
# get connection status of all 3 devices using device twin
173+
twin = status_client.get_twin(devices[d])
174+
# if a device is connected, make the status circle green
175+
if twin.connection_state == 'Connected':
176+
status_circles[d].fill = 0x00FF00
177+
# if a device is disconnected, make the status circle red
178+
if twin.connection_state == 'Disconnected':
179+
status_circles[d].fill = 0xFF0000
180+
# update the read partition from event hub
181+
partition_context.update_checkpoint()
182+
# in case of error with an incoming event
183+
def on_error(partition_context, error):
184+
if partition_context:
185+
print("An exception: {} occurred during receiving from Partition: {}.".format(
186+
partition_context.partition_id,
187+
error
188+
))
189+
else:
190+
print("An exception: {} occurred during the load balance process.".format(error))
191+
# connect to event hub
192+
client = EventHubConsumerClient.from_connection_string(
193+
conn_str = event_connection,
194+
consumer_group = "$default",
195+
)
196+
# connect to device twin
197+
status_client = IoTHubRegistryManager(status_connection)
198+
199+
# while the display is active
200+
while display.running:
201+
try:
202+
# recieve incoming events
203+
client.receive_batch(
204+
on_event_batch = on_event_batch,
205+
on_error = on_error
206+
)
207+
# keyboard exception
208+
except KeyboardInterrupt:
209+
print("Receiving has ")
1.84 MB
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)