Summer course: Applied IoT (2022) Created by: Eric Jansson (ej224de)
UPDATE As of early 2023, Heroku’s free services are no longer available. That means that the website part of this tutorial will NOT be possible without a paid plan.
Contents
Electronics: Wires and Components (30 min)
Optional: Soldering and Casing (90 min)
Curtains: Line Setup (30 min)
Device Code: Modifications and Upload (30 min)
Node-Red and MongoDB: Setup (30 min)
Website: Deployment (30 min)
Estimated Total Time: 4 hours
Introduction
Summer is the best time of the year. However, the heat can become too much and turn your home into a place of discomfort. To help prevent this issue we can close our curtains to block out the nefarious sun. Unfortunately this creates a dark and moody setting which is not how we want to spend our summer.
So we want sunlight but only when it’s not too hot inside.
Automating the curtains will help us get an optimal time of natural light but also a less extreme indoor temperature. The perfect indoor summer!
Quick description
The curtains will be controlled by pulling two different fishing lines back and forth to open and close them. A motor will be installed to the other end and together with a microcontroller it will open and close the curtains automatically. This will all be based on a reading from a temperature sensor. If it’s too hot inside the curtains will close and vice versa. The status of the balcony door (open/closed) will also be measured and sent to a database together with the other data such as temperature, curtain status and the time of day.
By storing and visualizing statistics we can also analyze the data. We can look for temperature trends depending on the balcony door- or curtain- status. This could possibly tell us if this method is efficient or not.
Project Objective
The reason I chose this project is because I mainly wanted to automate my curtains. Since my apartment gets increadibly hot, I thought a temperature sensor would be a perfect combination with this idea.
The main purpose is to make the apartment avoid the extreme heat during hot days and still get the most natural light. But it also would mean I won’t have to run around as much. To open and close the curtains all the time depending on the weather and indoor temperature.
I also think the data will help me understand how much the curtains and balcony door helps the situation. A lot of people, including me, would open a door when it’s hot inside no matter the outside temperature, in hopes that it will cool down their home. This data will hopefully give me an understanding of how the door and curtains affects the indoor temperature. Both positively and negatively.
Hardware and Electronics
Temperature Sensor
Model: TMP36
This is the sensor I’m using to measure the temperature in my apartment.
Price
Amount required
Link
Datasheet
46 SEK
1x
There are other temperature sensors that might be cheaper or even better but this worked fine for me.
Stepper Motor
Model: 28BYJ-48
The motor is what opens and closes the curtains by spinning either clockwise or counter clockwise.
Price
Amount required
Link
Datasheet
35 SEK
1x
Wires
Wires to connect everything together.
Package Price
Amount required
Link
Header Type
29 SEK
2x
Female/Male
29 SEK
10x+
Male/Male
Note that I’m using only 2x Female/Male wires in the whole curcuit. I actually made my own 2 wires myself by inserting a pin into a Female/Female wire. The resulting wire is not perfect but it’s an option if you don’t need a full package.
PyCom Fipy
This device is the brain of the project. It’s what controls all our components and sends the data to the database.
Price
Amount required
Link
Datasheet
1349 SEK
1x
The Pysense expansion board and USB cable is included in the link.
LiPo Battery
Battery to power the PyCom device.
Price
Amount required
Link
149 SEK
1x
Resistor
Resistor: 220 Ohm
Price
Amount required
Link
1 SEK
1x
We actually only need one resistor but I would highly recommend buying a set of all kinds of resistors instead. A kit is not too expensive and it doesn’t hurt to have a few spares incase it’s destroyed.
Breadboard
A breadboard to connect everything together. Easy to use and doesn’t require soldering.
Price
Amount
Link
59 SEK
1x
Circuit board
This board is used for the soldering. It will connect our wires and components permanently but it also helps by keeping the wires connected firmly.
Price
Amount
Link
100 SEK
1x
There is only 1x required for the soldering even though the package includes 10x. The soldering of this circuit board is actually not required for the project, but it helps.
Materials
Spool for Stepper Motor
This is the only part that’s 3D printed in this project. This 3D model file is specifically made for the stepper motor 28BYJ-48.
This is the thread I’m using in the curtains. It’s a nice choice because it’s almost invisible on my white walls and it also pretty strong. This will be connected to the spool and motor to open and close the curtains.
Price
Link
59 SEK
Program Setups and Installations
There will be some downloading during the tutorial. So I’ve gathered a list of everything download or account related in this section.
This makes it easier to setup everything that needs a download/account before we get to that part.
Notes:
The first time a program/account is required, there will be a shortcut to this section again.
There is an installation walkthrough for everything.
There will be guides to creating accounts for everything as well.
Name
Download
Account
PyCom Firmware
Atom
Node.js
Node-Red
GitHub
MongoDB
Heroku
PyCom Firmware
PyCom Fipy Firmware update
This section is taken from this tutorial. For a slightly more descriptive version you can visit that one instead.
You should not interrupt the firmware update process because it could damage the board, meaning:
Make sure that your laptop is connected to power and/or fully charged.
Do not disconnect the USB cable during firmware update process.
Do not press the buttons on board or expansion board during firmware update.
Do not cancel the firmware update process.
Follow these steps:
Step 1: Connect the PyCom Expansion board to the computer with a USB cable.
Step 2: Download the Firmware Update from this link.
Step 3: Install the downloaded file and open it.
Follow these steps to update the firmware:
Press Continue… and then Continue again…
In port section, select the COM port to your PyCom Expansion board (Could be find in Windows Device Manager)
In type section, select the development and press continue…
It should automatically detect your board type, then select Erase during update and CONFIG partion & NVS Partition, and finally press continue.
It takes some time and finally you can press on Done.
The firmware is now installed on your PyCom device.
To create a Heroku account we should visit Heroku’s website here and Sign up. Create the Heroku account by filling in the fields. Select the Role and Country/Region. As Primary development language we can select Node.js. Press CREATE FREE ACCOUNT when it’s all done.
Then we’ll get a confirmation link by email that we’ll have to click. That will open a new internet tab where we can create our new password.
When the firmware is updated we can move on to connecting some components.
Connecting all the components
In this section we’ll see the final schematics and then we’ll explore every bit of it, piece by piece.
The project can be divided into 5 parts:
Pysense development board
Temperature sensor
Door status
Motor
Manual switch
Important:
Put the batteries in LAST! They can damage your components if you’re not careful.
Triple check the connections before you put in the batteries. ESPECIALLY the 9V battery.
If you want to rearrenge the wires, DISCONNECT the batteries first to avoid damage.
Some pins are interchangeable but that also means you’ll have to change some code. The pin configuration can be found at the FiPy, PyCom documentation.
Here are the final schematics for this project.
Pysense development board
This connection is kind of weird. It would be preferable just to mount the Fipy onto the Pysense. But if we do that we can’t connect the pins to the breadboard in an efficient way.
Lucky for us, we dont need to connect every single pin between them. We just need to connect the 4 pins that are shown in the picture.
The main purpose of this part is that it enables us to connect power easily through the LiPo battery and also to upload code whenever we want to.
Temperature sensor
This is how we measure the temperature. We can see that ground is connected as the black wire, 3.3V is connected as the pink wire and the green one is our analog signal. The analog signal is what tells us what the temperature it is.
Door status
The button here represents the balcony door. The switch “is pressed” if the door is closed and vice versa. This allows the microcontroller to check the pin input value, to see if the door is opened or closed.
This has no real impact on the project in a physical sense. It’s just here to record data that can be analysed together with the temperature.
In real life this is actually done by connection tinfoil with the help of a clip. It’s low quality but it works reliably.
Motor
The motor is what controls the movement of the curtains. Depending on the direction of the motor it will either use the spool to wind-up or wind-down the string.
During the experimentation of this project I realized that the voltage from the pycom wasn’t enough to power the motor efficiently. That’s why there is a 9V battery to help out.
Manual Switch
Since the motor is controlled by the weather, this button will help if we want to control the curtain status manually.
It’s designed so that when the pycom is sleeping, it will wake up after pressing this button. It ALWAYS switch the status of the curtains no matter the temperature.
From OPENED to CLOSED or CLOSED to OPEN
Otherwise the FiPy functions like normally. It will still read the temperature and send it to the server.
Electrical Calculations
Power Consumption
TMP36 = 0.05 mA
PyCom Wifi = 120 mA
PyCom Deepsleep = 15 mA
Motor (Active) = 200 mA
The temperature sensor has power consumption but it’s so low it’s pretty much negligible during calculations. It has zero impact to the results because of rounding.
TimeAwake = 60 sec (1 min) TimeSleeping = 1200 sec (20 min) CurrentAwake = 120.05 mA CurrentSleeping = 0.15 mA BatteryCapacity = 1500 mAh
9V batteri: 550 mAh
motor: 200 mA
Motor spins for 22 seconds each
550 / 200 = 2.75 Hours => 9900 Seconds => 9900 / 22 = 450 spins
The device will last about 10 to 11 days with the current program and selected LiPo battery.
Soldering
This is what the curcuit looks like when connected.
It looks very messy and the wires are very easily dislodged. That could mean a possible short. This is very scary, especially because of the 9V battery. That could damage the FiPy very easily.
With all of that in mind it feels only natural to solder everything together.
This is what it looks like after it’s been soldered together.
There is still a feeling of clutter but it’s way better than before. However the risk of dislodging wires is minimal right now.
Casing
The motor will need something that supports it when it spins. So my thought was to make some sort of casing for the project. I decided to just take an old phone-box that I cut out for the motor and wiring. Most boxes would work here but it’s preferable if it’s a sturdy one.
This is how it looks from the outside.
Inside look.
The 9V battery is put in a static proof bag for safety reasons. The underside of the circuit board also got a static proof bag taped to it to ensure there wouldn’t be any shorts.
This is also nice because it gives it a discrete look while lying around in my apartment.
Curtain setup
For the curtains I’ve setup the open and close mechanism with the fishing line.
Pulling one line will open and pulling the other will close it.
The image shows how the lines affect the curtains. Both of them are pulled the same direction but they do the opposite to eachother.
The closing line has something that forces it to go round a certain point on the curtain rod.
This can be solved with whatever can be found at home. As you can see in the image I got a plastic straw and a pair of zipties to give the line the turning point it needs.
Deploying our Code
Now that all the parts are in place we can deploy our code to the FiPy.
For this we will use the Atom IDE. It’s my choice of IDE because it’s very simple to use together with the FiPy because of the pymakr package. It’s also not that much different from VSCode which I’m used to from before.
So we’ll have to install Atom together with the pymakr package to upload the code.
This variable decides how long we’ll try to get the input signal from the balcony door.
TEMP_DIFF = 1.5# Maximum acceptable discrepancy
During the data gathering phase the temperature margin of error is not allowed to be larger than this value.
ITERATOR_NUM = 100
TEMP_DATA_GATHERING_DELAY = 0.2# TEMP_DATA_GATHERING_DELAY * ITERATOR_NUM = total time gathering data
These values decide how long the data gathering time is. In this case 20 seconds. This contributes to a more precise reading by caluculating the average of the 100 temperature values gathered.
# Temperature boundaries
UPPER_TEMP = 30
LOWER_TEMP = 28# Time boundaries
EARLY_HOUR = 10
LATE_HOUR = 21
These are the boundaries of the automated curtains (motor). For example, what UPPER_TEMP says is that if the temperature goes above it’s value (30° C) the curtains will close. But ONLY if the current time is between the time boundaries (between 10am-9pm).
# Speed (ms) The larger the value, the slower the speed. Minimum value is 1800us
MOTOR_SPEED = 1800
REQ_DEGREE_TO_OPEN = 1900
These are the motor settings. It’s speed and rotation amount in degrees.
DEEP_SLEEP_TIME = 1000*60*20# 20 min (Format = Millisec)
Finally the amount of time the device is sleeping between each data reading.
Code walkthrough
Before we deploy the code we’ll briefly go through what it does.
This part only explains the code and no changes are required.
The boot file’s purpose is to get things running before the main file starts. Such as Wifi and RTC sync.
pycom.lte_modem_en_on_boot(False)
This will turn off LTE functionality and save us some battery.
ifnot wlan.isconnected():
print("Connecting to WiFi...")
wlan.connect(config.SSID, auth=(WLAN.WPA2, config.PASS))
whilenot wlan.isconnected():
passprint("Connected to Wifi")
Here the Wifi will be connected. The loop is there to make sure it get’s connected properly before continuing.
rtc = machine.RTC()
rtc.ntp_sync("pool.ntp.org")
whilenot rtc.synced():
machine.idle()
print("RTC synced with NTP time")
#adjust your local timezone, by default, NTP time will be GMT
time.timezone(config.CURRENT_TIMEZONE) #we are located at GMT+2, thus 2*60*60
The RTC sync (Real Time Clock) is useful to get the current date and time through Wifi. The time is used when sending data to the database so we can accurately tell when the data was read.
defturnOffWlan():
wlan.deinit()
This function is imported to the main.py so the Wifi can be turned off before deep sleep.
motor.py
# Speed (ms) The larger the value, the slower the speed, the minimum value is 1.8ms
speed = constants.MOTOR_SPEED
STEPER_ROUND = 512# Rotation cycle (360 degrees)
ANGLE_PER_ROUND = STEPER_ROUND / 360# Rotation 1 degree cycle
We begin with configuring some pins for the stepper motor. The speed works like a delay. So less delay between steps will make it go faster and 1.8ms is the minimum delay before the motor stops spinning. ANGLE_PER_ROUND makes it possible to enter degrees as an argument to our functions instead of amount of steps.
defStepperRun(angle):
global ANGLE_PER_ROUND
val = ANGLE_PER_ROUND * abs(angle)
for i inrange(0, val):
StepperFrontTurn()
angle = 0
StepperStop()
This is what brings the whole thing together. With an angle as input we can make the motor spin with pretty reliable accuracy.
myFuncs.py
deftoggleCurtains(isCurtainClosed):
...
This function opens and closes the curtains. What’s important is that if the curtains are open they can NEVER open more and vice versa. It will always check the curtain status and do the action based on that.
defreason_for_wakeup(wake_reason):
...
Here we check how the PyCom woke up. It will return a boolean on whether it was forced to wake up or because of the timer.
defis_door_closed():
... # pins setup
doorClosed = "False"for i inrange(constants.DOOR_NUMBER_OF_CHECKS):
if p_in() == 1:
doorClosed = "True"break
time.sleep(0.1)
return doorClosed
The function will return a value whether the door is open or not. Because the wire connection here is physical and can be a little inconsistent, I wanted it to check several times for input. This makes it more reliable.
The return value is a string because it won’t be used as a boolean. It will be sent together with the data as a formatted string to the database.
defcurtains_should_close(temp, curtainClosed):
cur_time = list(time.localtime())
currentHour = cur_time[3] # index 3 is the current hourif temp >= constants.UPPER_TEMP and curtainClosed == "False"and currentHour >= constants.EARLY_HOUR and currentHour <= constants.LATE_HOUR:
print("It's hot and the time is appropriate. Curtains should CLOSE.")
returnTrueelse:
returnFalsedefcurtains_should_open(temp, curtainClosed):
...
This function will check if the temperature is high and thus, the curtains should close. But only if the time is approriate too. Because the curtains shouldn’t toggle during the night since the nonexistent sunlight isn’t a factor to the temperature at that time.
tempData.py
With this file I decided to explore an object oriented approach to handle all the data.
This line gets saved data from the internal memory of the PyCom and declares a variable. The value is depending on whether the curtains were closed or not. The curtain value is saved to the internal memory every time before deepsleep.
if wakeUpForced:
wakeUpForcedStatus = "true"# Saved as data to the database
curtainClosed = myFuncs.toggleCurtains(curtainClosed)
curtainChangeToMode = 1if curtainClosed == "True"else0
pycom.nvs_set("curtainClosed", curtainChangeToMode)
This section checks to see if the PyCom woke up by the manual switch. If it did, it will save the new state to the internal memory and ALWAYS toggle the curtains. This is the reason the manual switch exists from the beginning. For example it makes it possible to open/close the curtains during the night.
Here the data gathering starts and adds the values to an array.
if iterator > constants.ITERATOR_NUM:
discrepancy_is_low = tempObj.acceptableDiscrepancy()
tempObj.calcAvg()
tempObj.displayStatistics()
tempObj.resetDataManagement()
ifnot discrepancy_is_low: # Error margin too big
iterator = 0# Restarting sensor gathering...else:
...
The accuracy of the temperature reading is pretty important to me. This is why I decided to get 100 readings and then calculate the average. There is even a discrepancy check that compares the difference between the max_temp and min_temp. If the margin of error is too big, the data reading phase will start over.
# Curtain check/toggleifnot wakeUpForced: # Toggle was forced, don't change state this timeif myFuncs.curtains_should_close(tempObj.temp, curtainClosed):
myFuncs.toggleCurtains(curtainClosed)
pycom.nvs_set("curtainClosed", 1)
After passing the data gathering phase, we will check if the curtains should close/open. However, if the PyCom was forced to wake up it would mean the curtains won’t have to move again this session.
Right before we send the data we make a check to see if the door is open. We then format the data into a string and send it to our database.
turnOffWlan()
machine.deepsleep(constants.DEEP_SLEEP_TIME) # sleep for X min
Finaly we turn off the wifi to save battery. Then we go to deepsleep for 20 minutes before process is repeated.
Uploading the code
To deploy the code we must first make sure the PyCom expansion board is connected to the computer with the provided USB cable. Then we select our project in Atom.
Now connect the device.
Your device might not use the same COM port here. Your COM port is the same as the one shown during the firmware update.
At last we press upload.
After a short while the code should be uploaded onto the PyCom.
Now all the electronics are functioning as intended and we can move onto doing something with out data.
Platform
My choice of platform is mainly because I’m used to programming with Javascript. I had heard about Node.js as a useful platform for building web applications with Javascript. It’s also what Node-Red uses so that felt like a natural choice.
Node-Red is the backend serverside tool between the PyCom and the database. It recieves the formatted string from the PyCom, creates an object and sends it to the MongoDB database. Where it is stored as easily accessible JSON objects.
During experimentation I tried out Node-Red’s data visualizer but I wasn’t completely happy with the outcome. Then I realized Node-Red could be used to get data from the PyCom and send it to a MongoDB cloud database. That gave me the idea to create my own website. Since I had created static websites before, with the help of Heroku, I thought it must be possible.
After getting the data to the web application I then checked out Chart.js. It was a tool that helped making charts with a decent result. But when I found Google charts it felt even better. The coding was relatively beginner friendly for an intermediate programmer and the documentation was very helpful.
Transmitting data
Data is sent and stored in the database every time the device turns on. Which is once every 20 minutes. I experimented with 30 minutes at first. But I like to get more precise readings. I went with 15 minutes as well but that felt slightly too much when reading the data visually. So it was a matter of reading data 2, 3 or 4 times each hour and I decided 3 times felt best.
The project uses Wifi. It’s mainly because of the convenience factor, since the project is inside my apartment. Wifi is low range but a little more power consuming for IoT devices. Since range is not even a factor in my small apartment I thought this is a decent choice for me, even if the power consumption is less optimal. That would mean though that if my Wifi went down I wouldn’t send any data. But that doesn’t matter too much since Node-Red requires an internet connection as well. So internet connection would be required somehow even if I chose another wireless protocol for the PyCom.
The transport protocol used between the PyCom and Node-Red is UDP (User datagram protocol). Node-Red and MongoDB uses DNS (Domain Name System). Which means the transport protocol is UDP as defualt but can include TCP (Transmission control protocol) depending on the packet.
From here you can decide how to register. Since the option to use our Google account exists, we will use that method.
Now you just have to fill in the fields that apply to you. As application we can select Internet of things and as language JavaScript.
From here you will select a deployment option. I would suggest the Free option (Shared)
Create a new Database
Let’s create our first database. This is how it looks from your profile page.
Here you will select the cloud provider and the region that fits the best for you.
In the Cluster Tier section we’ll just let it be the default, otherwise just choose what’s for Free again.
We will leave the cluster name as default as well.
IMPORTANT: This tutorial expects your cluster name to be Cluster0 so if you don’t pick that name you will run into trouble later.
Finally we click Create Cluster
Now we’ll need to create a user. Enter a Username and Password for your new user and press Create User.
Now we select local environment. Also press the Add My Current IP Address. Finally press Finish and Close
Allow access to our database
This next step is required for Heroku to be able to connect to our database.
From our dashboard, go to Network Access.
Press the +ADD IP ADDRESS button.
Here we’ll select ALLOW ACCESS FROM ANYWHERE. This is to make Heroku get access to the database later.
Then Confirm
Now we’ve setup a MongoDB account and created a new database. We’ve also created a cluster and allowed access to Heroku. That completes the MongoDB section for now.
Node-Red
To transfer the data from the PyCom to the database we will use Node-Red.
Now to start the Node-Red server we will use the windows terminal.
To open the terminal press: Start + R and open ‘cmd’
From the terminal we enter: node-red This will start a session and lets us connect to the application.
Here we can see the URL we need to access the server: http://127.0.0.1:1880/ To start the Node-Red application we put the URL in a webbrowser’s address bar.
Node-Red with MongoDB
To be able to use MongoDB with Node-Red we must install the correct MongoDB nodes first. We can do that in Settings > Manage pallete > Install and search for node-red-node-mongodb. The pictures below explain how to do it.
Nodes
The project only requires 3 nodes:
udp in (network)
function (function)
mongodb.out (storage)
Udp in
In this node we must change the port to 4445 and the output to a String. Also we must make sure our firewall will allow the data in. Let’s finish by pressing Done to save the changes.
Function
We’ll just copy the code below and paste it into the function’s: On Message.
This code will format an object that will be saved in the database.
The format of this code is important for the website to work.
Mongodb.out
To setup the mongo node correctly we need to select Add new mongodb… in the server menu and click on properties.
Here we need to change the following:
Host: Enter our Cluster0, Host Name.
How to find the Host Name?
From our MongoDB dashboard:
Press the Connect button next to Cluster0.
Connect your application
Select Node.js and the latest version. Then we can see our host name inside the connection string.
(There is also an example below)
Connection topology: Select DNS Cluster (mongodb+srv://)
Database: Enter: IoT.
Username: Enter the MongoDB username.
Password: Enter the MongoDB password.
In the image below we can see an example.
Then press Update.
The Server properties are now done.
We finish the node by entering our Collection name in the Collection field. Then press Done.
Deploy
Our site should now look something like this.
The last thing we need to do is press Deploy in the top-right corner.
When the PyCom is running it will now send the temperature data through this Node-Red application and store the data in a MongoDB database. That however requires the Node-Red server to be online in the command line.
Website
Starting off I want to mention this is not a web development tutorial but we will get through all the necessary steps to make it work.
Github
Now we need a GitHub account. So we’ll create one if we don’t have one.
Now we’ll create our first app. From our dashboard at Heroku’s website: Select Create new app.
Here we only need an App name and choose a region. (The app name will be part of our domain name) Then press Create app.
Connect to our GitHub
Here we will connect our GitHub account to Heroku.
A popup will appear and ask us to authorize the connection between our Heroku and GitHub account. From there we must confirm the access with our password.
Now connect to GitHub by selecting the GitHub repository we want to deploy.
How to find our GitHub repository name.
On our GitHub profile, we go to Repositories and we can find a list of them. Select the one we just created.
Heroku settings
We must now change Config Vars and add Buildpacks.
Go to Settings and scroll down to Config Vars.
In the Key field enter: MONGO_URI The Value field needs a part of the Connection string.
How to find the Connection String?
From our MongoDB dashboard:
Press Connect next to Cluster0.
Connect your application
Select Node.js and the latest version. Then we can see our host name inside the connection string.
(There is also an example below)
IF we copy the Connection string from the MongoDB profile we MUST add IoT as the collection. That is NOT included from the website. Like in the string below. ...mongodb.net/IoT?retryWrites...
Just below the Config vars section we find Buildpacks.
Press Add buildpack, select nodejs and then press Save changes.
Deploy to Heroku
If we now select Deploy in the top menu and scroll to the bottom we’ll press Deploy Branch.
The site should now be uploaded and ready to use. We can open the website by pressing Open app at the top of the page.
Presenting the data
The fact that MongoDB has an unlimited time, free cloud service was very important to me. I then managed to get my data to the web client from the MongoDB database. Then it was just a matter of how to visualize it.
What drove me to take this path was that I can have 100% control over the visual aspect. It being a free method is very comforting as well. But the fact that I can control pretty much every pixel on the screen really sells it. This also gives me the possibility to improve the project almost infinitely.
There are 3 modes you can change between:
Raw daily data: This is just a line which clearly displays the time of the highest and lowest temperature each day.
Curtain status: Here we see an area chart which is green when the curtains are open and blue when they are closed. The curtains automatically close at around 30° C and open at 28° C. This value has been changed many times during the development stage.
Door status: Lastly we can see another area chart which displays the status of the balcony door at different times of the day. This last one actually has some extra backend security added to it. It will always display the balcony door as closed on all datapoints up to 12 hours before the current time.
I really like how this ended up both in reality and in the data presentation.
One thing that could have been improved was that it was initially planed to have the box on the floor. That would mean it’s not visible behind my sofa. But because the motor was slightly too weak it couldn’t handle pulling the string from that angle and I had to change it up. This also applies to the wires hanging off the box. They would be more discrete from the floor.
Video presentation
Here is the full video presentation of the project.
Temperature Controlled Curtains
Summer course: Applied IoT (2022)
Created by: Eric Jansson (ej224de)
UPDATE
As of early 2023, Heroku’s free services are no longer available.
That means that the website part of this tutorial will NOT be possible without a paid plan.
Contents
Estimated Total Time: 4 hours
Introduction
Summer is the best time of the year.
However, the heat can become too much and turn your home into a place of discomfort. To help prevent this issue we can close our curtains to block out the nefarious sun. Unfortunately this creates a dark and moody setting which is not how we want to spend our summer.
So we want sunlight but only when it’s not too hot inside.
Automating the curtains will help us get an optimal time of natural light but also a less extreme indoor temperature. The perfect indoor summer!
Quick description
The curtains will be controlled by pulling two different fishing lines back and forth to open and close them. A motor will be installed to the other end and together with a microcontroller it will open and close the curtains automatically. This will all be based on a reading from a temperature sensor. If it’s too hot inside the curtains will close and vice versa. The status of the balcony door (open/closed) will also be measured and sent to a database together with the other data such as temperature, curtain status and the time of day.
By storing and visualizing statistics we can also analyze the data. We can look for temperature trends depending on the balcony door- or curtain- status. This could possibly tell us if this method is efficient or not.
Project Objective
The reason I chose this project is because I mainly wanted to automate my curtains. Since my apartment gets increadibly hot, I thought a temperature sensor would be a perfect combination with this idea.
The main purpose is to make the apartment avoid the extreme heat during hot days and still get the most natural light. But it also would mean I won’t have to run around as much. To open and close the curtains all the time depending on the weather and indoor temperature.
I also think the data will help me understand how much the curtains and balcony door helps the situation. A lot of people, including me, would open a door when it’s hot inside no matter the outside temperature, in hopes that it will cool down their home. This data will hopefully give me an understanding of how the door and curtains affects the indoor temperature. Both positively and negatively.
Hardware and Electronics
Temperature Sensor
Model: TMP36
This is the sensor I’m using to measure the temperature in my apartment.
required
Stepper Motor
Model: 28BYJ-48
The motor is what opens and closes the curtains by spinning either clockwise or counter clockwise.
required
Wires
Wires to connect everything together.
Price
required
PyCom Fipy
This device is the brain of the project. It’s what controls all our components and sends the data to the database.
required
LiPo Battery
Battery to power the PyCom device.
required
Resistor
Resistor: 220 Ohm
required
Breadboard
A breadboard to connect everything together. Easy to use and doesn’t require soldering.
Circuit board
This board is used for the soldering. It will connect our wires and components permanently but it also helps by keeping the wires connected firmly.
Materials
Spool for Stepper Motor
This is the only part that’s 3D printed in this project. This 3D model file is specifically made for the stepper motor 28BYJ-48.
Fishing line
This is the thread I’m using in the curtains. It’s a nice choice because it’s almost invisible on my white walls and it also pretty strong.
This will be connected to the spool and motor to open and close the curtains.
Program Setups and Installations
There will be some downloading during the tutorial. So I’ve gathered a list of everything download or account related in this section.
This makes it easier to setup everything that needs a download/account before we get to that part.
Notes:
PyCom Firmware
PyCom Fipy Firmware update
This section is taken from this tutorial. For a slightly more descriptive version you can visit that one instead.
You should not interrupt the firmware update process because it could damage the board, meaning:
Follow these steps:
Follow these steps to update the firmware:
The firmware is now installed on your PyCom device.
Atom
Installing Atom
This installation is very easy. We go to their website, install Atom and it will do everything itself.
Installing packages
We also need to install the pymakr package to Atom.
We can find the package installer from the welcome screen or the settings.
From here we go to Install and install the pymakr package. When this is done we should be able to find pymakr in our Packages.
Node.js
Download Node.js
First we should check if Node.js is installed. We can do that by typing
node -vin the VSCode terminal or the windows terminal.If we get a response it means we have node.js installed already. Otherwise we’ll have to install it.
We’ll need to download Node.js to be able to use Node-Red and other things.
Download the Current version for your operating system.
Setup walkthrough
Node.js is now installed.
Node-Red
Node-Red installation
This section is based off of documentation from the Node-Red website.
When installing Node-Red make sure that Node.js is installed already. Otherwise this won’t work. (Installation of Node.js can be found above.)
Open the Windows terminal.
From here just enter this command. (This command is specifically for windows.)
Now Node-Red will be installed automatically.
GitHub
Create Github account
Go to GitHub’s website here and press Sign up.
Fill in your information and press Create account.
When we’ve created our account we will be met with the screen below.
For now we’ll just Skip personalization.
MongoDB
We can create your own account here. But since the option to use your Google account exists, we will use that.
Heroku
Create Heroku account
To create a Heroku account we should visit Heroku’s website here and Sign up.
Create the Heroku account by filling in the fields. Select the Role and Country/Region.
As Primary development language we can select Node.js. Press CREATE FREE ACCOUNT when it’s all done.
Then we’ll get a confirmation link by email that we’ll have to click. That will open a new internet tab where we can create our new password.
Electronics and hardware
Before we connect everything we will update the firmware on our FiPy. For that we need to install the firmware updater.
When the firmware is updated we can move on to connecting some components.
Connecting all the components
In this section we’ll see the final schematics and then we’ll explore every bit of it, piece by piece.
The project can be divided into 5 parts:
Important:
Some pins are interchangeable but that also means you’ll have to change some code. The pin configuration can be found at the FiPy, PyCom documentation.
Here are the final schematics for this project.
Pysense development board
This connection is kind of weird. It would be preferable just to mount the Fipy onto the Pysense. But if we do that we can’t connect the pins to the breadboard in an efficient way.
Lucky for us, we dont need to connect every single pin between them. We just need to connect the 4 pins that are shown in the picture.
The main purpose of this part is that it enables us to connect power easily through the LiPo battery and also to upload code whenever we want to.
Temperature sensor
This is how we measure the temperature. We can see that ground is connected as the black wire, 3.3V is connected as the pink wire and the green one is our analog signal. The analog signal is what tells us what the temperature it is.
Door status
The button here represents the balcony door. The switch “is pressed” if the door is closed and vice versa. This allows the microcontroller to check the pin input value, to see if the door is opened or closed.
This has no real impact on the project in a physical sense. It’s just here to record data that can be analysed together with the temperature.
In real life this is actually done by connection tinfoil with the help of a clip. It’s low quality but it works reliably.
Motor
The motor is what controls the movement of the curtains. Depending on the direction of the motor it will either use the spool to wind-up or wind-down the string.
During the experimentation of this project I realized that the voltage from the pycom wasn’t enough to power the motor efficiently. That’s why there is a 9V battery to help out.
Manual Switch
Since the motor is controlled by the weather, this button will help if we want to control the curtain status manually.
It’s designed so that when the pycom is sleeping, it will wake up after pressing this button. It ALWAYS switch the status of the curtains no matter the temperature.
Otherwise the FiPy functions like normally. It will still read the temperature and send it to the server.
Electrical Calculations
Power Consumption
The temperature sensor has power consumption but it’s so low it’s pretty much negligible during calculations. It has zero impact to the results because of rounding.
TimeAwake = 60 sec (1 min)
TimeSleeping = 1200 sec (20 min)
CurrentAwake = 120.05 mA
CurrentSleeping = 0.15 mA
BatteryCapacity = 1500 mAh
Formulas
TimeAwake∗CurrentAwake+TimeSleeping∗CurrentSleepingTimeAwake+TimeSleeping=DevicePowerConsumption
BatteryCapacityDevicePowerConsumption=BatteryRuntimeHours
BatteryRuntimeHours24=BatteryRuntimeDays
Calculations
60∗120+1200∗0.1560+1200=5.86mA
1500mAh5.86mA=256h
25624=10.7days
The device will last about 10 to 11 days with the current program and selected LiPo battery.
Soldering
This is what the curcuit looks like when connected.
It looks very messy and the wires are very easily dislodged. That could mean a possible short. This is very scary, especially because of the 9V battery. That could damage the FiPy very easily.
With all of that in mind it feels only natural to solder everything together.
This is what it looks like after it’s been soldered together.
There is still a feeling of clutter but it’s way better than before. However the risk of dislodging wires is minimal right now.
Casing
The motor will need something that supports it when it spins. So my thought was to make some sort of casing for the project. I decided to just take an old phone-box that I cut out for the motor and wiring. Most boxes would work here but it’s preferable if it’s a sturdy one.
This is how it looks from the outside.
Inside look.
This is also nice because it gives it a discrete look while lying around in my apartment.
Curtain setup
For the curtains I’ve setup the open and close mechanism with the fishing line.
Pulling one line will open and pulling the other will close it.
The image shows how the lines affect the curtains. Both of them are pulled the same direction but they do the opposite to eachother.
The closing line has something that forces it to go round a certain point on the curtain rod.
This can be solved with whatever can be found at home. As you can see in the image I got a plastic straw and a pair of zipties to give the line the turning point it needs.
Deploying our Code
Now that all the parts are in place we can deploy our code to the FiPy.
For this we will use the Atom IDE. It’s my choice of IDE because it’s very simple to use together with the FiPy because of the pymakr package. It’s also not that much different from VSCode which I’m used to from before.
So we’ll have to install Atom together with the pymakr package to upload the code.
We also require Node.js for this to function properly.
The code that we want to upload can be found at this GitHub repository.
Now we create a suitable folder for the zip file and extract it there.
Open the code
Now we can start Atom to look at our code.
In Atom, go to File > Open folder… and navigate to and select the folder we extracted the files into.
We can now go through the code in the project.
Changing constants
Since this project needs our IP and Wifi we must tell the FiPy the correct values before we upload the code.
We’ll start with the config.py file.
config.py
Here we put our Wifi name, Wifi password, IP address and Timezone.
Below here is an example.
constants.py
Here we can choose our constants. Things like motor speed, device sleep time and motor spin amount.
These are all the pins in use. Some of them can be changed but that would also require the wiring to change as well.
This variable decides how long we’ll try to get the input signal from the balcony door.
During the data gathering phase the temperature margin of error is not allowed to be larger than this value.
These values decide how long the data gathering time is. In this case 20 seconds. This contributes to a more precise reading by caluculating the average of the 100 temperature values gathered.
These are the boundaries of the automated curtains (motor). For example, what UPPER_TEMP says is that if the temperature goes above it’s value (30° C) the curtains will close. But ONLY if the current time is between the time boundaries (between 10am-9pm).
These are the motor settings. It’s speed and rotation amount in degrees.
Finally the amount of time the device is sleeping between each data reading.
Code walkthrough
Before we deploy the code we’ll briefly go through what it does.
This part only explains the code and no changes are required.
The code can be found here.
boot.py
The boot file’s purpose is to get things running before the main file starts. Such as Wifi and RTC sync.
This will turn off LTE functionality and save us some battery.
Here the Wifi will be connected. The loop is there to make sure it get’s connected properly before continuing.
The RTC sync (Real Time Clock) is useful to get the current date and time through Wifi. The time is used when sending data to the database so we can accurately tell when the data was read.
This function is imported to the main.py so the Wifi can be turned off before deep sleep.
motor.py
We begin with configuring some pins for the stepper motor. The speed works like a delay. So less delay between steps will make it go faster and 1.8ms is the minimum delay before the motor stops spinning.
ANGLE_PER_ROUND makes it possible to enter degrees as an argument to our functions instead of amount of steps.
This is what brings the whole thing together. With an angle as input we can make the motor spin with pretty reliable accuracy.
myFuncs.py
This function opens and closes the curtains. What’s important is that if the curtains are open they can NEVER open more and vice versa. It will always check the curtain status and do the action based on that.
Here we check how the PyCom woke up. It will return a boolean on whether it was forced to wake up or because of the timer.
The function will return a value whether the door is open or not. Because the wire connection here is physical and can be a little inconsistent, I wanted it to check several times for input. This makes it more reliable.
This function will check if the temperature is high and thus, the curtains should close. But only if the time is approriate too. Because the curtains shouldn’t toggle during the night since the nonexistent sunlight isn’t a factor to the temperature at that time.
tempData.py
With this file I decided to explore an object oriented approach to handle all the data.
Adds a data point to a list. Also checks if the value is the maximum or minimum value in the list and saves that value.
Checks to see if the discrepancy between data readings are too high.
Pretty simple “average calculation”. Also checks to see if the list is empty to avoid dividing by 0.
SumOfDataReadingsNumberOfDataReadings=Average
main.py
This file starts with a lot of variable declarations that we’ll skip.
This line gets saved data from the internal memory of the PyCom and declares a variable. The value is depending on whether the curtains were closed or not.
The curtain value is saved to the internal memory every time before deepsleep.
This section checks to see if the PyCom woke up by the manual switch. If it did, it will save the new state to the internal memory and ALWAYS toggle the curtains. This is the reason the manual switch exists from the beginning. For example it makes it possible to open/close the curtains during the night.
Here the data gathering starts and adds the values to an array.
The accuracy of the temperature reading is pretty important to me. This is why I decided to get 100 readings and then calculate the average. There is even a discrepancy check that compares the difference between the max_temp and min_temp. If the margin of error is too big, the data reading phase will start over.
After passing the data gathering phase, we will check if the curtains should close/open. However, if the PyCom was forced to wake up it would mean the curtains won’t have to move again this session.
Right before we send the data we make a check to see if the door is open. We then format the data into a string and send it to our database.
Finaly we turn off the wifi to save battery. Then we go to deepsleep for 20 minutes before process is repeated.
Uploading the code
To deploy the code we must first make sure the PyCom expansion board is connected to the computer with the provided USB cable. Then we select our project in Atom.
Now connect the device.
At last we press upload.
After a short while the code should be uploaded onto the PyCom.
Now all the electronics are functioning as intended and we can move onto doing something with out data.
Platform
My choice of platform is mainly because I’m used to programming with Javascript. I had heard about Node.js as a useful platform for building web applications with Javascript. It’s also what Node-Red uses so that felt like a natural choice.
Node-Red is the backend serverside tool between the PyCom and the database. It recieves the formatted string from the PyCom, creates an object and sends it to the MongoDB database. Where it is stored as easily accessible JSON objects.
During experimentation I tried out Node-Red’s data visualizer but I wasn’t completely happy with the outcome. Then I realized Node-Red could be used to get data from the PyCom and send it to a MongoDB cloud database. That gave me the idea to create my own website. Since I had created static websites before, with the help of Heroku, I thought it must be possible.
After getting the data to the web application I then checked out Chart.js. It was a tool that helped making charts with a decent result. But when I found Google charts it felt even better. The coding was relatively beginner friendly for an intermediate programmer and the documentation was very helpful.
Transmitting data
Data is sent and stored in the database every time the device turns on. Which is once every 20 minutes. I experimented with 30 minutes at first. But I like to get more precise readings. I went with 15 minutes as well but that felt slightly too much when reading the data visually. So it was a matter of reading data 2, 3 or 4 times each hour and I decided 3 times felt best.
The project uses Wifi. It’s mainly because of the convenience factor, since the project is inside my apartment.
Wifi is low range but a little more power consuming for IoT devices. Since range is not even a factor in my small apartment I thought this is a decent choice for me, even if the power consumption is less optimal.
That would mean though that if my Wifi went down I wouldn’t send any data. But that doesn’t matter too much since Node-Red requires an internet connection as well. So internet connection would be required somehow even if I chose another wireless protocol for the PyCom.
The transport protocol used between the PyCom and Node-Red is UDP (User datagram protocol). Node-Red and MongoDB uses DNS (Domain Name System). Which means the transport protocol is UDP as defualt but can include TCP (Transmission control protocol) depending on the packet.
MongoDB
Here we will do the following:
Setup a MongoDB account
Go to the MongoDB website and select Try Free.
Create a new Database
Allow access to our database
This next step is required for Heroku to be able to connect to our database.
Now we’ve setup a MongoDB account and created a new database. We’ve also created a cluster and allowed access to Heroku. That completes the MongoDB section for now.
Node-Red
To transfer the data from the PyCom to the database we will use Node-Red.
Now to start the Node-Red server we will use the windows terminal.
From the terminal we enter:
node-redThis will start a session and lets us connect to the application.
Here we can see the URL we need to access the server:
http://127.0.0.1:1880/To start the Node-Red application we put the URL in a webbrowser’s address bar.
Node-Red with MongoDB
To be able to use MongoDB with Node-Red we must install the correct MongoDB nodes first.
We can do that in Settings > Manage pallete > Install and search for node-red-node-mongodb. The pictures below explain how to do it.
Nodes
The project only requires 3 nodes:
Udp in
In this node we must change the port to 4445 and the output to a String. Also we must make sure our firewall will allow the data in. Let’s finish by pressing Done to save the changes.
Function
We’ll just copy the code below and paste it into the function’s: On Message.
This code will format an object that will be saved in the database.
The format of this code is important for the website to work.
Mongodb.out
To setup the mongo node correctly we need to select Add new mongodb… in the server menu and click on properties.
Here we need to change the following:
Enter our Cluster0, Host Name.
How to find the Host Name?
From our MongoDB dashboard:
(There is also an example below)
Select DNS Cluster (mongodb+srv://)
Enter:
IoT.Enter the MongoDB username.
Enter the MongoDB password.
In the image below we can see an example.
Then press Update.
The Server properties are now done.
We finish the node by entering our Collection name in the Collection field.
Then press Done.
Deploy
Our site should now look something like this.
The last thing we need to do is press Deploy in the top-right corner.
When the PyCom is running it will now send the temperature data through this Node-Red application and store the data in a MongoDB database. That however requires the Node-Red server to be online in the command line.
Website
Starting off I want to mention this is not a web development tutorial but we will get through all the necessary steps to make it work.
Github
Now we need a GitHub account. So we’ll create one if we don’t have one.
Create new/Import repository
Now we’ll create our new repository.
From your dashboard at GitHub.com we’ll just select Create a new repository.
Now we’ll go to Import a repository to import the code from somewhere else.
This is the URL:
https://github.com/eric-doe/AutomatedCurtains-Website.gitAfter a short while our repository will be ready.
Heroku
Create account
First we need to create an account
Create new app
Connect to our GitHub
Here we will connect our GitHub account to Heroku.
A popup will appear and ask us to authorize the connection between our Heroku and GitHub account. From there we must confirm the access with our password.
Now connect to GitHub by selecting the GitHub repository we want to deploy.
How to find our GitHub repository name.
On our GitHub profile, we go to Repositories and we can find a list of them. Select the one we just created.
Heroku settings
We must now change Config Vars and add Buildpacks.
Go to Settings and scroll down to Config Vars.
In the Key field enter:
MONGO_URIThe Value field needs a part of the Connection string.
How to find the Connection String?
From our MongoDB dashboard:
(There is also an example below)
IF we copy the Connection string from the MongoDB profile we MUST add
IoTas the collection. That is NOT included from the website. Like in the string below....mongodb.net/IoT?retryWrites...Note that we need to change 3 parts in the previous string.
The MongoDB:
Example string
(Copying this string will NOT work. It’s only here to show the format)
Then click Add.
Just below the Config vars section we find Buildpacks.
Press Add buildpack, select nodejs and then press Save changes.
Deploy to Heroku
If we now select Deploy in the top menu and scroll to the bottom we’ll press Deploy Branch.
The site should now be uploaded and ready to use. We can open the website by pressing Open app at the top of the page.
Presenting the data
The fact that MongoDB has an unlimited time, free cloud service was very important to me. I then managed to get my data to the web client from the MongoDB database. Then it was just a matter of how to visualize it.
What drove me to take this path was that I can have 100% control over the visual aspect. It being a free method is very comforting as well. But the fact that I can control pretty much every pixel on the screen really sells it. This also gives me the possibility to improve the project almost infinitely.
There are 3 modes you can change between:
The curtains automatically close at around 30° C and open at 28° C. This value has been changed many times during the development stage.
This last one actually has some extra backend security added to it. It will always display the balcony door as closed on all datapoints up to 12 hours before the current time.
Final results
I really like how this ended up both in reality and in the data presentation.
One thing that could have been improved was that it was initially planed to have the box on the floor. That would mean it’s not visible behind my sofa. But because the motor was slightly too weak it couldn’t handle pulling the string from that angle and I had to change it up.
This also applies to the wires hanging off the box. They would be more discrete from the floor.
Video presentation
Here is the full video presentation of the project.