# Network connected Door Lock **By Åskar Hjort (ah225ay)** [toc] # Introduction This project is a simple network connected servo that can be actuated from a webapp. this project is more off a template to get going and can be customized for according to your needs by adding more sensors and/or actions The base device for this project is the network enabled microcontroller LoPy4 by pycom. Using this device we can connect to a server program made in our favourite programming language, i personally chose [Node.js](https://nodejs.org/en/) since it's really easy to get started in. The project can be broken down into a couple of individual segments and those are as follows: 1. Create a physicall interface between the lock and the servo. 2. Program the actions in the iot-device. 2.1. Lock the door 2.2. Open the door 3. Create a client system in the iot-device. 4. Create a server program for your computer/server. This project is expected to take around 6hrs to complete from start to finnish. ## Materials Here is a list of materials i used to construct the device. | Material | Link | Cost | | -------------------------------------------------------- |:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | | Some scrap wood | N/A | 0 | | some Glue | [Biltema](https://www.biltema.se/en-se/construction/chemicals/glue-guns/) | 99kr for 30 sticks | | Generic Servo SG90 | [ali express](https://www.aliexpress.com/item/32975618090.html?spm=a2g0o.productlist.0.0.3a581b278NrWXB&algo_pvid=b8d3a9f4-e2d9-46c0-9b43-b2110030fc30&algo_expid=b8d3a9f4-e2d9-46c0-9b43-b2110030fc30-5&btsid=0ab50a5715935949657435382ec308&ws_ab_test=searchweb0_0,searchweb201602_,searchweb201603_) | 0.25-1.55$ | | any button/swithc i used: | [rs-online](https://se.rs-online.com/web/p/microswitches/1594590/) | 36,67kr | | some wires | [kjell](https://www.kjell.com/se/produkter/el-verktyg/elektronik/elektroniklabb/luxorparts-kopplingskabel-med-quick-click-hane-hane-20-cm-p87332) | 60kr | | Breadboard | [kjell](https://www.kjell.com/se/produkter/el-verktyg/elektronik/elektroniklabb/kopplingsdack-400-hal-p36285) | 90kr | | some resistors (10k for pulldown) | [kjell](https://www.kjell.com/se/produkter/el-verktyg/elektronik/komponentsatser/playknowlogy-sortiment-med-resistorer-600-pack-p90646) | 120kr | | Lopy4 + expansion board | [lopy4](https://pycom.io/product/lopy4/?gclid=Cj0KCQjw6PD3BRDPARIsAN8pHuHEt1EXLYHsTnuyv9R4QgwJwYIPwbDSSDMdDBeZ-Tyu5dt5dQNtRc8aAvfjEALw_wcB) [expansion board](https://pycom.io/product/expansion-board-3-0/) | total inkl shipping and vat: 71.34 euro | If you are like me and have a bunch of these things just laying around the price won't be too rough, you don't neceserally need a lopy4 you can use a device such as arduino and add wifi capabilities or a raspberry pi but the lopy4 has a bunch of usefull things such as built in Lora and Sigfox which could come in handy if you decide to build this somewhere without a wifi connection. ## Before you start Before we start with the project we need to install some software on our machine, namely a code editor and plugins to uppload and run files on our lopy4. I personally used [Vscode](https://code.visualstudio.com/) by microsoft which also is supported by pycom with the build tools [Pymakr](https://docs.pycom.io/gettingstarted/installation/pymakr/). You can also use [atom](https://atom.io/). **[Pymakr](https://docs.pycom.io/gettingstarted/installation/pymakr/)** is the way we uppload our code to the lopy device so it's **very important** to get it running before you continue with the tutorial. **We also need to install [Nodejs](https://nodejs.org/en/) and [Python](https://www.python.org/), then register our lopy4 device on [Pybytes](https://pybytes.pycom.io/) where we can update the firmware on the device**. You can follow the guide inside of the [pycom docs](https://docs.pycom.io/gettingstarted/installation/). Now let's get started with the first part of this project! # The physicall interface In order for the servo to lock and open the door we need to make a physicall interface like the janky setup below: ![Figure 1: my servo door interface](https://i.imgur.com/gsxb7ao.jpg) As you can see it doesn't take much to make this interface, i essencially took some scrap pieces of wood and cut it into shape and drilled a few holes. I also added a switch to see what position the servo is in (locked state or open state). Because i don't have a soldering station i decided to just strip some wires and whind them around the connectors then glue them on. **You need to check how your door is constructed**, first below is some images of the door i need to interface with: ![Figure 2: my door](https://i.imgur.com/sm6Uz4i.png) As you can see there really isn't much too it all we need to do is make sure we can rotate the metal piece showed on the right. We also have some convinient mounting points and screws that we can use from the door. I would have prefered to create something in a cad program then 3d print it but unfortunatelly that is not available to me, if you can i do recommend you use some better materials but it's not required for this simple setup. When you have created the physicall setup we can move on to programming our microcontroller. # Programming the servo actions A servo is a rotary actuator which allows us to have precise controll of it's angular position, the way it works is it takes in a PWM(pulse width modulated) signal and it matches the driveshafts position according to the width of this signal. A common frequency for the PWM signal is 50hz and for a SG90 servo we have position 0 at 1ms pulse or 5% duty cycle and position 180 at 2ms or 10% duty cycle. For a more in depth guide on servos i recomend watching [GreatScott!s](https://www.youtube.com/watch?v=J8atdmEqZsc) video on servos and how to use them. For now all we need to know is that we need to give the servo a **50hz PWM signal with a 5% duty cycle for the 0 position and 10% for 180 position.** We first need to connect the servo to our Lopy4. on the servo there are 3 cables 1 brown(ground), 1 red(vcc) and 1 orange(signal). **Connect the red wire to vin on the lopy expansion board, then connect the brown wire to the ground pin and then the orange to pin12** **If you whant to have a button to see if the door is open or closed** you can connect 3.3v to one end of the button and the other end to **pin 8** below is a very professional diagram of the circuit: ![](https://i.imgur.com/X8BtzAU.jpg) When everything is hooked up you should create the following project structure: ``` .project name/ | +--lib/ | +--__init__.py | +--servo.py | +--boot.py +--main.py ``` Since the lopy4 uses a modified version of micropython the code for all components on the device will be made in python. For this module we will be working in the **lib/servo.py** file to create all the servo actions. ```{python} # We need PWM to create a PWM signal from machine import PWM # Timer will be used to turn of the servo when it's idle from machine import Timer class Servo: def __init__(self): #here we define the variables # create a pwm object with 50hz frequency pwm = PWM(1,frequency = 50) # attach the pwm object to the variable self.servo_v self.servo_v = pwm.channel(0, pin='P12',duty_cycle=0) # create variables to hold frequencies we need to turn lef/right self.right_pos = 0.04 self.left_pos = 0.11 # as i am using a generic servo i had to tweek the values abit # to get it to turn the amount i whanted # this method is used to turn the servo off after a certain intervall def turn_off(self,alarm): # set duty_cycle to 0 self.servo_v.duty_cycle(0) # remove alarm callback alarm.callback(None) # Method to turn the servo left def turn_left(self): #set duty_cycle to left_pos(0.11) self.servo_v.duty_cycle(self.left_pos) #add timer callback to turn the servo off after 1 second self.timer = Timer.Alarm(self.turn_off,1) def turn_right(self): #set duty_cycle to right_pos(0.04) self.servo_v.duty_cycle(self.right_pos) #add timer callback to turn the servo off after 1 second self.timer = Timer.Alarm(self.turn_off,1) ``` As we can see it's quite easy to controll the servo with the included pwm module in micropython. Now we can test this in our main.py by first upploading the file structure using the vscode extension, then go into the main python file and **add the following:** ```{python} from servo import Servo import time servo1 = Servo() while True: servo1.turn_left() time.sleep(1000) servo1.turn_right() ``` Then run the main file and you should see the servo going from left to right. # Make a nodejs server Now that we can use the servo by simply creating a servo object and calling turn_left and turn_right we need to be able to connect the device to a server and send instructions. For this we use [Nodejs](https://nodejs.org/en/) with the module [Net](https://nodejs.org/docs/latest-v13.x/api/net.html). This module will allow us to comunicate over the **[TCP/IP](https://sv.wikipedia.org/wiki/TCP/IP)** protocol stack. Create a new directory separete from the Lopy4 directory and create a file called "server.js", in this file we will create the server program like the following: ```{javascript} // import the net module const net = require('net'); // create a variable to store door state var doorState // create an array to append clients too const clientPool = []; // make a TCP server object const server = net.createServer(); // make the server listen on ip and port server.listen({ host:'your local ip', port:'8080' }); // make a connection listener server.on('connection', (client) => { console.log('connected!') // set client encoding client.setEncoding('utf-8'); // add data event listener to client client.on('data',(data) =>{ doorState = data }) // push client into pool clientPool.push(client); }); ``` that's it! now we can make a client on our lopy4 to connect to this server. you can run the server by typing ``` node server.js ``` into your terminal # Make the Lopy4 a client In order to connect to the node server we are going to use TCP/IP [sockets](https://docs.pycom.io/firmwareapi/micropython/usocket/#class-socket) over WiFi. This will limit the range of the device and battery life since WiFi takes quite abit of power. Luckily we can mitigate this atleast a little bit by not sending data untill we are told to do so, hence the code below only sends data when specified to. in our main.py file we can add the following: ```{python} #import servo class from our lib folder from servo import Servo #import pin for the button on pin8 from machine import Pin #import all required modules import socket import sys import errno import _thread #create the button object toggle_button = Pin('P8', mode=Pin.IN, pull=Pin.PULL_DOWN) #define the client socket def client_socket(servo,id): #get address info address = socket.getaddrinfo('your server ip',8080)[0][-1] #create socket object s = socket.socket() #connect to address s.connect(address) #connection loop while True: #try/catch, if the socket disconnects #try to reconnect try: #recive data data = sock.recv(512) #if data is open turn servo right if str(data, 'utf-8') == 'open': servo.turn_right() #if data is close turn servo left if str(data, 'utf-8') == 'close': servo.turn_left() #if data is read send state of switch if str(data, 'utf-8') == 'read': if toggle_switch(): s.send('1') else: s.send('0') #reset data data = 0 except socket.error as error: code = error.args #if error code 104 try to reconnect if code[0] == 104: s.close() s = socket.socket() s.connect(address) else: #else raise and end thread raise # create servo object servo1 = Servo() # start client socket thread _thread.start_new_thread(client_socket,(servo1,1)) ``` Good! now we can connect to the node server, remember to add your ip address in getaddrinfo. In windows you find your ip by starting a console and typing ipconfig, then look for your wifi adapter/ethernet adapter and getting the ipv4 address. If you run the server then run the lopy you should see that the server logs "connected!". # Interaction Now we need to make a way to interact with the device! for this we add some stuff to our node server, but first let's make a website! I would suggest just copying the code in the gist below into a file called index.html in the same directory as server.js, but you can customize it as you please: https://gist.github.com/AskarHjort/35c31c9f09dcffaf9e3b6b3532859488 From this simple html website we can trigger "routes" on the server. When we get a request such as "yourip:8000/open" we can turn the servo to the corresponding possition. ```{javascript} const http = require('http'); const url = require('url'); const fs = require('fs'); ``` These things are needed to first serve the website and then listen to requests from different urls such as yourip/open or yourip/close, this is how we communicate with the lopy4 device. now let's serve our html and respond to requests! add this to the bottom of your server.js ```{javascript} //start the http server http.createServer((request, response) =>{ //preemptively write header so we don't need to do it later response.writeHead(200,{'content-Type': 'text/html'}); //parse the url path const path = url.parse(request.url).pathname //now we create our "router" switch(path){ //if path is /open send open command to the lopy4 case '/open': if(clientPool.length != 0){ clientPool[0].write('open') response.end() }else{ response.writeHead(500) reponse.end() } break; //if path is /close send close command to the lopy4 case '/close': if(clientPool.length != 0){ clientPool[0].write('close') response.end() }else{ response.writeHead(500) reponse.end() } break; //if path is /read send read command to the lopy4 case '/read': if(clientPool.length != 0){ clientPool[0].write('read') if(doorState == '1'){ response.write('1') }else if (doorState == '0'){ response.write('0') } response.end() }else{ response.writeHead(500) reponse.end() } break; //and as a default we send the website default: fs.readFile('./index.html', null, (err,file) => { if(err){ response.writeHead(404); response.end() }else{ response.write(file); response.end() } }); break; } }).listen(8000) ``` Now we can try it out by starting the server and go to your ip on port 8000 and start the iot device, if everything has been done correctly it should look similar to this: <iframe width="100%" height="365" src="https://www.youtube.com/embed/xkRaQftfDAI" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> As a last step we can mount it on a our door to lock and open it from the website! note: this is not a safe implementation anyone that can connect to your server can use the device!