|
| 1 | +# This file is part of the Python Arduino IoT Cloud. |
| 2 | +# Any copyright is dedicated to the Public Domain. |
| 3 | +# https://creativecommons.org/publicdomain/zero/1.0/ |
| 4 | +import time |
| 5 | +import ssl # noqa |
| 6 | +import network |
| 7 | +import logging |
| 8 | +from time import strftime |
| 9 | +from arduino_iot_cloud import ArduinoCloudClient |
| 10 | +from arduino_iot_cloud import Location |
| 11 | +from arduino_iot_cloud import Schedule |
| 12 | +from arduino_iot_cloud import ColoredLight |
| 13 | +from arduino_iot_cloud import Task |
| 14 | +from arduino_iot_cloud import CADATA # noqa |
| 15 | +from random import uniform |
| 16 | +from secrets import WIFI_SSID |
| 17 | +from secrets import WIFI_PASS |
| 18 | +from secrets import DEVICE_ID |
| 19 | + |
| 20 | + |
| 21 | +# Provisioned boards with secure elements can provide key and |
| 22 | +# certificate URIs in the SE, in following format: |
| 23 | +KEY_PATH = "se05x:token=0x00000064" # noqa |
| 24 | +CERT_PATH = "se05x:token=0x00000065" # noqa |
| 25 | + |
| 26 | +# Alternatively, the key and certificate files can be stored |
| 27 | +# on the internal filesystem in DER format: |
| 28 | +#KEY_PATH = "key.der" # noqa |
| 29 | +#CERT_PATH = "cert.der" # noqa |
| 30 | + |
| 31 | + |
| 32 | +def on_switch_changed(client, value): |
| 33 | + # This is a write callback for the switch that toggles the LED variable. The LED |
| 34 | + # variable can be accessed via the client object passed in the first argument. |
| 35 | + client["led"] = value |
| 36 | + |
| 37 | + |
| 38 | +def on_clight_changed(client, clight): |
| 39 | + logging.info(f"ColoredLight changed. Swi: {clight.swi} Bri: {clight.bri} Sat: {clight.sat} Hue: {clight.hue}") |
| 40 | + |
| 41 | + |
| 42 | +def user_task(client, args): |
| 43 | + # NOTE: this function should not block. |
| 44 | + # This is a user-defined task that updates the colored light. Note any registered |
| 45 | + # cloud object can be accessed using the client object passed to this function. |
| 46 | + # The composite ColoredLight object fields can be assigned to individually, using dot: |
| 47 | + client["clight"].hue = round(uniform(0, 100), 1) |
| 48 | + client["clight"].bri = round(uniform(0, 100), 1) |
| 49 | + |
| 50 | + |
| 51 | +def wdt_task(client, wdt): |
| 52 | + # Update the WDT to prevent it from resetting the system |
| 53 | + wdt.feed() |
| 54 | + |
| 55 | + |
| 56 | +def wifi_connect(): |
| 57 | + if not WIFI_SSID or not WIFI_PASS: |
| 58 | + raise (Exception("Network is not configured. Set SSID and passwords in secrets.py")) |
| 59 | + wlan = network.WLAN(network.STA_IF) |
| 60 | + wlan.active(True) |
| 61 | + wlan.connect(WIFI_SSID, WIFI_PASS) |
| 62 | + while not wlan.isconnected(): |
| 63 | + logging.info("Trying to connect. Note this may take a while...") |
| 64 | + time.sleep_ms(500) |
| 65 | + logging.info(f"WiFi Connected {wlan.ifconfig()}") |
| 66 | + |
| 67 | + |
| 68 | +if __name__ == "__main__": |
| 69 | + # Configure the logger. |
| 70 | + # All message equal or higher to the logger level are printed. |
| 71 | + # To see more debugging messages, set level=logging.DEBUG. |
| 72 | + logging.basicConfig( |
| 73 | + datefmt="%H:%M:%S", |
| 74 | + format="%(asctime)s.%(msecs)03d %(message)s", |
| 75 | + level=logging.INFO, |
| 76 | + ) |
| 77 | + |
| 78 | + # NOTE: Add networking code here or in boot.py |
| 79 | + wifi_connect() |
| 80 | + |
| 81 | + # Create a client object to connect to the Arduino IoT cloud. |
| 82 | + # For mTLS authentication, "keyfile" and "certfile" can be paths to a DER-encoded key and |
| 83 | + # a DER-encoded certificate, or secure element (SE) URIs in the format: provider:token=slot |
| 84 | + client = ArduinoCloudClient( |
| 85 | + device_id=DEVICE_ID, |
| 86 | + ssl_params={ |
| 87 | + "keyfile": KEY_PATH, "certfile": CERT_PATH, "cadata": CADATA, |
| 88 | + "verify_mode": ssl.CERT_REQUIRED, "server_hostname": "iot.arduino.cc" |
| 89 | + }, |
| 90 | + sync_mode=False, |
| 91 | + ) |
| 92 | + |
| 93 | + # Register cloud objects. |
| 94 | + # Note: The following objects must be created first in the dashboard and linked to the device. |
| 95 | + # This cloud object is initialized with its last known value from the cloud. When this object is updated |
| 96 | + # from the dashboard, the on_switch_changed function is called with the client object and the new value. |
| 97 | + client.register("sw1", value=None, on_write=on_switch_changed, interval=0.250) |
| 98 | + |
| 99 | + # This cloud object is updated manually in the switch's on_write_change callback. |
| 100 | + client.register("led", value=None) |
| 101 | + |
| 102 | + # This is a periodic cloud object that gets updated at fixed intervals (in this case 1 seconed) with the |
| 103 | + # value returned from its on_read function (a formatted string of the current time). Note this object's |
| 104 | + # initial value is None, it will be initialized by calling the on_read function. |
| 105 | + client.register("clk", value=None, on_read=lambda x: strftime("%H:%M:%S", time.localtime()), interval=1.0) |
| 106 | + |
| 107 | + # This is an example of a composite cloud object (a cloud object with multiple variables). In this case |
| 108 | + # a colored light with switch, hue, saturation and brightness attributes. Once initialized, the object's |
| 109 | + # attributes can be accessed using dot notation. For example: client["clight"].swi = False. |
| 110 | + client.register(ColoredLight("clight", swi=True, on_write=on_clight_changed)) |
| 111 | + |
| 112 | + # This is another example of a composite cloud object, a map location with lat and long attributes. |
| 113 | + client.register(Location("treasureisland", lat=31.264694, lon=29.979987)) |
| 114 | + |
| 115 | + # This object allows scheduling recurring events from the cloud UI. On activation of the event, if the |
| 116 | + # on_active callback is provided, it gets called with the client object and the schedule object value. |
| 117 | + # Note: The activation status of the object can also be polled using client["schedule"].active. |
| 118 | + client.register(Schedule("schedule", on_active=lambda client, value: logging.info(f"Schedule activated {value}!"))) |
| 119 | + |
| 120 | + # The client can also schedule user code in a task and run it along with the other cloud objects. |
| 121 | + # To schedule a user function, use the Task object and pass the task name and function in "on_run" |
| 122 | + # to client.register(). |
| 123 | + client.register(Task("user_task", on_run=user_task, interval=1.0)) |
| 124 | + |
| 125 | + # If a Watchdog timer is available, it can be used to recover the system by resetting it, if it ever |
| 126 | + # hangs or crashes for any reason. NOTE: once the WDT is enabled it must be reset periodically to |
| 127 | + # prevent it from resetting the system, which is done in another user task. |
| 128 | + # NOTE: Change the following to True to enable the WDT. |
| 129 | + if False: |
| 130 | + try: |
| 131 | + from machine import WDT |
| 132 | + # Enable the WDT with a timeout of 5s (1s is the minimum) |
| 133 | + wdt = WDT(timeout=7500) |
| 134 | + client.register(Task("watchdog_task", on_run=wdt_task, interval=1.0, args=wdt)) |
| 135 | + except (ImportError, AttributeError): |
| 136 | + pass |
| 137 | + |
| 138 | + # Start the Arduino IoT cloud client. In synchronous mode, this function returns immediately |
| 139 | + # after connecting to the cloud. |
| 140 | + client.start() |
| 141 | + |
| 142 | + # In sync mode, start returns after connecting, and the client must be polled periodically. |
| 143 | + while True: |
| 144 | + client.update() |
| 145 | + time.sleep(0.100) |
0 commit comments