Skip to content

Commit 98bdbe0

Browse files
author
brentru
committed
start adding to planter code...
1 parent 7e717e1 commit 98bdbe0

2 files changed

Lines changed: 305 additions & 0 deletions

File tree

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
"""
2+
GFX Helper for PyPortal GCP IoT Plant Monitor
3+
"""
4+
import board
5+
import displayio
6+
import terminalio
7+
from adafruit_display_text.label import Label
8+
9+
# the current working directory (where this file is)
10+
cwd = ("/"+__file__).rsplit('/', 1)[0]
11+
12+
# GFX Font
13+
font = terminalio.FONT
14+
15+
class Google_GFX(displayio.Group):
16+
def __init__(self, is_celsius=False):
17+
"""Creates an Google_GFX object for displaying plant
18+
and Google Cloud IoT Core status.
19+
:param bool is_celsius: Temperature displayed in Celsius.
20+
"""
21+
# root displayio group
22+
root_group = displayio.Group(max_size=23)
23+
self.display = board.DISPLAY
24+
self.display.show(root_group)
25+
super().__init__(max_size=15)
26+
27+
# temperature display option
28+
self._is_celsius = is_celsius
29+
30+
# create background icon group
31+
self._icon_group = displayio.Group(max_size=3)
32+
self.append(self._icon_group)
33+
self.display.show(self._icon_group)
34+
# create text object group
35+
self._text_group = displayio.Group(max_size=40)
36+
self.append(self._text_group)
37+
38+
print("Displaying splash screen")
39+
self._icon_sprite = None
40+
self._icon_file = None
41+
self._cwd = cwd
42+
self.set_icon(self._cwd+"/images/gcp_splash.bmp")
43+
44+
print('Setting up labels...')
45+
header_group = displayio.Group(scale=3)
46+
header_label = Label(font, text="Google Cloud IoT\n Planter")
47+
header_label.x = (self.display.width // 2) // 22
48+
header_label.y = 15
49+
header_group.append(header_label)
50+
self._text_group.append(header_group)
51+
52+
# Temperature Display
53+
temp_group = displayio.Group(scale=2, max_size=3)
54+
temp_label = Label(font, text="Temperature: ")
55+
temp_label.x = (self.display.width//2) // 11
56+
temp_label.y = 55
57+
temp_group.append(temp_label)
58+
59+
self.temp_data_label = Label(font, text="75 F")
60+
self.temp_data_label.x = (self.display.width//3)
61+
self.temp_data_label.y = 55
62+
temp_group.append(self.temp_data_label)
63+
self._text_group.append(temp_group)
64+
65+
# Water Level
66+
water_group = displayio.Group(scale=2, max_size=2)
67+
self.water_level = Label(font, text="Water Level: ")
68+
self.water_level.x = (self.display.width//2) // 11
69+
self.water_level.y = 75
70+
water_group.append(self.water_level)
71+
72+
self.water_lvl_label = Label(font, text="350")
73+
self.water_lvl_label.x = (self.display.width//3)
74+
self.water_lvl_label.y = 75
75+
temp_group.append(self.water_lvl_label)
76+
self._text_group.append(water_group)
77+
78+
# GCP Status
79+
status_group = displayio.Group()
80+
self.gcp_status_label = Label(font, text="Connecting to GCP IoT Core...")
81+
self.gcp_status_label.x = self.display.width//4
82+
self.gcp_status_label.y = 200
83+
status_group.append(self.gcp_status_label)
84+
self._text_group.append(status_group)
85+
86+
self.display.show(self._text_group)
87+
88+
def show_gcp_status(self, status_text):
89+
"""Displays the system status on the PyPortal
90+
:param str status_text: Description of current GCP IoT status
91+
"""
92+
self.gcp_status_label.text = status_text
93+
94+
def show_water_level(self, water_data):
95+
"""Displays the water level from the Stemma Soil Sensor.
96+
:param int water_data: water value
97+
"""
98+
print('Water Level: ', water_data)
99+
self.water_lvl_label.text = str(water_data)
100+
101+
def show_temp(self, temp_data):
102+
"""Displays the temperature from the Stemma Soil Sensor.
103+
:param float temp_data: Temperature value.
104+
"""
105+
if not self._is_celsius:
106+
temp_data = (temp_data * 9 / 5) + 32 - 15
107+
print('Temperature: %0.0f°F'%temp_data)
108+
if temp_data >= 212:
109+
self.temp_data_label.color = 0xFD2EE
110+
elif temp_data <= 32:
111+
self.temp_data_label.color = 0xFF0000
112+
self.temp_data_label = '%0.0f°F'%temp_data
113+
temp_data = '%0.0f'%temp_data
114+
return int(temp_data)
115+
else:
116+
print('Temperature: %0.0f°C'%temp_data)
117+
if temp_data <= 0:
118+
self.temp_data_label.color = 0xFD2EE
119+
elif temp_data >= 100:
120+
self.temp_data_label.color = 0xFF0000
121+
self.temp_data_label.text = '%0.0f°C'%temp_data
122+
temp_data = '%0.0f'%temp_data
123+
return int(temp_data)
124+
125+
def set_icon(self, filename):
126+
"""Sets the background image to a bitmap file.
127+
128+
:param filename: The filename of the chosen icon
129+
"""
130+
print("Set icon to ", filename)
131+
if self._icon_group:
132+
self._icon_group.pop()
133+
134+
if not filename:
135+
return # we're done, no icon desired
136+
if self._icon_file:
137+
self._icon_file.close()
138+
self._icon_file = open(filename, "rb")
139+
icon = displayio.OnDiskBitmap(self._icon_file)
140+
try:
141+
self._icon_sprite = displayio.TileGrid(icon,
142+
pixel_shader=displayio.ColorConverter())
143+
except TypeError:
144+
self._icon_sprite = displayio.TileGrid(icon,
145+
pixel_shader=displayio.ColorConverter(),
146+
position=(0,0))
147+
148+
self._icon_group.append(self._icon_sprite)
149+
self.display.refresh_soon()
150+
self.display.wait_for_frame()

PyPortal_AWS_IOT_Planter/code.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
"""
2+
PyPortal Amazon AWS IoT Plant Monitor
3+
=========================================================
4+
Log your plant's vitals to AWS IoT and receive email
5+
notifications when it needs watering with your PyPortal.
6+
7+
Author: Brent Rubell for Adafruit Industries, 2019
8+
"""
9+
import json
10+
import board
11+
import busio
12+
from digitalio import DigitalInOut
13+
import neopixel
14+
from adafruit_esp32spi import adafruit_esp32spi
15+
from adafruit_esp32spi import adafruit_esp32spi_wifimanager
16+
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
17+
from adafruit_minimqtt import MQTT
18+
from adafruit_aws_iot import MQTT_CLIENT
19+
from adafruit_seesaw.seesaw import Seesaw
20+
21+
22+
# Get wifi details and more from a secrets.py file
23+
try:
24+
from secrets import secrets
25+
except ImportError:
26+
print("WiFi secrets are kept in secrets.py, please add them there!")
27+
raise
28+
29+
# Get device certificate and private key from a certificates.py file
30+
try:
31+
from certificates import DEVICE_CERT, DEVICE_KEY
32+
except ImportError:
33+
print("Certificate and private key data is kept in certificates.py, \
34+
please add them there!")
35+
raise
36+
37+
# PyPortal ESP32 Setup
38+
esp32_cs = digitalio.DigitalInOut(board.ESP_CS)
39+
esp32_ready = digitalio.DigitalInOut(board.ESP_BUSY)
40+
esp32_reset = digitalio.DigitalInOut(board.ESP_RESET)
41+
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
42+
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
43+
status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
44+
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(
45+
esp, secrets, status_light)
46+
47+
# Set AWS Device Certificate
48+
esp.set_certificate(DEVICE_CERT)
49+
50+
# Set AWS RSA Private Key
51+
esp.set_private_key(DEVICE_KEY)
52+
53+
# Connect to WiFi
54+
print("Connecting to WiFi...")
55+
wifi.connect()
56+
print("Connected!")
57+
58+
# Soil Sensor Setup
59+
i2c_bus = busio.I2C(board.SCL, board.SDA)
60+
ss = Seesaw(i2c_bus, addr=0x36)
61+
62+
# Initialize the graphics helper
63+
print("Loading GCP Graphics...")
64+
gfx = gcp_gfx_helper.Google_GFX()
65+
print("Graphics loaded!")
66+
67+
68+
# Define callback methods which are called when events occur
69+
# pylint: disable=unused-argument, redefined-outer-name
70+
def connect(client, userdata, flags, rc):
71+
# This function will be called when the client is connected
72+
# successfully to the broker.
73+
print('Connected to AWS IoT!')
74+
print('Flags: {0}\nRC: {1}'.format(flags, rc))
75+
# TODO: Subscribe to shadow topics!
76+
77+
78+
def disconnect(client, userdata, rc):
79+
# This method is called when the client disconnects
80+
# from the broker.
81+
print('Disconnected from AWS IoT!')
82+
83+
84+
def subscribe(client, userdata, topic, granted_qos):
85+
# This method is called when the client subscribes to a new topic.
86+
print('Subscribed to {0} with QOS level {1}'.format(topic, granted_qos))
87+
88+
89+
def unsubscribe(client, userdata, topic, pid):
90+
# This method is called when the client unsubscribes from a topic.
91+
print('Unsubscribed from {0} with PID {1}'.format(topic, pid))
92+
93+
94+
def publish(client, userdata, topic, pid):
95+
# This method is called when the client publishes data to a topic.
96+
print('Published to {0} with PID {1}'.format(topic, pid))
97+
98+
99+
def message(client, topic, msg):
100+
# This method is called when the client receives data from a topic.
101+
# TODO
102+
103+
# Set up a new MiniMQTT Client
104+
client = MQTT(socket,
105+
broker = secrets['broker'],
106+
client_id = secrets['client_id'],
107+
network_manager = wifi,
108+
log=True)
109+
110+
# Initialize AWS IoT MQTT API Client
111+
aws_iot = MQTT_CLIENT(client)
112+
113+
# Connect callback handlers to AWS IoT MQTT Client
114+
aws_iot.on_connect = connect
115+
aws_iot.on_disconnect = disconnect
116+
aws_iot.on_subscribe = subscribe
117+
aws_iot.on_unsubscribe = unsubscribe
118+
aws_iot.on_publish = publish
119+
aws_iot.on_message = message
120+
121+
print('Attempting to connect to %s'%client.broker)
122+
aws_iot.connect()
123+
124+
# Time in seconds since power on
125+
initial = time.monotonic()
126+
127+
while True:
128+
try:
129+
gfx.show_gcp_status('Listening for new messages...')
130+
now = time.monotonic()
131+
if now - initial > (SENSOR_DELAY * 60):
132+
# read moisture level
133+
moisture = ss.moisture_read()
134+
# read temperature
135+
temperature = ss.get_temp()
136+
# Display Soil Sensor values on pyportal
137+
temperature = gfx.show_temp(temperature)
138+
gfx.show_water_level(moisture)
139+
print('Sending data to AWS IoT...')
140+
gfx.show_gcp_status('Publishing data...')
141+
# Create a json-formatted device payload
142+
payload = {"state":{"reported":
143+
{"moisture":str(moisture),
144+
"temp":str(temperature)}}}
145+
# Update device shadow
146+
aws_iot.shadow_update(json.dumps(payload))
147+
gfx.show_gcp_status('Data published!')
148+
print('Data sent!')
149+
# Reset timer
150+
initial = now
151+
aws_iot.loop()
152+
except (ValueError, RuntimeError) as e:
153+
print("Failed to get data, retrying", e)
154+
wifi.reset()
155+
continue

0 commit comments

Comments
 (0)