Bluetooth Natural Light Alarm Clock

/media/uploads/ooglezoiden/20170503_113242.jpg /media/uploads/ooglezoiden/img_20170316_164101092.jpg

This page documents the development of an alarm clock with three modes:

  • Bluetooth set time mode: the user sets the time on the alarm clock via Bluetooth, then the clock wakes him/her up after that much time has passed.
  • Natural light activation mode: the alarm wakes the user up once the sun has risen outside. This way the user can target a more natural sleep schedule.
  • Smart home mode: the alarm reacts to voice commands through an Echo Dot using the skills kit and a locally hosted server.

In each mode, the alarm will wake the user up by turning on the lights in the user's room, simulating the rising sun.

You can find the code for the project below.

Import programBluetoothLightAlarmClock

An alarm clock that wakes you up naturally!

This project requires the connection of three buttons, the uLCD, the Bluefruit BLE module, the WiFi module, a mosfet, two external power supplies, a 10K Ohm resistor, and a photocell. Tutorials for each of these can be found below.

uLCD

/media/uploads/ooglezoiden/capture0.jpg

https://developer.mbed.org/users/4180_1/notebook/ulcd-144-g2-128-by-128-color-lcd/

Bluefruit BLE

/media/uploads/ooglezoiden/capture.jpg

https://developer.mbed.org/users/4180_1/notebook/adafruit-bluefruit-le-uart-friend---bluetooth-low-/

Mosfets

/media/uploads/ooglezoiden/capture1.jpg

scroll down to "MOSFETs" after following this link.

https://developer.mbed.org/users/4180_1/notebook/relays1/

the external power supply in this Alarm project is plugged into the MOSFET, and the ground of the power supply is tied to the mbed ground.

Photocells

/media/uploads/ooglezoiden/capture3.jpg

https://developer.mbed.org/users/4180_1/notebook/using-a-photocell-to-determine-light-levels/

Pushbuttons

/media/uploads/ooglezoiden/capture2.jpg

scroll down to "using debouncein for switch debouncing" after following this link.

https://developer.mbed.org/users/4180_1/notebook/pushbuttons/

Wifi chip

https://developer.mbed.org/users/ausdong/notebook/using-the-adafruit-huzzah-esp8266-to-add-wi-fi-to-/

The entire wiring spreadsheet is as follows:

/media/uploads/ooglezoiden/wiring_chart.png

The Amazon Web Services Lambda function code is as follows:

Smart Light Alarm Clock Lambda Function

# This sample demonstrates a simple skill built with the Amazon Alexa Skills Kit. The Intent Schema, Custom Slots, and Sample Utterances for this skill, as wellas testing instructions are located at http://amzn.to/1LzFrj6 For additional samples, visit the Alexa Skills Kit Getting Started guide athttp://amzn.to/1LGWsLG """
from __future__ import print_function
import socket
HOST = '192.168.43.148'   # The remote host
PORT = 1035              # The same port as used by the server
 
 
# --------------- Helpers that build all of the responses ----------------------
 
def build_speechlet_response(title, output, reprompt_text, should_end_session):
    return {
        'outputSpeech': {
            'type': 'PlainText',
            'text': output
        },
        'card': {
            'type': 'Simple',
            'title': "SessionSpeechlet - " + title,
            'content': "SessionSpeechlet - " + output
        },
        'reprompt': {
            'outputSpeech': {
                'type': 'PlainText',
                'text': reprompt_text
            }
        },
        'shouldEndSession': should_end_session
    }
 
 
def build_response(session_attributes, speechlet_response):
    return {
        'version': '1.0',
        'sessionAttributes': session_attributes,
        'response': speechlet_response
    }
 
 
# --------------- Functions that control the skill's behavior ------------------
 
