From 1fee2af6b1b2d5ce20e8d0da372ebab9367a9fc1 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 26 Sep 2022 09:24:01 +0200 Subject: [PATCH] examples: Add separate example for MicroPython. --- README.md | 21 +++---- example.py | 125 ---------------------------------------- examples/example.py | 98 +++++++++++++++++++++++++++++++ examples/micropython.py | 83 ++++++++++++++++++++++++++ 4 files changed, 188 insertions(+), 139 deletions(-) delete mode 100644 example.py create mode 100644 examples/example.py create mode 100644 examples/micropython.py diff --git a/README.md b/README.md index 87f7b8a..f8df410 100644 --- a/README.md +++ b/README.md @@ -43,16 +43,17 @@ softhsm2-util --delete-token --token "arduino" ``` ### Run the example script -* Set `KEY_URI`, `CERT_URI`, `DEVICE_ID`, `THING_ID` in `example.py`. +* Set `KEY_PATH`, `CERT_PATH`, `DEVICE_ID`, `THING_ID` in `example.py`. * Provide a CA certificate in a `ca-root.pem` file or set `CA_PATH` to `None` if it's not used. * Override the default `pin` and provide `ENGINE_PATH` and `MODULE_PATH` in `ssl_params` if needed. * Clone this repository and run the following: ```bash -python example.py +python examples/example.py ``` ## Testing on MicroPython -The following changes to the example code are required, because MicroPython does Not support secure elements yet: +MicroPython currently does Not support secure elements, the key and cert files must be stored in DER format on the filesystem. +Convert the key and certificate to DER, using the following commands, and copy to the filesystem storage. #### Convert key and certificate to `.DER` ```bash @@ -60,17 +61,9 @@ openssl ec -in key.pem -out key.der -outform DER openssl x509 -in cert.pem -out cert.der -outform DER ``` -#### Load key and certificate from filesystem storage -```python -KEY_PATH = "key.der" -CERT_PATH = "cert.der" -.... - -async def main(): - with open(KEY_PATH, "rb") as fin: key = fin.read() - with open(CERT_PATH, "rb") as fin: cert = fin.read() - client = AIOTClient(device_id=DEVICE_ID, keepalive=10, ssl_params = {"key":key, "cert":cert}) - .... +### Run the MicroPython example script +* Set `KEY_PATH`, `CERT_PATH`, to key and certificate DER paths respectively. +* run `examples/micropython.py` ``` ## Useful links diff --git a/example.py b/example.py deleted file mode 100644 index 82b0907..0000000 --- a/example.py +++ /dev/null @@ -1,125 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2022 Arduino SA -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -# This file is part of the Python Arduino IoT Cloud. - -import time - -try: - import logging - import asyncio -except ImportError: - import uasyncio as asyncio - import ulogging as logging -if hasattr(time, "strftime"): - from time import strftime -else: - from ulogging.ustrftime import strftime -from arduino_iot_cloud import AIOTClient -from arduino_iot_cloud import Location -from arduino_iot_cloud import Schedule -from arduino_iot_cloud import ColoredLight -from random import randint, choice - -DEBUG_ENABLED = True - -KEY_URI = "pkcs11:token=arduino" -CERT_URI = "pkcs11:token=arduino" -CA_PATH = "ca-root.pem" -DEVICE_ID = b"25deeda1-3fda-4d06-9c3c-dd31be382cd2" - - -async def user_main(client): - """ - Add your code here. - NOTE: To allow other tasks to run, this function must yield - execution periodically by calling asyncio.sleep(seconds). - """ - while True: - # The composite cloud object's fields can be assigned to individually: - client["clight"].hue = randint(0, 100) - client["clight"].bri = randint(0, 100) - client["user"] = choice(["=^.. ^=", "=^ ..^="]) - await asyncio.sleep(1.0) - - -def on_switch_changed(client, value): - """ - This is a write callback for the switch that toggles the LED variable. The LED - variable can be accessed via the client object passed in the first argument. - """ - if value and not hasattr(on_switch_changed, "init"): - on_switch_changed.init = True - logging.info("Someone left the lights on!") - client["led"] = value - - -def on_clight_changed(client, clight): - logging.info(f"ColoredLight changed. Swi: {clight.swi} Bri: {clight.bri} Sat: {clight.sat} Hue: {clight.hue}") - - -async def main(): - client = AIOTClient( - device_id=DEVICE_ID, - ssl_params={"pin": "1234", "keyfile": KEY_URI, "certfile": CERT_URI, "ca_certs": CA_PATH}, - ) - # This cloud object is initialized with its last known value from the cloud. - client.register("sw1", value=None, on_write=on_switch_changed, interval=0.250) - - # This cloud object is initialized with its last known value from the cloud, - # and gets manually updated from the switch's on_write_change callback. - client.register("led", value=None) - - # This is a periodic cloud object that gets updated every 1 second. - client.register("pot", value=None, on_read=lambda x: randint(0, 1024), interval=1.0) - - # This is a periodic cloud object that gets updated every 1 second, - # with the formatted current time value. - client.register("clk", value=None, on_read=lambda x: strftime("%H:%M:%S", time.localtime()), interval=1.0) - - # This variable is an example for a composite object (a colored light object in this case), - # which is composed of multiple variables. Once initialized, the object's variables can be - # accessed as normal attributes, using dot notation (e.g: client["clight"].swi = False) - client.register(ColoredLight("clight", swi=True, hue=22, sat=75, bri=10, on_write=on_clight_changed)) - - # This variable is an example for a composite object (a map location). - client.register(Location("treasureisland", lat=31.264694, lon=29.979987)) - - # This variable is updated manually from user_main. - client.register("user", value="") - - # This object allows scheduling recurring events from the cloud UI. On activation of the event, if - # on_active callback is provided, it gets called with the client object and the schedule object value. - # The activation status of the object can also be polled using client["schedule"].active. - client.register(Schedule("schedule", on_active=lambda client, value: logging.info(f"Schedule activated {value}!"))) - - # Start the Arduino IoT cloud client. - await client.run(user_main) - - -if __name__ == "__main__": - logging.basicConfig( - datefmt="%H:%M:%S", - format="%(asctime)s.%(msecs)03d %(message)s", - level=logging.DEBUG if DEBUG_ENABLED else logging.INFO, - ) - asyncio.run(main()) diff --git a/examples/example.py b/examples/example.py new file mode 100644 index 0000000..c05bf82 --- /dev/null +++ b/examples/example.py @@ -0,0 +1,98 @@ +# This file is part of the Python Arduino IoT Cloud. +# The MIT License (MIT) +# Copyright (c) 2022 Arduino SA +import time +import asyncio +import logging +from time import strftime +from arduino_iot_cloud import AIOTClient +from arduino_iot_cloud import Location +from arduino_iot_cloud import Schedule +from arduino_iot_cloud import ColoredLight +from random import randint +import argparse + +KEY_PATH = "pkcs11:token=arduino" +CERT_PATH = "pkcs11:token=arduino" +CA_PATH = "ca-root.pem" +DEVICE_ID = b"25deeda1-3fda-4d06-9c3c-dd31be382cd2" + + +async def user_main(client): + # Add your code here. Note to allow other tasks to run, this function + # must yield execution periodically by calling asyncio.sleep(seconds). + while True: + # The composite cloud object's fields can be assigned to individually: + client["clight"].hue = randint(0, 100) + client["clight"].bri = randint(0, 100) + await asyncio.sleep(1.0) + + +def on_switch_changed(client, value): + # This is a write callback for the switch that toggles the LED variable. The LED + # variable can be accessed via the client object passed in the first argument. + client["led"] = value + + +def on_clight_changed(client, clight): + logging.info(f"ColoredLight changed. Swi: {clight.swi} Bri: {clight.bri} Sat: {clight.sat} Hue: {clight.hue}") + + +async def main(): + # Create a client to connect to the Arduino IoT cloud. To use a secure element, set the token "pin" + # and URI in "keyfile" and "certfile, and CA certificate (if used), in ssl_params. Alternatively, + # a username and a password can be used for authentication, for example: + # client = AIOTClient(device_id, username="username", password="password") + client = AIOTClient( + device_id=DEVICE_ID, + ssl_params={"pin": "1234", "keyfile": KEY_PATH, "certfile": CERT_PATH, "ca_certs": CA_PATH}, + ) + + # Register cloud objects. Note these objects must be created first in the dashboard. + # This cloud object is initialized with its last known value from the cloud. When this object is updated + # from the dashboard, the on_switch_changed function is called with the client object and the new value. + client.register("sw1", value=None, on_write=on_switch_changed, interval=0.250) + + # This cloud object is updated manually in the switch's on_write_change callback. + client.register("led", value=None) + + # This is a periodic cloud object that gets updated at fixed intervals (in this case 1 seconed) with the + # value returned from its on_read function (a formatted string of the current time). Note this object's + # initial value is None, it will be initialized by calling the on_read function. + client.register("clk", value=None, on_read=lambda x: strftime("%H:%M:%S", time.localtime()), interval=1.0) + + # This is an example of a composite cloud object (a cloud object with multiple variables). In this case + # a colored light with switch, hue, saturation and brightness attributes. Once initialized, the object's + # attributes can be accessed using dot notation. For example: client["clight"].swi = False. + client.register(ColoredLight("clight", swi=True, on_write=on_clight_changed)) + + # This is another example of a composite cloud object, a map location with lat and long attributes. + client.register(Location("treasureisland", lat=31.264694, lon=29.979987)) + + # This object allows scheduling recurring events from the cloud UI. On activation of the event, if the + # on_active callback is provided, it gets called with the client object and the schedule object value. + # Note: The activation status of the object can also be polled using client["schedule"].active. + client.register(Schedule("schedule", on_active=lambda client, value: logging.info(f"Schedule activated {value}!"))) + + # Start the Arduino IoT cloud client. Note a co-routine can be passed to client.run(coro), in this case it will + # scheduled to run along with the other cloud objects. + await client.run(user_main) + + +if __name__ == "__main__": + # Parse command line args. + parser = argparse.ArgumentParser(description="arduino_iot_cloud.py") + parser.add_argument("-d", "--debug", action="store_true", help="Enable debugging messages") + args = parser.parse_args() + + # Assume the host has an active Internet connection. + + # Configure the logger. + # All message equal or higher to the logger level are printed. + # To see more debugging messages, pass --debug on the command line. + logging.basicConfig( + datefmt="%H:%M:%S", + format="%(asctime)s.%(msecs)03d %(message)s", + level=logging.DEBUG if args.debug else logging.INFO, + ) + asyncio.run(main()) diff --git a/examples/micropython.py b/examples/micropython.py new file mode 100644 index 0000000..3005542 --- /dev/null +++ b/examples/micropython.py @@ -0,0 +1,83 @@ +# This file is part of the Python Arduino IoT Cloud. +# The MIT License (MIT) +# Copyright (c) 2022 Arduino SA +import time +import uasyncio as asyncio +import ulogging as logging +from ulogging.ustrftime import strftime +from arduino_iot_cloud import AIOTClient, Location, Schedule, ColoredLight +from random import randint + +KEY_PATH = "key.der" +CERT_PATH = "cert.der" +DEVICE_ID = b"25deeda1-3fda-4d06-9c3c-dd31be382cd2" + + +async def user_main(client): + # Add your code here. Note to allow other tasks to run, this function + # must yield execution periodically by calling asyncio.sleep(seconds). + while True: + # The composite cloud object's fields can be assigned to individually: + client["clight"].hue = randint(0, 100) + client["clight"].bri = randint(0, 100) + await asyncio.sleep(1.0) + + +def on_switch_changed(client, value): + # This is a write callback for the switch that toggles the LED variable. The LED + # variable can be accessed via the client object passed in the first argument. + client["led"] = value + + +def on_clight_changed(client, clight): + logging.info(f"ColoredLight changed. Swi: {clight.swi} Bri: {clight.bri} Sat: {clight.sat} Hue: {clight.hue}") + + +async def main(): + # Create a client to connect to the Arduino IoT cloud. For MicroPython, the key and cert files must be stored + # in DER format on the filesystem. Alternatively, a username and a password can be used for authentication: + # client = AIOTClient(device_id, username="username", password="password") + client = AIOTClient(device_id=DEVICE_ID, ssl_params={"keyfile": KEY_PATH, "certfile": CERT_PATH}) + + # Register cloud objects. Note these objects must be created first in the dashboard. + # This cloud object is initialized with its last known value from the cloud. When this object is updated + # from the dashboard, the on_switch_changed function is called with the client object and the new value. + client.register("sw1", value=None, on_write=on_switch_changed, interval=0.250) + + # This cloud object is updated manually in the switch's on_write_change callback. + client.register("led", value=None) + + # This is a periodic cloud object that gets updated at fixed intervals (in this case 1 seconed) with the + # value returned from its on_read function (a formatted string of the current time). Note this object's + # initial value is None, it will be initialized by calling the on_read function. + client.register("clk", value=None, on_read=lambda x: strftime("%H:%M:%S", time.localtime()), interval=1.0) + + # This is an example of a composite cloud object (a cloud object with multiple variables). In this case + # a colored light with switch, hue, saturation and brightness attributes. Once initialized, the object's + # attributes can be accessed using dot notation. For example: client["clight"].swi = False. + client.register(ColoredLight("clight", swi=True, on_write=on_clight_changed)) + + # This is another example of a composite cloud object, a map location with lat and long attributes. + client.register(Location("treasureisland", lat=31.264694, lon=29.979987)) + + # This object allows scheduling recurring events from the cloud UI. On activation of the event, if the + # on_active callback is provided, it gets called with the client object and the schedule object value. + # Note: The activation status of the object can also be polled using client["schedule"].active. + client.register(Schedule("schedule", on_active=lambda client, value: logging.info(f"Schedule activated {value}!"))) + + # Start the Arduino IoT cloud client. Note a co-routine can be passed to client.run(coro), in this case it will + # scheduled to run along with the other cloud objects. + await client.run(user_main) + + +if __name__ == "__main__": + # Configure the logger. + # All message equal or higher to the logger level are printed. + # To see more debugging messages, set level=logging.DEBUG. + logging.basicConfig( + datefmt="%H:%M:%S", + format="%(asctime)s.%(msecs)03d %(message)s", + level=logging.INFO, + ) + # NOTE: Add networking code here or in boot.py + asyncio.run(main())