Project tutorial
Dooreo - US China Young Maker Comp Submission

Dooreo - US China Young Maker Comp Submission © GPL3+

This device will allow you to open your door by asking Alexa to open it for you.

  • 2,989 views
  • 0 comments
  • 7 respects

Components and supplies

Necessary tools and machines

3drag
3D Printer (generic)
09507 01
Soldering iron (generic)

Apps and online services

About this project

Updated for 2019 China-US Young Maker Competition

Introduction

Dooreo can open your door when you can't or don't want to. Use it to let your friends in without getting up from the couch or open your door when your hands are full.

Dooreo uses stepper motors and 3D-printed parts to physically operate the door and a combination of Raspberry Pi and Arduino to control it. The Raspberry interfaces with AWS to allow control through Amazon Alexa.

Here's a video of Dooreo in action.

The story

Development Process

I began by verifying the electrical hardware setup. I was planning to use 2 stepper motors controlled by an Arduino through an easy driver and a big easy driver. The Arduino would get signals from the Raspberry Pi which would get information from the amazon skill.

I had done a previous project where I used Alexa to control a Raspberry Pi but I wanted to go through the proper AWS channels this time instead of using the ngrok shortcut that is not really a permanent solution. Working through setting up an Alexa Skill, Lambda Function, IoT Thing and having the pi listen for delta events for the Thing Shadow proved to be a challenging but rewarding process. Initially I thought to use an Alexa Smart Home Skill as the interaction is pre-built and would save me some effort. However, Smart Home Skills aren't allowed for doors. This is for a good reason as they don't support any sort of pin or passphrase. Instead, I developed a custom skill and Lambda handler to allow for opening and closing the door with a four digit pin. I'll try to explain it as best I can to make it easier for any who follow down this path.

Communicating between a Raspberry Pi and Arduino is very straightforward as the Arduino can use the Raspberry Pi's usb interface. Wiring up all of the motor wires is always a bit of a task but presented no real hurdles.

In the electrical setup my main issue was discovered later. I wanted to use a 6V wall adapter I had available. However, when I tried to actually move the door knob with this 6V source powering my stepper motor the motor could not provide enough torque. I ended up having to upsize to a bigger power supply that operates at 24V.

The next step was to figure out the mechanisms to turn the door knob and open the door. For the doorknob I initially wanted to use a timing belt and to open it I wanted to use a winch.

To attach the timing belt to the door knob I had to clamp a timing belt pulley to the door knob. I took some rough measurements and modeled some corresponding pieces in Solidworks and printed them out.

The clamping on the doorknob worked pretty well but the timing belt was not able to provide enough force. I decided to change to a gear mechanism. I was able to keep the same geometry as I had for the timing belt and use gear models from McMaster-Carr to design my needed mechanism.

Full Build Steps

Step 1: Wiring

The Arduino connects to the Raspberry Pi with an FTDI adapter from the programming header of the Arduino to the usb port of the Raspberry Pi.

The Arduino can be powered through its connection to the Raspberry Pi, and the logic voltage for the easy drivers can be supplied by the Arduino, this is the "5V" reference.

The motor drivers are powered by a separate 24V source. Connect this to "PWR IN" at "V+" and "GND", make sure to get the polarity right. The motors connect to the easy drivers at the "MOTOR" pins. Wiring color can be somewhat unreliable so the best thing to do is identify the two wires that comprise a coil and connect these together to A and B. So take the wires in pairs and measure their resistance The pairs that have low resistance between them are coils. Connect the first coil to the two "A" pins and the other coil to the two "B" pins.

Lastly, connect the Arduino to the two easy drivers. You need to wire a digital pin from the Arduino to each of the following pins on the easy drivers: STP, DIR, EN, MS1, MS2 and if you are using a big easy driver MS3. If your pin assignments don't match mine you'll have to change them in the Arduino sketch to match.

I used the box my parts came in from SparkFun as an enclosure for the electronics.

Step 2: Raspberry Pi Setup

We'll want to set up the Raspberry Pi headless so that we can ssh into it and reprogram the Arduino without needing to access the enclosure. Here's a pretty decent tutorial on setting up the Raspberry Pi: http://www.circuitbasics.com/raspberry-pi-basics-setup-without-monitor-keyboard-headless-mode/

and one for Mac:

https://lifehacker.com/the-always-up-to-date-guide-to-setting-up-your-raspberr-1781419054

and if you don't want to do this headlessly here's the regular way to do it with a temporary keyboard, mouse and monitor:

https://www.instructables.com/id/My-Raspberry-Pi-Setup/

Once your Raspberry Pi is set up we'll want to ssh in with x11 forwarding enabled. So install Xming:

https://sourceforge.net/projects/xming/files/

and make sure it's running. Then, use Putty:

https://www.putty.org/

with Enable X11 Forwarding selected:

Once SSH is established with your Pi use the following commands to install Arduino

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install arduino

Then run it from the terminal by typing "Arduino." It will take a second to come up but you should see the Arduino IDE on your computer.

Make sure to select the correct microcontroller and com port (it's probably /dev/ttyUSB0 but if it's not use "lsusb" in your terminal to list the devices and find your Arduino).

Step 3: Program Arduino

Next we'll program the Arduino to control the motors when it receives a command. The Raspberry Pi will send it a byte for open and a byte for close. On open, the Arduino should move the doorknob motor far enough to unlatch the door and then turn the winch motor far enough to open the door. These parameters will probably change from door to door, I found them by trial and error.First, we set up the pin definitions:

#define stp 2
#define dir 3
#define MS1 4
#define MS2 5
#define EN  6
#define stpb 12
#define dirb 7
#define ENb 11
#define MS1b 8
#define MS2b 9
#define MS3b 10

Then we set all of the pins as outputs and start up the serial connection.

void setup() {
 pinMode(stp, OUTPUT);
 pinMode(dir, OUTPUT);
 pinMode(MS1, OUTPUT);
 pinMode(MS2, OUTPUT);
 pinMode(EN, OUTPUT);
 pinMode(stpb, OUTPUT);
 pinMode(dirb, OUTPUT);
 pinMode(MS1b, OUTPUT);
 pinMode(MS2b, OUTPUT);
 pinMode(MS3b, OUTPUT);
 pinMode(ENb, OUTPUT);
 Serial.begin(9600); //Open Serial connection for debugging
}

Our main loop just listens for a serial command and makes sure not to call the open and close door functions unless there's a state change.

//Main loop
void loop() {
 while(Serial.available()){
     user_input = Serial.read(); //Read user input and trigger appropriate function
     if(user_input =='5')
     {
       if(onOffState==0){
         openDoor();
       }
       onOffState=1;
     }
     else if(user_input=='6')
     {
       if(onOffState==1){
         closeDoor();
       }
       onOffState=0;
     }
     else
     {
       Serial.println("Invalid option entered.");
     }
 }
}

We write functions for controlling the motors that take direction, number of steps and speed (in microseconds delay between steps) so that we can easily change the behavior of the motor by just modifying our calls to these functions.

void turnKnob(int dirValue, int steps, int motorSpeed)
{
 digitalWrite(dir,dirValue);
 for(x=1;x<steps;x++)
 {
   digitalWrite(stp,HIGH);
   delayMicroseconds(motorSpeed);
   digitalWrite(stp,LOW);
   delayMicroseconds(motorSpeed);
 }
}
void turnWinch(int dirValue, int steps, int motorSpeed)
{
 digitalWrite(dirb,dirValue);
 for(x=1;x<steps;x++)
 {
   digitalWrite(stpb,HIGH);
   delayMicroseconds(motorSpeed);
   digitalWrite(stpb,LOW);
   delayMicroseconds(motorSpeed);
 }
}

Finally, we write functions that make the appropriate calls to the motor control functions in order to open and close the door. One thing of note is for the winch I had to call the function multiple times as the step size is too large for the standard int variable (I could also just use a larger type).

void openDoor()
{
 Serial.println("opening door");
 turnKnob(0,250,1000);
 //wind in winch
 turnWinch(0,20000,90);
 turnWinch(0,20000,90);
 turnWinch(0,20000,90);
 turnWinch(0,20000,90);
}
void closeDoor()
{
 Serial.println("closing door");
 digitalWrite(EN,HIGH);
 //winch out door
 turnWinch(1,20000,60);
 turnWinch(1,20000,60);
 turnWinch(1,20000,60);
 turnWinch(1,20000,60);
 digitalWrite(ENb,HIGH);
}

Step 4: Install Hardware

I use a lot of double sided tape in this installation. The specific tape I'm using is almost scary strong "Scotch Extreme Mounting Tape"

First, attach the gear to your doorknob. This is accomplished by sliding the back plate over the doorknob behind it, and then screwing the gear portion onto it with 4-40 x 1" screws and nuts. The part relies on the friction created with clamping force so it's important to tighten the screws quite tight.

