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. :sunny: 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 :link: :link:

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 :link: :link:

Wires

Wires to connect everything together.

Package
Price
Amount
required
Link Header Type
29 SEK 2x :link: Female/Male
29 SEK 10x+ :link: 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 :link: :link:

The Pysense expansion board and USB cable is included in the link.


LiPo Battery

LiPo battery

Battery to power the PyCom device.

Price Amount
required
Link
149 SEK 1x :link:

Resistor

220ohm-resistor

Resistor: 220 Ohm

Price Amount
required
Link
1 SEK 1x :link:

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 :link:

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 :link:

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.

The 3D model file can be found here.


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.

Price Link
59 SEK :link:

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:

Name Download Account
PyCom Firmware :heavy_check_mark: :x:
Atom :heavy_check_mark: :x:
Node.js :heavy_check_mark: :x:
Node-Red :heavy_check_mark: :x:
GitHub :x: :heavy_check_mark:
MongoDB :x: :heavy_check_mark:
Heroku :x: :heavy_check_mark:

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:

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.



Atom

Installing Atom

This installation is very easy. We go to their website, install Atom and it will do everything itself.

Link to Atom’s website.

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.

If you already closed the welcome screen we can go to Settings > Packages > Settings View > Open.

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.

We should close Atom and reopen it to ensure the changes have been applied before continuing.



Node.js

Download Node.js

First we should check if Node.js is installed. We can do that by typing node -v in the VSCode terminal or the windows terminal.

To open the windows terminal press Start + R and open ‘cmd’.

I recommend that our version is atleast this or higher.

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.

Link to the Node.js website.

Download the Current version for your operating system.


Setup walkthrough



Accept the terms of service.


Select where you want to save it.


The rest is left as default.


Node.js is now installed.

If we want, we can now check the version in the windows terminal again like we did in the first step.



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.

Press: Start + R. Then open ‘cmd’


From here just enter this command. (This command is specifically for windows.)

npm install -g --unsafe-perm node-red

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.

:rocket: Go to Firmware download guide.

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.

Door opened

Door closed

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

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

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.

Circuit

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.

Soldered circuit

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.

Outer casing

Inside look.

Inside peek

Inner casing

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.

Curtain setup

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.

Curtain setup 2

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.

:rocket: Go to Atom and package download guide.

We also require Node.js for this to function properly.

:rocket: Go to Node.js installation guide.

The code that we want to upload can be found at this GitHub repository.

Code Zip Download

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

SSID = <Your_Wifi_Name> PASS = <Your_Wifi_Password> IP = <Your_IP_Address> CURRENT_TIMEZONE = <Your_Timezone>

Here we put our Wifi name, Wifi password, IP address and Timezone.
Below here is an example.

SSID = 'Eric Wifi' PASS = 'password123' IP = '192.0.2.146' CURRENT_TIMEZONE = 2*60*60 # I'm located at GMT+2, thus 2*60*60

constants.py

Here we can choose our constants. Things like motor speed, device sleep time and motor spin amount.

These are not important to change. However, things like the motor spin amount probably differs for most people.

WAKE_UP_PIN = 'P4' DOOR_PIN_1 = 'P9' DOOR_PIN_2 = 'P10' TEMP_DATA_PIN = 'P16' MOTOR_PIN_LIST = ['P19', 'P20', 'P21', 'P22']

These are all the pins in use. Some of them can be changed but that would also require the wiring to change as well.

DOOR_NUMBER_OF_CHECKS = 50 # totalDoorCheckTime = DOOR_NUMBER_OF_CHECKS * 0.1

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 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.

pycom.lte_modem_en_on_boot(False)

This will turn off LTE functionality and save us some battery.

if not wlan.isconnected(): print("Connecting to WiFi...") wlan.connect(config.SSID, auth=(WLAN.WPA2, config.PASS)) while not wlan.isconnected(): pass print("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") while not 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.

def turnOffWlan(): 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.

def StepperRun(angle): global ANGLE_PER_ROUND val = ANGLE_PER_ROUND * abs(angle) for i in range(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

def toggleCurtains(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.

def reason_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.

def is_door_closed(): ... # pins setup doorClosed = "False" for i in range(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.

def curtains_should_close(temp, curtainClosed): cur_time = list(time.localtime()) currentHour = cur_time[3] # index 3 is the current hour if 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.") return True else: return False def curtains_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.

def addValue(self, value): self.listOfVals.append(value) self.checkIfMaxMin(value)

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.

def acceptableDiscrepancy(self): if (self.max - self.min) / 2 < constants.TEMP_DIFF: return True else: return False

Checks to see if the discrepancy between data readings are too high.

def calcAvg(self): if len(self.listOfVals) == 0: return avg = 0 for key in self.listOfVals: avg += key avg /= len(self.listOfVals) self.temp = avg

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.

curtainClosedData = curtainClosed = "True" if pycom.nvs_get("curtainClosed") == 1 else "False"

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 = 1 if curtainClosed == "True" else 0 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.

while True: millivolts = apin.voltage() celsius = (millivolts - 500.0) / 10.0 tempObj.addValue(celsius) iterator += 1 if iterator > constants.ITERATOR_NUM: # 100 ... time.sleep(constants.TEMP_DATA_GATHERING_DELAY) # 0.2s

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() if not 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/toggle if not wakeUpForced: # Toggle was forced, don't change state this time if 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.

doorClosed = myFuncs.is_door_closed() newData = tempObj.formatData(curtainClosedData, doorClosed, wakeUpForcedStatus) s.connect(addr) s.send(newData)

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.

MongoDB

Here we will do the following:

Setup a MongoDB account

Go to the MongoDB website and select Try Free.

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.

:rocket: Node-Red installation guide.

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

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.

split = msg.payload.split(' ') msg.payload = { temp: parseFloat(split[0]), curtainsClosed: split[1] === "True" ? true : false, doorClosed: split[2] === "True" ? true : false, time: {} } msg.payload.time = { year: parseInt(split[3]), month: parseInt(split[4]), day: parseInt(split[5]), hour: parseInt(split[6]), min: parseInt(split[7]), sec: parseInt(split[8]), dayOfWeek: parseInt(split[9]), dayOfYear: parseInt(split[10]) } return msg;

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:

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)

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.

:rocket: Go to GitHub creation guide

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.



After a short while our repository will be ready.


Heroku

Create account

First we need to create an account

:rocket: Go to Heroku account creation

Create new app

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...

mongodb+srv://<username>:<password>@Cluster0.<Cluster-ID>.mongodb.net/IoT?retryWrites=true&w=majority

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)

mongodb+srv://Rapidall:password123@Cluster0.ypofdq1.mongodb.net/IoT?retryWrites=true&w=majority

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:

Website finish

Website finish curtain status

Website finish door status

:star: This is a public website that can be viewed at: automated-curtains.herokuapp.com

Final results

Final curtains

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.

Final curtains 2



Video presentation

Here is the full video presentation of the project.