# Tutorial on how to build a connected thermometer **By Måns Sjöstedt (ms226jb)** [ToC] ## Introduction The following is a tutorial on how to assemble and connect your device to a temperature sensor and to a cloud platform. This will be a rather simple build using only one sensor, which you will be able to monitor from the cloud platform. The tutorial will also include how to set up the cloud platform so that it sends an email to you, alerting you of an event triggered by the data from the sensor. With this build you will be able to monitor the temperature of the location of your device around the clock. You can use the information to be more aware of the temperature in a specific room, and perhaps use a heater or a window to adjust the temperature in order to fulfill the needs of that specific room. For this particular build we will use a LoPy 4 by Pycom, along with an expansionboard 3.0 also from Pycom. With this we will connect over wifi to the cloud platform Ubidots. On Ubidots we will display the data and set up our email alerts. The build will take approximately 2-3 hours to complete. ### Materials | Materials | Links | Cost | | ----------------- |----------------------- |-------- | | LoPy 4 | [:link:][lopy 4] |366 SEK | | Expansionboard 3.0| [:link:][ExpBoard3.0] |167 SEK | | Wires | [:link:][wires] |60 SEK | | Breadboard | [:link:][breadboard] |90 SEK | | Temperature sensor| [:link:][temp] |8 SEK | | Battery pack (AAA) | [:link:][Batteri] |29 SEK | [lopy 4]: https://pycom.io/product/lopy4/ [Expboard3.0]: https://pycom.io/product/expansion-board-3-0/ [wires]: https://www.kjell.com/se/produkter/el-verktyg/elektronik/elektroniklabb/luxorparts-kopplingskabel-med-quick-click-hane-hane-20-cm-p87332 [breadboard]: https://www.kjell.com/se/produkter/el-verktyg/elektronik/elektroniklabb/kopplingsdack-400-hal-p36285 [temp]: https://www.electrokit.com/produkt/mcp9700-e-to-to-92-temperaturgivare/ [Batteri]: https://www.electrokit.com/produkt/batterihallare-3xaaa-med-strombrytare-och-jst-kontakt/ The prices of the items above are not including the price of shipping. The cost, without shipping, would land on a total of 691 SEK. The temperature sensor is a MCP9700 TO-92, which is a cheap and easy to use analog temperature sensor. I chose to work with a LoPy 4 and the extensionboard because it runs micropython, and also have further capabilities with LoRa and Sigfox. This is useful if there is a need to use this device in a location that doesen't have a wifi setup. The breadboard is needed to connect the sensor to the LoPy 4. Another solution could be to buy different wires to connect the sensor directly to the LoPy. However, the breadboard allows for the possibility to connect more sensors, depending on whether or not you would want to get more data from the sensor. The batterypack is practical to have so you don't need to power your device by USB, since that would limit the number of possible locations for your device. ### Before you start Before you start setting up your device we will need to install some programs to work with. Firstly we will install an IDE(Integrated Development Environment), which can use the Pymakr plugin. This plugin is avaible for both [Atom](https://atom.io/) and [VScode](https://code.visualstudio.com/). For this tutorial I chose the Atom since it's what I'm most familiar with. Once you have installed Atom go to File -> Settings -> Install and searh for the Pymakr plugin and install it. **Pymakr** will be the tool we will use to interact with, and upload, code to our LoPy so make sure to get this working before you continue with the tutorial. Since we will be running our device with Micropython you will also need to install [Python](https://www.python.org/downloads/) and [Node JS](https://nodejs.org/en/download/). Lastly you will need to register for an account at [Pybytes](https://pybytes.pycom.io/), where we can install the Pycom updater firmware to make sure we have the latest firmware on our device. There is a very good tutorial for this in [pycoms own docs](https://docs.pycom.io/gettingstarted/installation/). ## Connecting the different parts The pycom board needs to be attached to the expansionboard. While doing this, make sure that all the pins are lined up properly and that the white LED on the left side of the pycom board is lined up with the micro-USB conenctor on the expansion board. Connect the sensor, like the image below, where the orange wire is connected to the GND(Ground) pin on the LoPy, the white wire is connected to the 3V3 pin on the LoPy and the yellow cable is connected to pin 20. In this case there is no need for any resistors on the build since the MCP9700 TO-92 temperature sensor runs well in the range 2.3-5.5 VDC where we are supplying it with 3.3 VDC. ![](https://i.imgur.com/LDne6Hb.jpg) The battery pack can be connected to the white casing in the top right corner of the expansion board, as in the picture above. ## Cloud platform This tutorial will be using the [Ubidots](https://ubidots.com/) cloud platform since it's easy to use and set up a new device to. It's also free, but you can change to other plans with higher capacities if necessary. It also allows the user to send data over wifi, LoRa, and plenty of other alternatives, so it should be practical no matter what way you want to send the data. With the free plan you can also send 4000 points of data every 24 hours. This allows for a possible rate of about 2 times / minute, which will be enough for this project. ## Code We will need 3 different python files, which will need to be in a specific project structure. Set it up in the following way: ``` .project folder/ | +--lib | +--urequests.py +--main.py +--boot.py ``` We will start with the shortest file, the boot.py file: ``` #this code is needed to transform the data we will send from parallel data to serial data using UART from machine import UART import machine import os uart = UART(0, baudrate=115200) os.dupterm(uart) machine.main('main.py') ``` The next file will be the urequests.py file in the lib folder. This code is used to properly use and mange HTTP/HTTPS requests, which will be the protocol used to send our data to the Ubidots cloud platform. The reason I'm using the HTTP/HTTPS protcol is because of the rather simple implementation and also because of the integration with Ubidots. ``` import usocket class Response: def __init__(self, f): self.raw = f self.encoding = "utf-8" self._cached = None def close(self): if self.raw: self.raw.close() self.raw = None self._cached = None @property def content(self): if self._cached is None: try: self._cached = self.raw.read() finally: self.raw.close() self.raw = None return self._cached @property def text(self): return str(self.content, self.encoding) def json(self): import ujson return ujson.loads(self.content) def request(method, url, data=None, json=None, headers={}, stream=None): try: proto, dummy, host, path = url.split("/", 3) except ValueError: proto, dummy, host = url.split("/", 2) path = "" if proto == "http:": port = 80 elif proto == "https:": import ussl port = 443 else: raise ValueError("Unsupported protocol: " + proto) if ":" in host: host, port = host.split(":", 1) port = int(port) try: ai = usocket.getaddrinfo(host, port, 0, usocket.SOCK_STREAM) except: ai = usocket.getaddrinfo(host, port) s = usocket.socket() ai = ai[0] try: s.connect(ai[-1]) if proto == "https:": s = ussl.wrap_socket(s, server_hostname=host) s.write(b"%s /%s HTTP/1.0\r\n" % (method, path)) if not "Host" in headers: s.write(b"Host: %s\r\n" % host) # Iterate over keys to avoid tuple alloc for k in headers: s.write(k) s.write(b": ") s.write(headers[k]) s.write(b"\r\n") if json is not None: assert data is None import ujson data = ujson.dumps(json) s.write(b"Content-Type: application/json\r\n") if data: s.write(b"Content-Length: %d\r\n" % len(data)) s.write(b"\r\n") if data: s.write(data) l = s.readline() #print(l) l = l.split(None, 2) status = int(l[1]) reason = "" if len(l) > 2: reason = l[2].rstrip() while True: l = s.readline() if not l or l == b"\r\n": break #print(l) if l.startswith(b"Transfer-Encoding:"): if b"chunked" in l: raise ValueError("Unsupported " + l) elif l.startswith(b"Location:") and not 200 <= status <= 299: raise NotImplementedError("Redirects not yet supported") except OSError: s.close() raise resp = Response(s) resp.status_code = status resp.reason = reason return resp def head(url, **kw): return request("HEAD", url, **kw) def get(url, **kw): return request("GET", url, **kw) def post(url, **kw): return request("POST", url, **kw) def put(url, **kw): return request("PUT", url, **kw) def patch(url, **kw): return request("PATCH", url, **kw) def delete(url, **kw): return request("DELETE", url, **kw) ``` The last file will be the main.py file where we receive and calculate our data from the sensor and use the JSON format to send it to Ubidots. Comments in the code are marked with a hashtag(#). In this part of the code you will need you API token for Ubidots aswell as your wifi credentials. To get the API token you will need to register for a free account at [Ubidots](https://ubidots.com/stem/). You will then find the API token under Profile -> API credentials. ``` from network import WLAN import urequests as requests import machine import time TOKEN = "your--Ubidots-API-token" DELAY = 30 # Delay in seconds in how often you will send your data #Base values for the sensor calculations found trough the data sheet for the sensor T_C = 10 #°C T_0C = 500 #mV #set the mode to station and initiate the wlan wlan = WLAN(mode=WLAN.STA) wlan.antenna(WLAN.INT_ANT) #sets the data pin to pin 20 and defines it as an analog pin adc = machine.ADC() apin = adc.channel(pin='P20') val = apin() # Assign your Wi-Fi credentials wlan.connect("wifi-SSID-here", auth=(WLAN.WPA2, "wifi-password-here"), timeout=5000) #loop to check if succsessfully connected while not wlan.isconnected (): machine.idle() print("Connected to Wifi\n") # Builds the json to send the request def build_json(variable1, value1): try: data = {variable1: {"value": value1}} return data except: return None # Sends the request to ubidots. def post_var(device, value1): try: url = "https://industrial.api.ubidots.com/" url = url + "api/v1.6/devices/" + device headers = {"X-Auth-Token": TOKEN, "Content-Type": "application/json"} data = build_json("temperature", value1) if data is not None: print(data) req = requests.post(url=url, headers=headers, json=data) return req.json() else: pass except: pass #the loop that will be running while True: val = apin.voltage() #get the voltage value from the data pin temp = (val - T_0C) / T_C #calculation to get the correct value in °C post_var("pycom", temp) time.sleep(DELAY) ``` After entering the code in the different files, and making sure that you have the proper file structure, all you need to do to start sending data is uploading the files to your pycom device! You should now be sending data to Ubidots and when logged in to Ubidots your pycom device should now appear under Devices. ## Presenting the data Now that the device is connected to Ubidots it's time to present the data we are sending. This will be done using two different widgets; a thermometer and a line chart. To do this, use the Ubidots webpage and go to Data -> Dashboard then click the + icon to add a new widget. Let's start with the thermometer, so add a thermometer widget. As the data source you want to click 'add variable' and then select the temperature varaible from your pycom device. For the next widget, the line chart, you want to go ahead and choose that from the selections of widgets. Again you want to select the same data source from your pycom device. For this widget I chose to go with 2 decimal points, in order to not get overwhelmed with more or less meaningless decimals. This can be changed if you want the measurments to be more precise. Go ahead and name the Y-axis '°C' and turn on SI prefix. You can then decide on a suitable interval for the Y-axis, or just let it be automated, depending on the values. You will now have a way to look at the most recent value, using the thermometer widget, and a way at looking at a bit more historical values, using the line chart. When using the free plan for Ubidots your data points will be stored for 30 days. If you wish to keep them for longer you will need to upgrade to one of the paid plans. Your dashboard should now look something like this: ![](https://i.imgur.com/F5oNiBO.png) ## Getting alerts by email For this last part I will guide you through how to set up an event and trigger for your data, using Ubidots, to get an automated email sent to you! For this you will, on Ubidots webpage, need to go to Data -> Events and press the + icon once again. Then, select your temperature variable from your pycom device and decide on a value that will trigger the following event. I set mine to trigger if a value was greater than 25 °C for five minutes. Then press the right arrow button and add a new action. Select 'send Email', enter you email address and press the green check mark. Then press the right arrow button again and decide during what times it may send an email and lastly press the green check mark again. Now an alert email will be sent to you once the temperature reaches your assigned threshold value. ## The final product This is a very beginner friendly and easy project to do, and you can still get some interesting data from your sensor. However, reading only the temperature might not always be interesting on its own. It could therefore be a good idea to expand this build with more sensors to get other sensor data, for example, humidity. Now everything should be set up properly and running smoothly! Below you can see how my build ended up looking! ![](https://i.imgur.com/bhqS91w.jpg)