Next, mount the knob-motor to the door. The smaller gear is fit onto the shaft of the motor (held in place by gorilla glue). Line the small gear up with the gear on the door knob such that the gears mesh and tape the motor in place with mounting tape.

Next we mount the winch assembly. The winch assembly is two 3D-printed parts, the spool and the bracket. Slide the spool onto the bracket's pole, then slide the motor's shaft into the spool's hole and attach the motor with 4-40 x 1" screws and nuts.

Then, measure how long a string needs to be to reach from the winch to an opposing wall. Cut a length of string and insert this through the hole in the middle of the spool. Secure it using a knot.

Now, mount the winch assembly to the door using mounting tape.

Lastly, we mount the electronics to the door and rout power to it. I used an extension chord and a usb charger and mounting tape or gorilla tape to secure everything.

Alexa Interaction

In order to get Alexa to send directions to the Raspberry Pi we will set up an Alexa skill, a Lambda function for it to call, and an AWS IoT Thing for the Lambda function to update. The Raspberry Pi will then detect changes in the AWS IoT Thing and respond appropriately.

Step 5: Create an AWS IoT Thing

Navigate to the aws console. Create an account if you don't have one.

https://console.aws.amazon.com/

In "AWS services" type "iot" and click on "AWS IoT".

Then go to "Manage" in the side menu and click in the top right for "Create".

Select "Create a single thing", name it "Dooreo", you don't have to do anything else on this page, no type, no group, no attributes. Click Next.

Select "Create certificate". On the next page it's very important to download all of the files AND save the root CA. For the root CA just save it to a text file, don't worry about the name (We'll name it later (root-CA.crt))

Click "Activate" and then "Done"

Next, go to "Secure" -> "Policies" in the Side Menu. Click in the top right "Create"

Name it "Dooreo Policy" and in action put "iot:*", in "Resource ARN" put "*" and then check "Allow" then click "Create"

Go to "Secure" -> "Certificates" and find the certificate you just created. Click on the three dots, select "Attach policy" and select Dooreo Policy. Click on the three dots again and select "Attach thing" and select Dooreo.

Step 6: Set Up Raspberry Pi

To have the Raspberry Pi react to commands from Alexa we'll be using the python version of aws-iot-device-sdk. Their github page does an excellent job explaining it and how to install it.

https://github.com/aws/aws-iot-device-sdk-python

Basically we'll be using the same functionality as the BasicShadow sample. Scroll through the ReadMe till the part that describes the BasicShadow sample and make sure you get that running properly.

When you go to run the command to start basicShadowDeltaListener.py and basicShadowUpdater.py you'll need the endpoint name and certificate files from the thing you created. Put these in the sample folder and rename them easy names. I use WinSCP to put files on my Raspberry Pi.

https://winscp.net/eng/download.php

  • "certificate.pem.crt"
  • "root-CA.crt"
  • "private.pem.key"

You can find the endpoint by going to the aws console, going to "Manage" -> "Things" clicking on "Dooreo" and then going to "Interact". The first field is your endpoint.

So open up two SSH sessions for your Raspberry Pi and go to the folder for basicShadow. In the first, run basicShadowDeltaListener

python basicShadowDeltaListener.py -e <endpoint> -r root-CA.crt -c certificate.pem.crt -k private.pem.key

and in the second SSH session run basicShadowUpdater

python basicShadowUpdater.py -e <endpoint> -r root-CA.crt -c certificate.pem.crt -k private.pem.key

Makes sure that in the basicShadowDeltaListener you see messages coming in that look like the picture:

If you verified that this works, congratulations you have you Raspberry Pi communication with AWS.

Now put "dooreo.py" in the same folder and replace your endname on line 65.

Run this in tmux. If you aren't familiar with tmux. It's a virtual terminal that you can run on a linux environment. I use it so that when I close my ssh session the processes that I run stay running. Install it by running

sudo apt-get install tmux

Then open a tmux session by typing "tmux" and run

python3 dooreo.py

Step 7: LWA

This post has a good explanation of how to set up LWA for linking an Alexa skill to a Lambda Function. Only do step 1:

https://developer.amazon.com/blogs/alexa/post/Tx4WG410EHXIYQ/five-steps-before-developing-a-smart-home-skill

Make sure to get the Client ID and Client Secret. Once you have this you can move on to step 8

Step 8: Lambda Function and Creating an Alexa Skill

8.1) Go to the aws console. In "AWS services" type "Lambda" and select it to go to the Lambda console.

