# IoT Lab 2: from devices to platform ###### tags: `RSE` `Labs` > curso 2019/2020 In this lab session you will: 1. Learn how to use a type of IoT devices called LoPy by [Pycom](http://pycom.io) 2. Connect these devices to an IoT platform called [Ubidots](https://ubidots.com/) using MQTT. **All the code necessary for this Lab session is available at [bit.ly/rse2019lab](http://bit.ly/rse2019lab)** in folder `codigo/IoT`. ## Part1: Getting started with IoT devices The HW you will use is based on a [LoPy4](https://pycom.io/product/lopy4/) with a [PySense](https://pycom.io/product/pysense/) board. ![](https://i.imgur.com/vSrQQ7f.jpg =x400) LoPys are based on [MicroPython](https://micropython.org/). MicroPython is a full Python compiler and runtime that runs on the bare-metal. You get an interactive prompt, called the REPL ("Read Evaluate Print Loop"), to execute commands immediately, along with the ability to run and import scripts from the built-in filesystem. The REPL has history, tab completion, auto-indent and paste mode. More infos about REPL can be found here: https://docs.pycom.io/pymakr/toolsfeatures/ To connect to the LoPy's REPL you will use a Python tool called **[mpy-repl-tool](https://mpy-repl-tool.readthedocs.io/en/latest/)**. You will learn how to use it in this section. ==Now, plug-in your LoPy device in a USB port and wait a few seconds until the LED starts blinking in blue. Before, you'll probably be asked whether you want to use the device in the VM== ![](https://i.imgur.com/CIKBNVH.jpg =x400) Then execute: ``` python3 -m there detect ``` This command will list the serial ports and "hopefully" :smiley: will automatically find your LoPy board. If everything is fine you should see something like: ``` /dev/ttyACM0 - Pysense - Pysense ``` To get access to the REPL prompt, write: * `python3 -m there -i` You'll get the `>>>` REPL prompt (see below). To exit from the REPL you have to type `Ctrl+]` ![](https://i.imgur.com/CEq8z05.png) ### The basic commands: These are the basic commands that you will have to use: * getting a list of the files on the LoPy: ``` $ python3 -m there ls -l /flash/* ---------- 0 0 34B 1980-01-01 01:09:16 /flash/main.py ---------- 0 0 29B 1980-01-01 01:09:18 /flash/boot.py ``` :::info The filesystem has ``/`` as the root directory and the available physical drives are accessible from here. They are: * `/flash`: the internal flash filesystem * `/sd`: the SD card (if it is inserted, obviously) ::: * coping file `_filename_` from your computer to the LoPy: ```python3 -m there push _filename_ /flash``` * wildcards can be used to copy multiple files from your computer to the LoPy. For example: ```python3 -m there push *.py /flash``` * reading the contents of file `_filename_` on the LoPy: ```python3 -m there cat /flash/_filename_``` * removing file `_filename_` on the LoPy: ```python3 -m there rm /flash/_filename_``` wildcards can be used to delete multiple files. A detailed list of commands can be found here: [mpy-repl-tool.readthedocs](http://mpy-repl-tool.readthedocs.io/en/latest/commandline.html) :::info BTW; it can be useful to create the alias: `$ alias pyt="python3 -m there"` so not to type too much :smile: ::: ### Typical Lopy device workflow A typical workflow is the following: 1. Write the micropython code in your computer 1. Copy the code file or files from your computer to the LoPy, e.g.,: ```$ python3 -m there push *.py /flash``` 1. Start a serial terminal and get access to the REPL: ```$ python3 -m there -i``` 1. Execute the code in file `_filename_.py` on the LoPy, with: * `>>> import _filename_` ==Some useful commands are available [in the Appendix of this doc.](#Appendix-Just-in-case)== ### Let's see if you got it. Execute the following code on your LoPy ```python= # file: led_blink.py import pycom import time RED = 0xFF0000 YELLOW = 0xFFFF33 GREEN = 0x007F00 OFF = 0x000000 def set_led_to(color=GREEN): pycom.heartbeat(False) # Disable the heartbeat LED pycom.rgbled(color) def flash_led_to(color=GREEN, t1=1): set_led_to(color) time.sleep(t1) set_led_to(OFF) for i in range(1,5): flash_led_to(RED) flash_led_to(YELLOW) set_led_to(OFF) ``` :::danger 1.- indica los pasos que has tenido que seguir para ejecutar este codigo en la LoPy. ¿Cual es el resultado de la ejecucion de este codigo? ::: ### Accessing the sensors of the Pysense board The PySense offers various embedded sensors, namely: * Ambient light sensor * Barometric pressure sensor * Temperature sensor * Humidity sensor * 3 axis 12-bit accelerometer Take a look at the code in file `"reads.py"` to understand how to use them. As you can see, various python libraries are necessary, they are all in the `lib` folder. To copy the whole `lib` folder to your LoPy, you have to execute: ```python3 -m there push -r lib/* /flash/lib``` :::danger 2.- indica los pasos que has tenido que seguir para ejecutar el file `"reads.py"` en la LoPy. ¿Cual es el resultado de la ejecucion de este codigo? ::: --- ## Part2: Connecting LoPys to an IoT platform In this part you will experiment with MQTT using **MicroPython**. You will perform experiments that will allow you to learn how to interact with a cloud platform called [Ubidots](https://ubidots.com/). ==You should already have copied the whole `lib` folder to your LoPy, by doing: `python3 -m there push -r lib/* /flash/lib`== Library `lib/ufun.py`, among other, provides some useful functions: * `connect_to_wifi_UPVIoT()`: connects the LoPy to the UPV-IoT WiFi LAN. By properly passing the values `wifi_user` and `wifi_passwd` this function will try three times to connect, exiting if the operation is not possible. * `random_in_range()`: generates random numbers in a range, and * `set_led_to()` and `flash_led_to()`: simplify the control of the LED. ### MQTT clients with MicroPython and the LoPy In this block you will see the **MicroPython** version of the code for a MQTT publisher and subscriber. #### A simple MicroPython subscriber The code below represent a simple subscriber. In this case we use the broker `iot.eclipse.org` but you can use any other accessible broker. Take a look at the code below and try to understand how it works. :::warning Remember to properly assign a value to **`wifi_user`** and **`wifi_passwd`**, using [this list](https://hackmd.io/@pmanzoni/SJyXHYHqr). ::: You can also, if you want, assign a value to `CLIENT_ID`. ```python= # file: mp_sisub.py from mqtt import MQTTClient import pycom import sys import time import ufun wifi_user = 'IOTPMxxx' wifi_passwd = '_____' THE_BROKER = "iot.eclipse.org" THE_TOPIC = "test/RSElab" CLIENT_ID = "" def settimeout(duration): pass def on_message(topic, msg): print("Received msg: ", str(msg), "with topic: ", str(topic)) ### if __name__ == "__main__": ufun.connect_to_wifi_UPVIoT(wifi_user, wifi_passwd) client = MQTTClient(CLIENT_ID, THE_BROKER, 1883) client.set_callback(on_message) print ("Connecting to broker: " + THE_BROKER) try: client.connect() except OSError: print ("Cannot connect to broker: " + THE_BROKER) sys.exit() print ("Connected to broker: " + THE_BROKER) client.subscribe(THE_TOPIC) print('Waiting messages...') while 1: client.check_msg() ``` :::danger 3.- modifica `sipub.py` para enviar mensajes a este subscriber en la LoPy. ::: #### A simple publisher Let's produce some random data using the code below. Take a look at the code below and try to understand how it works. :::warning Remember to properly assign a value to **`wifi_user`** and **`wifi_passwd`**, using [this list](https://hackmd.io/@pmanzoni/SJyXHYHqr). ::: You can also, if you want, assign a value to `CLIENT_ID`. ```python= # file: mp_sipub.py from mqtt import MQTTClient import pycom import sys import time import ufun wifi_user = 'IOTPMxxx' wifi_passwd = '_____' THE_BROKER = "iot.eclipse.org" THE_TOPIC = "test/SRM2018" CLIENT_ID = "" def settimeout(duration): pass def get_data_from_sensor(sensor_id="RAND"): if sensor_id == "RAND": return ufun.random_in_range() ### if __name__ == "__main__": ufun.connect_to_wifi_UPVIoT(wifi_user, wifi_passwd) client = MQTTClient(CLIENT_ID, THE_BROKER, 1883) print ("Connecting to broker: " + THE_BROKER) try: client.connect() except OSError: print ("Cannot connect to broker: " + THE_BROKER) sys.exit() print ("Connected to broker: " + THE_BROKER) print('Sending messages...') while True: # creating the data the_data = get_data_from_sensor() # publishing the data client.publish(THE_TOPIC, str(the_data)) print("Published message with value: {}".format(the_data)) time.sleep(1) ``` :::danger 4.- modifica `sisub.py` para recibir los mensajes que genera la LoPy con este codigo. ::: --- ### Connecting to the Ubidots platform In this block you will experiment about how MQTT can be used to send data to a cloud based platform. This procedure allows you to store your data in a cloud based repository and to analyze your data with software tools made available by the used platform. For these experiments we will use the [Ubidots](https://ubidots.com/) plaftorm. You will have to first create your free account in the Ubidots platform here: https://ubidots.com/stem/ Then you have to add a **Device** (select first the "Devices" section in the top on the web page): ![](https://i.imgur.com/cmCWh9I.png =x200) choose: ![](https://i.imgur.com/M7nUX31.png =x100) and add a name, like: ![](https://i.imgur.com/Bry2Axc.png =x200) Now click on the device name and you'll get to the variables creation section: ![](https://i.imgur.com/0IXjq4Q.png =x200) click on "Add Variable" and create a "Raw" type variable: ![](https://i.imgur.com/fRrfL6O.png =x200) #### Sending data to Ubidots Now you will send data to our device **using MQTT with Python, that is not yet with the LoPy**. Take a look first to the Ubidots MQTT API Reference: https://ubidots.com/docs/hw/?language=Python#mqtt The name of the broker for educational users is **"things.ubidots.com"**. To interact with it, you will need a TOKEN. To get yours click on “API Credentials” under your profile tab: ![](https://i.imgur.com/StmHEVg.png) In my case I have: ![](https://i.imgur.com/pCmKyeQ.png) To connect to the MQTT broker you'll have to use your ``Default Token`` as the MQTT username, and `None` as password. The **topic** you have to use is **`/v1.6/devices/{LABEL_DEVICE}/{LABEL_VARIABLE}`** where you have to replace the ``API labels`` `{LABEL_DEVICE}` (e.g., mydevice_pm) and `{LABEL_VARIABLE}` (e.g., the_varpm). ![](https://i.imgur.com/QKfFnzp.png =200x) The data must be represented using JSON. The simplest format is: `{"value":10}` > the string ``value`` is fixed and defined by the Ubidots API So, summing up, to send value 25 to variable `the_varpm` of device ``mydevice_pm`` the code should look like: ```python= ... msg_to_be_sent = '{"value":25}' client.publish("/v1.6/devices/mydevice_pm/the_varpm", payload=msg_to_be_sent, qos=0, retain=False) ... ``` You'll get: ![](https://i.imgur.com/YsboDek.png) :::danger 5.- Repite los pasos anteriores con tus datos de Ubidots (credenciales y nombre del dispositivo y variable usandos) y utilizando el codigo del "producer" (`sipub.py`). ::: ## Final exercises ### Sending data from a LoPy with the PySense board :::danger 6.- Repite los pasos anteriores enviando los datos desde la LoPy, utilizando como referencia el codigo de `mp_sipub.py`. Puedes enviar cualquiera de los datos que la Pysense te ofrece... mira el codigo `reads.py` que has utilizado antes para leer datos de los sensores de la LoPy. Para conectarte al broker tienes que utilizar: `client = MQTTClient(dev_id, broker_addr, 1883, user=TOKEN, password='None')` ::: :::info Para manejar los datos JSON, python ofrece esta clase: ``` >>> import json >>> d = {'sensorId': 'temp1', 'Value': 25} >>> dj = json.dumps(d) >>> nd = json.loads(dj) ``` ::: ### Creating dashbord elements. Ubidots allows to visualize the data in various graphical ways. Go to the Dashboard section and add new widget associated with the variable of your device. ![](https://i.imgur.com/YDQBZ4z.png) See the alternatives you have and how they can be configured. :::danger 7.- Haz una captura de pantalla de la dashboard que has creado ::: --- ## Appendix: Just in case LoPy's documentation is here: https://docs.pycom.io/index.html ### Problem "pushing" a file to the LoPy Sometime you get an error message when "push"ing a file to the LoPy, something like this: ``` $ python3 -m there push led_blink.py /flash ERROR: action or command failed: execution failed: : Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: _h ``` the solution is to use the **`--force`** option. That is: ``` $ python3 -m there push --force led_blink.py /flash ``` ### Resetting the LoPy They are different ways to reset your device. Pycom devices support both soft and hard resets. A **soft reset** clears the state of the MicroPython virtual machine but leaves hardware peripherals unaffected. To do a soft reset, press `Ctrl+D` while in the REPL. A **hard reset** is the same as performing a power cycle to the device. In order to hard reset the device, press the reset switch or run: ```python >>> import machine >>> machine.reset() ```