# Avoid heat, get an email when you need to roll down your curtains!
*made by Ellen Schuchert, es225ib*
In this tutorial, an OLED display, temperature sensor and a photoresistor will be connected to the Lopy 4 microcontroller. The display will show the information collected by the sensors. Additionally, the microcontroller will send the data to the Ubidots platform. Ubidots allows the data to be reachable from anywhere and specified events can trigger notifications. An email can be sent to notify a user when the temperature in a room is too hot, so that the user can roll down the curtains.
Estimated time: 4-7 hours
## Objective
I live in a cabin separated from the main house. Since I spend most of my time outside of my room, I forget that the temperature can get extremely high if I forget to roll down the curtains during the summers. However, I don't want to keep the curtains down all the time as that would kill my light craving plants. This project solves that problem. I will now receive an email when I need to roll down the curtains (and start my fan!). Furthermore, I can map out where my different plants thrive the best (different plants want different light conditions), by moving around the device in the room. I will also be able to find out what time my room is the hottest/coolest, so, during the winter instead of rolling down my curtains, I could rather be notified when I should start the radiator.
Adding the OLED display was not out of necessity or better convenience, although it is nice to easily see the temperature in the cabin. I added it due to me wanting to understand how the I2C protocol works better, and because it was something I have at home that I have never used before.
## Material
| Component and usage | Retailer | Price |
| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | --------- |
| OLED display 0.96″ 128×64 SPI/I2C, used to dispaly the sensor data | [Elecktrokit](https://www.electrokit.com/produkt/lcd-oled-0-96-128x64/) | 232,5 kr |
| Temperature sensor, MCP9700, used to mesure the temperature (there are alot of diffrent ones more accurate than this one) | [Electrokit](https://www.electrokit.com/produkt/mcp9700-e-to-to-92-temperaturgivare/) | 8 kr |
| Lopy4 with expansion board 3.1, the microcontoller used | [Pycom](https://pycom.io/product/lopy4-multipack/) | 632.82 kr |
| LDR, A light sensitive resistor used to measure the light | [Electrokit](https://www.electrokit.com/produkt/fotomotstand-cds-2-5-kohm/) | 8 kr |
| 1k Resistor, used as an pull down resitor | [Electrokit](https://www.electrokit.com/produkt/motstand-metallfilm-0-6w-1-1kohm-1k/) | 3 kr |
| Breadbord, used to connect everything more smoothly | [Kjell&Company](https://www.kjell.com/se/produkter/el-verktyg/elektronik/elektroniklabb/luxorparts-kopplingsdack-400-anslutningar-2-pack-p36283) | 89 kr |
| Wires, used to connect everyhting on the breadboard / expasion board | [Kjell&Company ](https://www.kjell.com/se/produkter/el-verktyg/elektronik/elektroniklabb/kopplingskablar-hane-hane-65-pack-p87212) | 90 kr |
| Micro usb cable, used for to connect to your computer and to connect to a socket | - | - | -
| Computer (Windows) | - | - |
**Total Price: 1063,32 kr**
Comments:
Of course, any corresponding component is valid and works. For instance, I have not used the OLED display linked in the chart since I already had the equivalent at home. If you choose to use something different, please check the datasheet for that sensor/component to make sure everything still works. For example, you might have to use some other calculations to get the correct temperature measurements if you have another temperature sensor.
I'm assuming you have a computer since you are reading this tutorial, and you probably have an old micro USB cable at home.
## Computer Setup
### Step 1 - Mounting
Start with mounting on the Lopy4 device on the expansion board correctly. 
*Picture credit: LNU university, Fredrik Ahlgren, [link](https://www.youtube.com/watch?v=YjeQ934ar7Q)*
The LED should be above the USB connector. Then connect your computer to the expansion board with the micro USB cable.
### Step 2 - Updating the firmeware
Since my expansion board was brand new, I did not need to update its firmware. If you need to update your expansion board see [this link](https://docs.pycom.io/updatefirmware/expansionboard/).
1. Download and install the firmware updating tool for your OS. [Link](https://docs.pycom.io/updatefirmware/device/)
2. Follow the instructions, I choose the pybytes version, so that I easily could be connected to pybytes. To do this you should [create a pybytes acount](https://sso.pycom.io/login/?client_id=pycom&redirect_uri=https%3A%2F%2Fpyauth.pybytes.pycom.io%2Fauth_code%2Fcallback&scope=profile&response_type=code&state=pybytes-browser) and [add the Lopy4 as a device](https://alepycom.gitbooks.io/pycom-docs/content/chapter/pybytes/connect/intro.html). (This is not needed for this tutorial since pybytes is not the platform we are going to use.)
3. Unplug your device.
For more information about updating the firmware, please check out [this link](https://docs.pycom.io/updatefirmware/device/).
### Step 3 - IDE and other requirements
I used Atom as my IDE.
1. If you don't already have the latest version of Python, make sure you [download that](https://www.python.org/). OBS! Be sure you check the box saying Add to PATH.
2. You will also need Node.js, [link.](https://nodejs.org/en/)
3. Download and install Atom, [link](https://atom.io/).
4. Open Atom and go to settings and den press +install.
5. Serch for the Pymakr plugin and install it! when you are done it should look like this!

I got an error messages when trying to install pymakr plugin, in order to solve this i had to download and install [Visual studio](https://visualstudio.microsoft.com/downloads/). Use the Desktop development with C++.

*Picture credit: LNUs IOT Tutorial 1, [link](https://hackmd.io/@lnu-iot/rk4qNlajd).*
PS! You might have to restart your computer after all the downloads.
### Establish a connection
1. Connect your expansion board with your computer and start Atom. It should connect automatically.
2. You should see this when it's ready

3. To run a program simply press the uplod button.
## Hardware Setup
This setup is not a long term solution, it is more of a prototype. In order to make it more long term, I would have liked to have a 3D-printed case for everything to function as protection. I would also have liked to solder some components onto a PCB prototype board to make certain everything stays connected. However, the prototype set-up I have here works very well and comes with its own set of advantages. As an example, you can reuse all components if you find another fun project you would like to do.


*Picture credit: Pycom, [link](https://docs.pycom.io/gitbook/assets/lopy-pinout.pdf).*
I connected GND to the blue power rail. And the 3V3 to the red power rail. Since I have multiple components this makes it easier to connect all of them to GND and 3V3.
### OLED display
I connected the VCC to the red power rail and GND to the blue power rail.
In this tutorial, we will be using the I2C protocol for the microcontroller to communicate with the OLED display. This means that we will need a data wire (SDA) and a clock wire. By checking the pins on the Lopy4 we can see that we could use pins P10 & P9 as these are the default pins used for I2C communication on the Lopy. I, therefore, connected the SCL to P10, and SDA to P9 as seen in the fritzing schematic.
### Temperature sensor
I connected GND (the right pin when facing the flat side of the sensor) to the blue power rail and the middle pin to P16 (this is the signal pin). Lastly, I connected the left pin to the red power rail.
### Photoresitor
I connected one pin to the red power rail (3V3) and the other pin to a 1k resistor connected to GND (the blue power rail) as well as to P15. See the fritzing schematic.
I used the 1k resistor as a result of testing a lot of different resistors. The 1k resistor made the gathered values intervals correlate the best with my perceived light detection. I suggest you also try using different resistors to see which one works best for you.
### Power supply
Since I am indoors and don't pay for my electricity bill (thx mom and dad) and I also have a very long micro USB cable, I decided to simply connect the micro USB port directly to a socket.
## Transmitting data
I am using wifi and sending via the webhooks protocol (HTTP), data in Jason format to the Ubidots REST API. The data is stored in Ubidots cloud.
Since I have connected my device directly to a socket, power is not a problem. I, therefore, thought it would be fun to update the OLED display quite often (every three minutes). However, Ubidots have restrictions on how much data you can send per day (4000 dots per day, 1 dot = 1 variable and its value) if you have the free STEM version, and I therefore only send data every nine minutes, which is more than enough since the temperature and light conditions do not change so quickly. If you were to use a battery, in order to save energy I would say it's fine to check and send the sensors data every 20-30 minutes.
## Code
If you would like to download my code directly here is a [link](https://github.com/piggpingvin/Avoid_Heat). If you haven't already, please [create a ubidots acount.](https://stem.ubidots.com/accounts/signin/)
Create a new project folder and add two files main and boot as well as a lib folder. In the lib folder create three new files, urequests, keys and ssd1306. Do not forget the .py at the end of every file. The structure should now look like this:

### Lib- folder
#### keys
The keys file is just three varibles containing your sensitive information. Here is[ a guide to find your ubidots tokein. ](https://help.ubidots.com/en/articles/590078-find-your-token-from-your-ubidots-account)
```` python
Wifi_SSID = 'enter you SSID here'
wifi_password = 'enter your wifipassword here'
ubidots_tokein = 'enter your udidots toiken here'
````
#### ssd1306
Copy the [ssd1306 libary](https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py), and change this line in write_cmd in the I2C class (it should be line 123). Otherwise an nasty error accors that took me three days to solve (big thanks to the TAs!).
```` python
self.i2c.writevto(self.addr, self.write_list)
````
to this
```` python
self.i2c.writeto(self.addr, b'\x40'+buf)
````
for more information about this error see [here](https://forum.pycom.io/topic/3886/using-i2c-ssd1306/3). If you copyied my code this is not necceray.
#### urequests
Copy [the urequests libery](https://github.com/piggpingvin/Avoid_Heat/blob/main/lib/urequests.py).
### boot
Copy this into your boot file.
```` python
from machine import UART
import machine
import os
uart = UART(0, baudrate=115200)
os.dupterm(uart)
machine.main('main.py')
````
### main
````python
from network import WLAN
import urequests as requests
import time
from keys import ubidots_tokein, wifi_password, Wifi_SSID
import machine
from machine import I2C, Pin, ADC
import ssd1306
import pycom
TOKEN = ubidots_tokein # Put your ubidots tokien here
DELAY = 180 # Delay in seconds
delaySendTime = 0 # Delay for sending data to ubidots
celsius = 0
light = 0
# ==============================================================================
#Connectes to wifi
wlan = WLAN(mode=WLAN.STA)
wlan.antenna(WLAN.INT_ANT)
# Assign your Wi-Fi credentials
wlan.connect(Wifi_SSID, auth=(WLAN.WPA2, wifi_password), timeout=5000)
while not wlan.isconnected ():
machine.idle()
print("Connected to Wifi\n")
pycom.heartbeat(False)
````
Then its time to build the jason package and send the request. To do this I followed [this tutorial](https://help.ubidots.com/en/articles/961994-connect-any-pycom-board-to-ubidots-using-wi-fi-over-http) (it also helped me set up the wifi connection). However, I made some adjustments to the code since they send three varibles as well as some context(lat and lng). I only need to send two varibels and their values.
````python
def build_json(variable1, value1, variable2, value2):
try:
data = {variable1: {"value": value1},
variable2: {"value": value2,}}
return data
except:
return None
# Sends the request. Please reference the REST API reference https://ubidots.com/docs/api/
def post_var(device, value1, value2):
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, "Light", value2)
if data is not None:
print(data)
req = requests.post(url=url, headers=headers, json=data)
return req.json()
else:
pass
except:
pass
````
If you would like to change the varible names do that in this line
````python
data = build_jason("change name here", value1, "change name here", value2)
````
I then set the pins, aktivated an I2C channel and declered a display
````python
pycom.rgbled(0xFF0000) # RED color, the built in LED is used to se if anything goes wrong in the set up in the "field"
#set the temperature pin
adc = machine.ADC(bits=10)
tempPin = adc.channel(pin='P16')
#set the light LightSensorPin, code taken from the example given by the TAs
lightPin = Pin('P15', mode=Pin.IN) # set up pin mode to input
analogLightPin = adc.channel(attn=ADC.ATTN_11DB, pin='P15') # create an analog pin on P16; attn=ADC.ATTN_11DB measures voltage from 0.1 to 3.3v
i2c = I2C(0, I2C.MASTER, baudrate=100000, pins=("P9","P10"))
display = ssd1306.SSD1306_I2C(128, 64, i2c)
pycom.rgbled(0x00FF00) #green
#=============================================================================
# funktions used in main code
# draws desired figure on the display, this function is written by Mohammad Qasem a TA
def draw_figure(display, shape, x=0, y=0, c = None):
for i, row in enumerate(shape):
for j, c in enumerate(row):
display.pixel((j+x), i+y, c)
# erases desired figure, I modifed Mohammads function
def erase_figure(oled, shape, x=0, y=0):
for i, row in enumerate(shape):
for j, c in enumerate(row):
c = not c
oled.pixel(j+x, i+y, 0)
# cheks the temperature 5 times and returns the avrage
def check_temp():
avrage_Temp = 0
celsius = 0
for i in range(5):
millivolts = tempPin.voltage()
celsius = (millivolts - 400.0)/19.5 # I have used the values for the MCP9701 as I found that to work better, consider changing to - 500.0 and /10 instead.
avrage_Temp += celsius
time.sleep(0.2) # wait a litte
return int(avrage_Temp/(5))
# cheks the photoresistor 5 times and returns the avrage
def check_light():
avrage_Light = 0
for i in range(5):
avrage_Light += analogLightPin.value()
time.sleep(0.2)
return int(avrage_Light/5)
````
To make my screen a bit more personal I decided to add some figures on my display. There is always a panda, and if the temperature is > 30 or < 10 there is either a sun or a snowflake above its head. Since the code for these figures are not very interesting (just an object with a bunch of 0 and 1 depending on what pixel should be turned on) I won't bring it up here, I instead advise you to check out the full [main file here](https://github.com/piggpingvin/Avoid_Heat/blob/main/main.py).
Nevertheless, after inverting the screen and drawing out the panda and the permanent texts it is time for the main code.
````python
while True:
display.text(str(celsius), 54, 30, 0) # clear old temp value from screen
display.text(str(light), 52, 50, 0) # clear old temp value from screen
celsius = check_temp() # chek temperature value
light = check_light() # get lightValue
if celsius > 30: # dispaly sun or snowflake depending on the temperature (30 or 10 degree celsius)
draw_figure(display, sun, 5, 5)
elif celsius < 10:
draw_figure(display, snowflake, 5, 5)
else: # clear sun or snowflake if in normal range
erase_figure(display, snowflake, 5, 5)
erase_figure(display, sun, 10, 10)
print("Temperature is ", celsius, '\n', "Light is ", light) # just for controll
display.text(str(celsius), 54, 30, 1) # display temp on display
display.text(str(light), 52, 50, 1) # display temp on display
display.show()
if delaySendTime == 3 : #sends the collected data to ubibots every nine minutes
post_var("pycom", celsius, light)
delaySendTime = 0 # reset the delaySendTime
delaySendTime += 1 #Delay for sending data to ubidots
time.sleep(DELAY) #Sleeps
````
## The Platform & Presenting the data
Ubidots is an IOT-platform that allows people to use REST API cloud services to gather, view and analyse data. It saves my data for a month since I only use the free STEAM version, and it saves the data automatically after receiving it.
My opinon is that bidots displays the data very nicely and it is very easy to reach from anywhere(you can download an app and reach your dashboards from your phone if you'd like). Another good thing about Ubidots is that setting upp events is super easy.
To view your data in a nice way you simply press data in the middle top and then dashboards. You can add more dashboards, perhaps for different devices, by pressing the three horizontal lines in the left corner. Press the plus button in the corner to add any widget you might want.This is what my dashboard looks like from my computer:

To edit any widget on your computer simply press the three vertical dots in the top right corner of the said widget.
Since the light variables values by them selfs do not give much information, I decided to add a gauge, activate the Pointer setting and then I removed the value interval that poped up. Removing the values was harder than I thought, as it required a custom style.
This is the Jason formatted code I used for the custom style.
````
{
"color": "#ffffff",
"header": {
"color": "#5e5e5e",
"backgroundColor": "#ffffff"
},
"fontSize": 14,
"borderRadius": 0,
"backgroundColor": "#ffffff"
}
````
This is what my dashboard looks like from my phone:

### Events
Setting up an event is super easy.
1. Simply press data in the top middle and then select events. To create a new event press the plus button in the right corner.

2. Then select which variable (e.g temperature or light) and what you want to be notified by (a value, position or if the device has been inactive). If you have a Ubidots account that you pay for you can add conditions (and/or). As an example, you might not want to receive an email to roll down your curtains, if your light sensor says that there is not much light.

3. Then, choose how you want to be notified. I chose email. I, therefore, had to add my email address and the message that would be sent.

4. Lastly, you can choose if you do not want to be notified any certain days or hours. I chose to not be notified while I am asleep.

## Final Project
I am really happy with it, and the panda looks super cute!

Here I have cheated a bit so that you can see the sun!


(I have since changed the alert to only notify me when the temperature is >30 degress celcius)
{%youtube XDW7M17mBVQ%}
(Yeah it is not the best film I have ever made)
### Future improvments
First of all, I would make the setup a bit more permanent, I currently have issues when I move the project around as the wires "jump" out of their rightful place. Also on the OLED display, you could make an indicator for how much light there is instead of just displaying the value as it currently does. Another idé that would be fun is to add is a stepper motor or a servo to automate the curtains.