8.2) Select "Create function" in the top right

8.3) Choose "Author from scratch", name it "Dooreo-Lambda", select "Python 2.7" as the runtime, select "Choose an existing role" and select "lambda_basic_execution" as the role.

8.4) The "role" "lambda_basic_execution" refers to an IAM role. Let's make sure to set that up correctly. Go back to the start page for the aws console. Type in IAM and select it.

8.5) Select "Roles" in the side menu and select "lambda_basic_execution"

8.6) Go to "Attach Policy" and select AWSIoTFullAccess and AWSIoTDataAccess and click "Attach"

8.7) In order to finish the configuration for the Lambda function we'll need an Alexa skill ID to fill in. In another tab from where you are setting your Lambda Function, go to the amazon developer console. If you don't have an account yet, make one (you will have had to make one for Step 7). and go to "Alexa" and click on "Get Started >" under "Alexa Skills Kit"

https://developer.amazon.com/edw/home.html#/

8.8) Click on "Add a New Skill" in the top right.

8.9) In "Skill Information" Select "Custom Skill" and name it "Dooreo Skill." Give it the invocation name "dooreo." Once you hit "Save" the page will update and show you the Application Id.

8.10) Go back to your Lambda function and go to the configuration page. In the "Add triggers" list click on Alexa Skills Kit. Copy the "Application Id" into the "Configure triggers" area under Application ID. Click "Add" then "Save" in the top right.

8.11) In "Configuration" click on "Dooreo-Lambda." Copy the code from "lamba.py" into the "Function code" section. If you've made any changes, make sure "thingName" on line 17 matches your thing name. Click "Save" in the top right.

This code is all about interpreting the Json from Alexa. Alexa will send its commands in this format and the Lambda function needs to interpret it and decide what to do. Refer to the code comments for explanation about its behavior.

Step 9: Alexa Skill

9.1) Now it's time to set up the Alexa Skill. Go back to your Alexa Skill. In "Configuration" copy the ARN from your Lambda function into the "Endpoint" section. Use "https://www.amazon.com/ap/oa" in "Authorization URL". Get the client ID from your LWA profile. Add "profile" to the Scope. Use "https://api.amazon.com/auth/o2/token" as your Access Token URI. Get the Client Secret from your LWA profile. If you want to publish a skill you'll have to maintain a privacy policy page and link it here.

9.2) In the side menu go to "Interaction Model" we'll be using "Builder" (currently in BETA)

9.3) You can take the code provided under "Alexa Skill" and paste it into the "Code Editor" to generate the behavior or build it step by step.

9.4) To build it step by step we'll need to add 3 intents. Next to Intents in the side menu, click add. Name your first intent changeDoorState and click create.

9.5) Add some sample Utterances for this intent such as "{doorState} door with pin {pin}" (see pictures). The words in braces are slots. We'll add those next.

9.6) Add the slot "pin" and give it the type AMAZON.FOUR_DIGIT_NUMBER.

9.7) Add the slot "doorState" and create a new type for it called "state"

9.8) Configure the state type by clicking on it in the side menu and adding its possible values with synonyms as shown in the picture

9.9) Add the clearPin intent with the pin slot and the setPin intent with the pin slot

9.10) Click on "Build Model" in the top bar and you should be all set.

Step 10: Test Lambda with Alexa Skill

In your Alexa skill go to the test section and bring up the Test Simulator (currently in BETA). Type in "ask Dooreo setPin to one two three four" you should get the response "updated pin"

Then try "Ask Dooreo open door" this should initiate the dialogue and Alexa should ask "What's the pin" type in "one two three four" and she'll respond "door open"

Step 11: Full System Integration

At this point everything should be working independently and it's time to tie it all together.

Make sure your hardware is securely mounted.

Make sure your electronics are wired and powered on.

Make sure your Arduino is running the proper code.

Make sure your Pi can see the Arduino (/dev/ttyUSB0)

Make sure your Pi is running dooreo.py in a tmux session.

From the "Test Simulator" run type in "Ask Dooreo open door" and your motor should run and the door should open. If this is the case, congrats!

Step 12: Echo Setup

The last step to use this with your voice is to enable this for your Echo device.

Go to your Alexa companion app on your smartphone. Go to "Skills" in the side menu. Go to "Your Skills" in the top right. Switch from "Recently Added" to "All Skills." Find "Dooreo". Click on "Enable". It should do the account linking as long as you've set up the LWA parameters correctly.