def get_welcome_response():
    """ If we wanted to initialize the session to have some attributes we could
    add those here
    """
 
    session_attributes = {}
    card_title = "Welcome"
    speech_output = "You now get to control the light alarm clock."
    # If the user either does not reply to the welcome message or says something
    # that is not understood, they will be prompted again with this text.
    reprompt_text = "Please tell me your command.  For example, say, " \
                    "Turn on the light"
    should_end_session = False
    return build_response(session_attributes, build_speechlet_response(
        card_title, speech_output, reprompt_text, should_end_session))
 
 
def handle_session_end_request():
    card_title = "Session Ended"
    speech_output = "Have a nice day!"
    # Setting this to true ends the session and exits the skill.
    should_end_session = True
    return build_response({}, build_speechlet_response(
        card_title, speech_output, None, should_end_session))
 
 
 
def send_on(intent, session):
 
    card_title = intent['name']
    session_attributes = {}
    should_end_session = False
    #s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    #s.connect((HOST, PORT))
    msg = '/turnOn/run'
    msg = str(chr(48+len(msg))) + msg
    #s.sendall(msg)
    #s.close()
    speech_output = "Ok.  I will turn the light on."
    reprompt_text = "I'm bored. Give me something to do."
    return build_response(session_attributes, build_speechlet_response(
        card_title, speech_output, reprompt_text, should_end_session))
        
def send_off(intent, session):
 
    card_title = intent['name']
    session_attributes = {}
    should_end_session = False
    #s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    #s.connect((HOST, PORT))
    msg = '/turnOff/run'
    msg = str(chr(48+len(msg))) + msg
    #s.sendall(msg)
    #s.close()
    speech_output = "Ok.  I will turn the light off."
    reprompt_text = "I'm bored. Give me something to do."
    return build_response(session_attributes, build_speechlet_response(
        card_title, speech_output, reprompt_text, should_end_session))
        
def send_dim(intent, session):
 
    card_title = intent['name']
    session_attributes = {}
    should_end_session = False
 
    if intent['slots']['Light_Level']['value']:
        dim_level = intent['slots']['Light_Level']['value']
        #session_attributes = create_command_attributes(command)
        #s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        #s.connect((HOST, PORT))
        msg = '/dim/run ' + str(dim_level)
        msg = str(chr(48+len(msg))) + msg
        #s.sendall(msg)
        #s.close()
        speech_output = "Ok.  I will dim the light to " +\
                        dim_level + " percent."
        reprompt_text = "I'm bored. Give me something to do."
    else:
        speech_output = "I'm not sure what your command is. " \
                        "Please try again."
        reprompt_text = "I'm not sure what your command is. " \
                        "You can tell me your command by saying, " \
                        "my command is command name."
    return build_response(session_attributes, build_speechlet_response(
        card_title, speech_output, reprompt_text, should_end_session))
 
def send_set(intent, session):
 
    card_title = intent['name']
    session_attributes = {}
    should_end_session = False
 
    if intent['slots']['Timer_Value']['value']:
        secs = intent['slots']['Timer_Value']['value']
        #session_attributes = create_command_attributes(command)
        #s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        #s.connect((HOST, PORT))
        msg = '/set/run ' + str(secs)
        msg = str(chr(48+len(msg))) + msg
        #s.sendall(msg)
        #s.close()
        speech_output = "Ok.  I will set the timer to " +\
                        secs + " seconds."
        reprompt_text = "I'm bored. Give me something to do."
    else:
        speech_output = "I'm not sure what your command is. " \
                        "Please try again."
        reprompt_text = "I'm not sure what your command is. " \
                        "You can tell me your command by saying, " \
                        "my command is command name."
    return build_response(session_attributes, build_speechlet_response(
        card_title, speech_output, reprompt_text, should_end_session))
    # Setting reprompt_text to None signifies that we do not want to reprompt
    # the user. If the user does not respond or says something that is not
    # understood, the session will end.
 
# --------------- Events ------------------
 
def on_session_started(session_started_request, session):
    """ Called when the session starts """
 
    print("on_session_started requestId=" + session_started_request['requestId']
          + ", sessionId=" + session['sessionId'])
 
 
