Skip to content

examples: Add separate example for MicroPython. #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 7 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,34 +43,27 @@ 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
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
Expand Down
125 changes: 0 additions & 125 deletions example.py

This file was deleted.

98 changes: 98 additions & 0 deletions examples/example.py
Original file line number Diff line number Diff line change
@@ -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())
83 changes: 83 additions & 0 deletions examples/micropython.py
Original file line number Diff line number Diff line change
@@ -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())