Congrats, you can now open your door with your voice!

Code

dooreo.pyPython
Python code to run on the Raspberry Pi to listen for updates to the IoT Thing Shadow
'''
/*
 * Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */
 '''

from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTShadowClient
import logging
import time
import json
import argparse
import serial

ser=serial.Serial("/dev/ttyUSB0",9600)
ser.flushInput()


# Shadow JSON schema:
#
# Name: Bot
# {
#	"state": {
#		"desired":{
#			"property":<INT VALUE>
#		}
#	}
# }

# Custom Shadow callback
def customShadowCallback_Delta(payload, responseStatus, token):
	# payload is a JSON string ready to be parsed using json.loads(...)
	# in both Py2.x and Py3.x
	print(responseStatus)
	payloadDict = json.loads(payload)
	print("++++++++DELTA++++++++++")
	#print("property: " + str(payloadDict["state"]["property"]))
	#print("version: " + str(payloadDict["version"]))
	print(payload)
	print(str(payloadDict["state"]["light"]))
	print("+++++++++++++++++++++++\n\n")
	onOff = payloadDict["state"]["light"]
	
	if onOff == "on":
		#if(status=='open'):
		ser.write(b'5')
	elif onOff == "off":
		#elif(status=='closed'):
		ser.write(b'6')
	
rootCAPath = "root-CA.crt"
certificatePath = "certificate.pem.crt"
privateKeyPath = "private.pem.key"
thingName = "Dooreo"
host = "a370wwfaias9e1.iot.us-east-1.amazonaws.com"
clientId = "DooreoPi"

# Configure logging
logger = logging.getLogger("AWSIoTPythonSDK.core")
logger.setLevel(logging.DEBUG)
streamHandler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
streamHandler.setFormatter(formatter)
logger.addHandler(streamHandler)

# Init AWSIoTMQTTShadowClient
myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient(clientId)
myAWSIoTMQTTShadowClient.configureEndpoint(host, 8883)
myAWSIoTMQTTShadowClient.configureCredentials(rootCAPath, privateKeyPath, certificatePath)

# Connect to AWS IoT
myAWSIoTMQTTShadowClient.connect()

# Create a deviceShadow with persistent subscription
deviceShadowHandler = myAWSIoTMQTTShadowClient.createShadowHandlerWithName(thingName, True)

# Listen on deltas
deviceShadowHandler.shadowRegisterDeltaCallback(customShadowCallback_Delta)

# Loop forever
while True:
	time.sleep(1)
