/ PyPortal_GCP_IOT_Planter / code.py
code.py
1 # SPDX-FileCopyrightText: 2019 Brent Rubell for Adafruit Industries 2 # 3 # SPDX-License-Identifier: MIT 4 5 """ 6 PyPortal Google Cloud IoT Core Planter 7 ======================================================= 8 Water your plant remotely and log its vitals to Google 9 Cloud IoT Core with your PyPortal. 10 11 Author: Brent Rubell for Adafruit Industries, 2019 12 """ 13 import time 14 import json 15 import board 16 import busio 17 import gcp_gfx_helper 18 import neopixel 19 from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager 20 import adafruit_esp32spi.adafruit_esp32spi_socket as socket 21 from adafruit_gc_iot_core import MQTT_API, Cloud_Core 22 import adafruit_minimqtt.adafruit_minimqtt as MQTT 23 from adafruit_seesaw.seesaw import Seesaw 24 import digitalio 25 26 # Delay before reading the sensors, in minutes 27 SENSOR_DELAY = 10 28 29 # Get wifi details and more from a secrets.py file 30 try: 31 from secrets import secrets 32 except ImportError: 33 print("WiFi secrets are kept in secrets.py, please add them there!") 34 raise 35 36 # PyPortal ESP32 Setup 37 esp32_cs = digitalio.DigitalInOut(board.ESP_CS) 38 esp32_ready = digitalio.DigitalInOut(board.ESP_BUSY) 39 esp32_reset = digitalio.DigitalInOut(board.ESP_RESET) 40 spi = busio.SPI(board.SCK, board.MOSI, board.MISO) 41 esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) 42 status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) 43 wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager( 44 esp, secrets, status_light) 45 46 # Connect to WiFi 47 print("Connecting to WiFi...") 48 wifi.connect() 49 print("Connected!") 50 51 # Initialize MQTT interface with the esp interface 52 MQTT.set_socket(socket, esp) 53 54 # Soil Sensor Setup 55 i2c_bus = busio.I2C(board.SCL, board.SDA) 56 ss = Seesaw(i2c_bus, addr=0x36) 57 58 # Water Pump Setup 59 water_pump = digitalio.DigitalInOut(board.D3) 60 water_pump.direction = digitalio.Direction.OUTPUT 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 Google Cloud IoT!') 74 print('Flags: {0}\nRC: {1}'.format(flags, rc)) 75 # Subscribes to commands/# topic 76 google_mqtt.subscribe_to_all_commands() 77 78 79 def disconnect(client, userdata, rc): 80 # This method is called when the client disconnects 81 # from the broker. 82 print('Disconnected from Google Cloud IoT!') 83 84 85 def subscribe(client, userdata, topic, granted_qos): 86 # This method is called when the client subscribes to a new topic. 87 print('Subscribed to {0} with QOS level {1}'.format(topic, granted_qos)) 88 89 90 def unsubscribe(client, userdata, topic, pid): 91 # This method is called when the client unsubscribes from a topic. 92 print('Unsubscribed from {0} with PID {1}'.format(topic, pid)) 93 94 95 def publish(client, userdata, topic, pid): 96 # This method is called when the client publishes data to a topic. 97 print('Published to {0} with PID {1}'.format(topic, pid)) 98 99 100 def message(client, topic, msg): 101 # This method is called when the client receives data from a topic. 102 try: 103 # Attempt to decode a JSON command 104 msg_dict = json.loads(msg) 105 # Handle water-pump commands 106 if msg_dict['pump_time']: 107 handle_pump(msg_dict) 108 except TypeError: 109 # Non-JSON command, print normally 110 print("Message from {}: {}".format(topic, msg)) 111 112 113 def handle_pump(command): 114 """Handles command about the planter's 115 watering pump from Google Core IoT. 116 :param json command: Message from device/commands# 117 """ 118 print("handling pump...") 119 # Parse the pump command message 120 # Expected format: {"power": true, "pump_time":3} 121 pump_time = command['pump_time'] 122 pump_status = command['power'] 123 if pump_status: 124 print("Turning pump on for {} seconds...".format(pump_time)) 125 start_pump = time.monotonic() 126 while True: 127 gfx.show_gcp_status('Watering plant...') 128 cur_time = time.monotonic() 129 if cur_time - start_pump > pump_time: 130 # Timer expired, leave the loop 131 print("Plant watered!") 132 break 133 water_pump.value = True 134 gfx.show_gcp_status('Plant watered!') 135 print("Turning pump off") 136 water_pump.value = False 137 138 139 # Initialize Google Cloud IoT Core interface 140 google_iot = Cloud_Core(esp, secrets) 141 142 # JSON-Web-Token (JWT) Generation 143 print("Generating JWT...") 144 jwt = google_iot.generate_jwt() 145 print("Your JWT is: ", jwt) 146 147 # Set up a new MiniMQTT Client 148 client = MQTT.MQTT(broker=google_iot.broker, 149 username=google_iot.username, 150 password=jwt, 151 client_id=google_iot.cid) 152 153 # Initialize Google MQTT API Client 154 google_mqtt = MQTT_API(client) 155 156 # Connect callback handlers to Google MQTT Client 157 google_mqtt.on_connect = connect 158 google_mqtt.on_disconnect = disconnect 159 google_mqtt.on_subscribe = subscribe 160 google_mqtt.on_unsubscribe = unsubscribe 161 google_mqtt.on_publish = publish 162 google_mqtt.on_message = message 163 164 print('Attempting to connect to %s' % client.broker) 165 google_mqtt.connect() 166 167 # Time in seconds since power on 168 initial = time.monotonic() 169 170 while True: 171 try: 172 gfx.show_gcp_status('Listening for new messages...') 173 now = time.monotonic() 174 if now - initial > (SENSOR_DELAY * 60): 175 # read moisture level 176 moisture_level = ss.moisture_read() 177 # read temperature 178 temperature = ss.get_temp() 179 # Display Soil Sensor values on pyportal 180 temperature = gfx.show_temp(temperature) 181 gfx.show_water_level(moisture_level) 182 print('Sending data to GCP IoT Core') 183 gfx.show_gcp_status('Publishing data...') 184 google_mqtt.publish(temperature, "events") 185 time.sleep(2) 186 google_mqtt.publish(moisture_level, "events") 187 gfx.show_gcp_status('Data published!') 188 print('Data sent!') 189 # Reset timer 190 initial = now 191 google_mqtt.loop() 192 except (ValueError, RuntimeError, OSError, ConnectionError) as e: 193 print("Failed to get data, retrying", e) 194 wifi.reset() 195 google_mqtt.reconnect() 196 continue