def on_launch(launch_request, session):
    """ Called when the user launches the skill without specifying what they
    want
    """
 
    print("on_launch requestId=" + launch_request['requestId'] +
          ", sessionId=" + session['sessionId'])
    # Dispatch to your skill's launch
    return get_welcome_response()
 
 
def on_intent(intent_request, session):
    """ Called when the user specifies an intent for this skill """
 
    print("on_intent requestId=" + intent_request['requestId'] +
          ", sessionId=" + session['sessionId'])
 
    intent = intent_request['intent']
    intent_name = intent_request['intent']['name']
 
    # Dispatch to your skill's intent handlers
    if intent_name == "OnIntent":
        return send_on(intent, session)
    elif intent_name == "OffIntent":
        return send_off(intent, session)
    elif intent_name == "DimIntent":
        return send_dim(intent, session)
    elif intent_name == "SetIntent":
        return send_set(intent, session)
    elif intent_name == "AMAZON.HelpIntent":
        return get_welcome_response()
    elif intent_name == "AMAZON.CancelIntent" or intent_name == "AMAZON.StopIntent":
        return handle_session_end_request()
    else:
        raise ValueError("Invalid intent")
 
 
def on_session_ended(session_ended_request, session):
    """ Called when the user ends the session.
 
    Is not called when the skill returns should_end_session=true
    """
    print("on_session_ended requestId=" + session_ended_request['requestId'] +
          ", sessionId=" + session['sessionId'])
    # add cleanup logic here
 
 
# --------------- Main handler ------------------
 
def lambda_handler(event, context):
    """ Route the incoming request based on type (LaunchRequest, IntentRequest,
    etc.) The JSON body of the request is provided in the event parameter.
    """
    print("event.session.application.applicationId=" +
          event['session']['application']['applicationId'])
 
    """
    Uncomment this if statement and populate with your skill's application ID to
    prevent someone else from configuring a skill that sends requests to this
    function.
    """
    # if (event['session']['application']['applicationId'] !=
    #         "amzn1.echo-sdk-ams.app.[unique-value-here]"):
    #     raise ValueError("Invalid Application ID")
 
    if event['session']['new']:
        on_session_started({'requestId': event['request']['requestId']},
                           event['session'])
 
    if event['request']['type'] == "LaunchRequest":
        return on_launch(event['request'], event['session'])
    elif event['request']['type'] == "IntentRequest":
        return on_intent(event['request'], event['session'])
    elif event['request']['type'] == "SessionEndedRequest":
        return on_session_ended(event['request'], event['session'])

The intent schema used was:

Intent Schema

{
  "intents": [
    {
      "intent": "OnIntent"
    },
    {
      "intent": "OffIntent"
    },
    {
      "slots": [
        {
          "name": "Light_Level",
          "type": "AMAZON.NUMBER"
        }
      ],
      "intent": "DimIntent"
    },
    {
      "slots": [
        {
          "name": "Timer_Value",
          "type": "AMAZON.NUMBER"
        }
      ],
      "intent": "SetIntent"
    },
    {
      "intent": "AMAZON.HelpIntent"
    }
  ]
}

Finally, the sample utterances used were:

Sample Utterances

OnIntent Turn On
OffIntent Turn Off
DimIntent Dim The Light To {Light_Level} percent
SetIntent Set The Timer To {Timer_Value} seconds

When it all works together (outside of the reach of GaTech firewall) the Echo Dot will send your voice command to an amazon server where it is deciphered into one of the sample utterances. With that, Amazon then extracts variables such as brightness or timer duration, and passes the intent of your command along with these variables to the lambda function. The lambda function takes this data and sends an appropriate RPC call to the server running on the mbed WiFi chip. The mbed, meanwhile is constantly listening on the appropriate port. When it receives a packet of data, the mbed deconstructs it and calls the corresponding subroutine associated with the RPC call passed. All of this happens in a second or two, and before you know it, your lights are on!


Please log in to post comments.