Alexa SkillJSON
Code that can be used to build Alexa skill
{
  "languageModel": {
    "types": [
      {
        "name": "state",
        "values": [
          {
            "id": null,
            "name": {
              "value": "open",
              "synonyms": [
                "opened"
              ]
            }
          },
          {
            "id": null,
            "name": {
              "value": "closed",
              "synonyms": [
                "close"
              ]
            }
          }
        ]
      }
    ],
    "intents": [
      {
        "name": "AMAZON.CancelIntent",
        "samples": []
      },
      {
        "name": "AMAZON.HelpIntent",
        "samples": []
      },
      {
        "name": "AMAZON.StopIntent",
        "samples": []
      },
      {
        "name": "changeDoorState",
        "samples": [
          "door {doorState} with pin {pin}",
          "{doorState} door with pin {pin}",
          "{doorState} door",
          "door {doorState}"
        ],
        "slots": [
          {
            "name": "doorState",
            "type": "state",
            "samples": [
              "{doorState}"
            ]
          },
          {
            "name": "pin",
            "type": "AMAZON.FOUR_DIGIT_NUMBER",
            "samples": [
              "{pin}"
            ]
          }
        ]
      },
      {
        "name": "clearPin",
        "samples": [
          "clear pin {pin}"
        ],
        "slots": [
          {
            "name": "pin",
            "type": "AMAZON.FOUR_DIGIT_NUMBER",
            "samples": [
              "{pin}"
            ]
          }
        ]
      },
      {
        "name": "setPin",
        "samples": [
          "set pin to {pin}",
          "set pin {pin}"
        ],
        "slots": [
          {
            "name": "pin",
            "type": "AMAZON.FOUR_DIGIT_NUMBER",
            "samples": [
              "{pin}"
            ]
          }
        ]
      }
    ],
    "invocationName": "dooreo"
  },
  "prompts": [
    {
      "id": "Elicit.Intent-changeDoorState.IntentSlot-doorState",
      "variations": [
        {
          "type": "PlainText",
          "value": "do you want the door open or closed?"
        }
      ]
    },
    {
      "id": "Elicit.Intent-changeDoorState.IntentSlot-pin",
      "variations": [
        {
          "type": "PlainText",
          "value": "please provide pin, if you have not set one, say set pin and then a four digit number"
        }
      ]
    },
    {
      "id": "Elicit.Intent-clearPin.IntentSlot-pin",
      "variations": [
        {
          "type": "PlainText",
          "value": "what is the old pin?"
        }
      ]
    },
    {
      "id": "Elicit.Intent-setPin.IntentSlot-pin",
      "variations": [
        {
          "type": "PlainText",
          "value": "what did you want the pin to be?"
        }
      ]
    }
  ],
  "dialog": {
    "intents": [
      {
        "name": "changeDoorState",
        "confirmationRequired": false,
        "prompts": {},
        "slots": [
          {
            "name": "doorState",
            "type": "state",
            "elicitationRequired": true,
            "confirmationRequired": false,
            "prompts": {
              "elicitation": "Elicit.Intent-changeDoorState.IntentSlot-doorState"
            }
          },
          {
            "name": "pin",
            "type": "AMAZON.FOUR_DIGIT_NUMBER",
            "elicitationRequired": true,
            "confirmationRequired": false,
            "prompts": {
              "elicitation": "Elicit.Intent-changeDoorState.IntentSlot-pin"
            }
          }
        ]
      },
      {
        "name": "clearPin",
        "confirmationRequired": false,
        "prompts": {},
        "slots": [
          {
            "name": "pin",
            "type": "AMAZON.FOUR_DIGIT_NUMBER",
            "elicitationRequired": true,
            "confirmationRequired": false,
            "prompts": {
              "elicitation": "Elicit.Intent-clearPin.IntentSlot-pin"
            }
          }
        ]
      },
      {
        "name": "setPin",
        "confirmationRequired": false,
        "prompts": {},
        "slots": [
          {
            "name": "pin",
            "type": "AMAZON.FOUR_DIGIT_NUMBER",
            "elicitationRequired": true,
            "confirmationRequired": false,
            "prompts": {
              "elicitation": "Elicit.Intent-setPin.IntentSlot-pin"
            }
          }
        ]
      }
    ]
  }
}
Lambda.pyPython
Lambda Code to interact with Dooreo thing shadow and Dooreo Alexa Skill
import logging
import time
import urllib2
import json
import boto3
import uuid
import datetime

#define client for interact with IoT Thing
client = boto3.client('iot-data')

# Setup logger
logger = logging.getLogger()
logger.setLevel(logging.INFO)

#entry point for lambda function, receives commands from alexa as json
def lambda_handler(event, context):
    logger.info("Received v3 directive!")
    logger.info(json.dumps(event))
    response = "null"
    
    #check to see that event is an intent from alexa
    if event["request"]["type"] == "IntentRequest":
        logger.info("Control")
        #call handler function for intent
        response = on_intent(event["request"], event["session"])
        
    logger.info("response" + json.dumps(response)) 
    return response

#handler for intent received from alexa        
def on_intent(intent_request, session):
    #extract the intent part of the json
    intent = intent_request["intent"]
    #get the name of the intent from the json
    intent_name = intent_request["intent"]["name"]
    
    #get the current state of the IoT thing to use its pin
    shadowResponse=client.get_thing_shadow(thingName='Dooreo2')
    shadowStream=shadowResponse["payload"]
    shadowJson=json.loads(shadowStream.read())
    
    #check if the IoT thing already has a pin or not
    try:
        currentPin = shadowJson["state"]["desired"]["pin"]
    except KeyError:
        currentPin = "nopin"
    
    #handler for setPin intent
    if intent_name == "setPin":
        if currentPin=="nopin":
            #call function to update IoT thing as desired
            updateShadowPin(intent)
            responseText = "updated pin"
        
        #don't set the pin if there already is one
        else:
            responseText = "pin already set, clear pin using the clear pin command or contact support to reset pin"
    
    #handler for clearPin intent
    elif intent_name == "clearPin":
        if currentPin=="nopin":
            responseText = "no pin to clear"
        
        #if there is a pin in the IoT thing, check if the user provided the correct pin, if so delete it from the IoT thing
        else:
            pinValue=intent["slots"]["pin"]["value"]
            if pinValue==currentPin:
                updateShadowPinClear()
                responseText = "pin cleared"
            else:
                responseText = "old pin does not match, to reset contact support"
    
    #handler for open and close commands
    elif intent_name == "changeDoorState":
        #if this is the first command from alexa we check if a pin is provided
        if intent_request["dialogState"] == "STARTED":
            try: 
                pinValue=intent["slots"]["pin"]["value"]
            #there was no pin, ask for it using the Dialog.ElicitSlot mechanism    
            except KeyError:
                response = {
                    "version": "1.0",
                    "sessionAttributes": {},
                    "response": {
                        "outputSpeech": {
                          "type": "PlainText",
                          "text": "What's the pin?"
                        },
                        "shouldEndSession": False,
                        "directives": [
                         {
                            "type": "Dialog.ElicitSlot",
                            "slotToElicit":"pin"
                        }
                        ]
                    }
                }
                return response
        
        #there was a pin, or it's not the first command and we assume there's a pin
        pinValue=intent["slots"]["pin"]["value"]
        if currentPin == "nopin":
            responseText = "no pin set, please set pin by saying set pin and a four digit number"
        #check if user provided pin matches that in IoT thing and if so, change IoT thing state appropriately
        else:
            if currentPin == pinValue:
                updateShadow(intent)
                value = intent["slots"]["doorState"]["value"]
                responseText = "door " + value
            else:
                responseText = "pin does not match"
    else:
        responseText= "not a known command"
    #build an appropriate response for Alexa using the text from the above cases
    response = {
        "version": "1.0",
        "sessionAttributes": {},
        "response": {
            "outputSpeech": {
                "type": "PlainText",
                "text": responseText
            },
            "shouldEndSession": True
        }
        }
    return response
   
#function to update the IoT thing shadow for door open and close     
def updateShadow(intent):
    #get the desired state from the Alexa json command
    state=intent["slots"]["doorState"]["value"]
    
    #update the thing shadow using boto3 library with a json object containing the new state
    response = client.update_thing_shadow(
        thingName = 'Dooreo2', 
        payload = json.dumps({
            'state': {
                'desired': {
                    'door': state
                }
            }
            }
            )
        )

#function to update the IoT thing shadow to set a new pin        
def updateShadowPin(intent):
    #get the desired state from the Alexa json command
    state=intent["slots"]["pin"]["value"]
    #update the thing shadow using boto3 library with a json object containing the new state
    response = client.update_thing_shadow(
        thingName = 'Dooreo2', 
        payload = json.dumps({
            'state': {
                'desired': {
                    'pin': state
                }
            }
            }
            )
        )

#function to update the IoT thing shadow to clear the old pin        
def updateShadowPinClear():
    response = client.delete_thing_shadow(thingName='Dooreo2')
    response = client.update_thing_shadow(
        thingName = 'Dooreo2', 
        payload = json.dumps({
            'state': {
                'desired': {
                    'door': 'closed'
                }
            }
            }
            )
        )
dooreo.inoArduino
Code to run on arduino for motor control for Dooreo
//Declare pin functions on Arduino
#define stp 2
#define dir 3
#define MS1 4
#define MS2 5
#define EN  6

#define stpb 12
#define dirb 7
#define ENb 11
#define MS1b 8
#define MS2b 9
#define MS3b 10

//Declare variables for functions
char user_input;
int x;
int y;
int state;
int onOffState=0;

void setup() {
  pinMode(stp, OUTPUT);
  pinMode(dir, OUTPUT);
  pinMode(MS1, OUTPUT);
  pinMode(MS2, OUTPUT);
  pinMode(EN, OUTPUT);

  pinMode(stpb, OUTPUT);
  pinMode(dirb, OUTPUT);
  pinMode(MS1b, OUTPUT);
  pinMode(MS2b, OUTPUT);
  pinMode(MS3b, OUTPUT);
  pinMode(ENb, OUTPUT);
  
  resetEDPins(); //Set step, direction, microstep and enable pins to default states
  Serial.begin(9600); //Open Serial connection for debugging
  Serial.println("Begin motor control");
  Serial.println();
  //Print function list for user selection
  Serial.println("Enter number for control option:");
  Serial.println("5. open the door.");
  Serial.println("6. close the door.");
  Serial.println();
}

//Main loop
void loop() {
  while(Serial.available()){
      user_input = Serial.read(); //Read user input and trigger appropriate function
      if(user_input =='5')
      {
        if(onOffState==0){
          openDoor();
        }
        onOffState=1;
      }
      else if(user_input=='6')
      {
        if(onOffState==1){
          closeDoor();
        }
        onOffState=0;
      }
      else
      {
        Serial.println("Invalid option entered.");
      }
  }
}

//Reset Easy Driver pins to default states
void resetEDPins()
{
  digitalWrite(stp, LOW);
  digitalWrite(dir, LOW);
  digitalWrite(MS1, LOW);
  digitalWrite(MS2,HIGH);
  digitalWrite(EN, HIGH);

  digitalWrite(stpb, LOW);
  digitalWrite(dirb, LOW);
  digitalWrite(MS1b, HIGH);
  digitalWrite(MS2b,HIGH);
  digitalWrite(MS3b,HIGH);
  digitalWrite(ENb, HIGH);
}

void turnKnob(int dirValue, int steps, int motorSpeed)
{
  digitalWrite(dir,dirValue);
  for(x=1;x<steps;x++)
  {
    digitalWrite(stp,HIGH);
    delayMicroseconds(motorSpeed);
    digitalWrite(stp,LOW);
    delayMicroseconds(motorSpeed);
  }
}

void turnWinch(int dirValue, int steps, int motorSpeed)
{
  digitalWrite(dirb,dirValue);
  for(x=1;x<steps;x++)
  {
    digitalWrite(stpb,HIGH);
    delayMicroseconds(motorSpeed);
    digitalWrite(stpb,LOW);
    delayMicroseconds(motorSpeed);
  }
}

void openDoor()
{
  digitalWrite(EN, LOW); //Pull enable pin low to allow motor control
  digitalWrite(ENb, LOW); //Pull enable pin low to allow motor control
  Serial.println("opening door");
  turnKnob(0,250,1000);
  //wind in winch
  turnWinch(0,20000,90);
  turnWinch(0,20000,90);
  turnWinch(0,20000,90);
  turnWinch(0,20000,90);
    
}

void closeDoor()
{
  Serial.println("closing door");
  digitalWrite(EN,HIGH);
  //winch out door
  turnWinch(1,20000,60);
  turnWinch(1,20000,60);
  turnWinch(1,20000,60);
  turnWinch(1,20000,60);
  digitalWrite(ENb,HIGH);
}

Custom parts and enclosures

Motor Mount STL
mounting bracket for Winch Assembly, STL for printing
Motor Gear Part
Gear to mount on smaller stepper motor
motor_gear_gtqzhhtnj5_7etfsiGvtY.SLDPRT
Motor Gear STL
Gear to mount on smaller stepper motor, STL for printing
Doorknob Gear Part
Gear to mount on doorknob
5172t450_ht-load_mtl_gear--20_deg_pressure_angle_mephswr3kq_lcsu2b9PBO.SLDPRT
Doorknob Gear STL
Gear to mount on doorknob, STL for printing
Doorknob Back Plate
Plate for back of doorknob to attach doorknob gear to.
front_plate_eaxm0zwttn_11ji8tzkrZ.SLDPRT
Doorknob Back Plate STL
Plate for back of doorknob to attach doorknob gear to. STL for printing
Spool Part
Spool to mount on larger motor for winch assembly
spool_vatrxjttpa_6ua92b9jMz.SLDPRT
Spool STL
Spool to mount on larger motor for Winch Assembly
Motor Mount
Mounting bracket for Winch Assembly
motor_mount_uloarllxmb_j2i8DebSM4.SLDPRT

Schematics

Main Wiring Diagram
Doorknob bb z0rhu2ahcf vgae9wd5w2

Comments

Similar projects you might like

Scent-terrific Smart Candle

Project tutorial by Darian Johnson

  • 3,404 views
  • 0 comments
  • 34 respects

Intelligent Door Lock

Project in progress by Md. Khairul Alam

  • 20,736 views
  • 23 comments
  • 111 respects

Voice Controlled RGB Lamp

Project tutorial by Pham Hoang Son

  • 3,490 views
  • 1 comment
  • 13 respects

Secure Package Delivery Trunk for Your Front Porch

Project tutorial by Castle Locker

  • 3,107 views
  • 1 comment
  • 19 respects

WalaBeer Tank

Project tutorial by Balázs Simon

  • 24,925 views
  • 5 comments
  • 165 respects

Hygge Home - Alexa Smart Bath

Project tutorial by J Howard

  • 5,968 views
  • 2 comments
  • 22 respects
Add projectSign up / Login