Project in progress

Solar Battery Powered Switch for Blinds, Lights, and Charger © GPL3+

Control window blinds, lights, and monitor battery charging using voice command.

  • 2,611 views
  • 0 comments
  • 12 respects

Components and supplies

Apps and online services

About this project

ABOUT

The Solar Switch is an automated switch / battery monitor that responds to voice command to operate DC / AC devices.

  • Automate solar battery bank charging when voltage falls below fixed level at night
  • Operates DIY Window Blinds with stepper motor.
  • Use voice command with Amazon Echo to Turn on/off Window Blinds, LED, charger, alarm, and other AC devices.
  • Option to Automatically close blinds at night and turn on Lights
voice operation demo

STORY

I have couples of solar panels to charge deep cycle battery for powering various DC devices like led lights and automated motion sensor lights. On extended cloudy day, the lead-acid battery become sulfated and lose capacity over time when undercharged.

Many times when I over use the power and depleted the battery, I need to manually check and charge the battery with a battery charger to avoid permanently damage the battery. I want to check the state of battery and it's usage patterns - how often it charges.

Also, I want to automate the task of shutting down the blinds for two windows and turn on light every night.

IMPLEMENTATION

Using Arduino Yun with its build in Wifi and separate processor running Linux, I Installed Amazon Web Service Library in Python. It'll allow the state of connected devices to be saved on the cloud - using AWS IOT Things Shadowed to update and modify the device's state information.

When user say a command, Amazon Echo will invoke Alexa Skill to interpret voice input into textual command called Intentions in Json data format. Data is passed and processed by serverless code ( AWS Lambda function implemented in Python). The script will process the intent by modify Things Shadowed settings - A virtual representation of the acutual device settings and acting as a message broker that will notify device or other connected clients.

When Arduino Solar Switch is turned on, it'll connect to the AWS IOT Things Shadowed via light weight messaging protocol (MQTT) . Then subscribe and listen to changes in the parameter settings and control the desired device.

HARDWARE SETUP

  • DC Power source is a 12 Volt deep cycle battery charged by solar panels.
  • 12V to 5V step down converter powers the Arudino Yun and stepper motor for the window blinds.
  • One relay switch operates AC powered 10-amp Car Battery charger connected to lead-acid battery.
  • Remaining Relay and output pins operates DC devices drawing power from battery.

DIY Windows Blind Controller

I used a stepper motor housed within a bracket mount attached side wall of the window blinds. I can adjust the fitting by rotating the mount during testing.

I 3d printed an adapter to fit the blind wand to motor rod. Grip has minimum grip and will slip off the turning wand if stuck. This is to prevent breaking the blinds gear with too much torque on already closed blinds. I taped the exterior with tape to help with grip.

Blinds Configuration

To sync with the motor, I count how many turns to fully close and open the blinds. Register the value and change it in configuration sketch file of the blinds.

#define STEPS_PER_REVOLUTION 64
#define MOTOR_INC_STEPS 300
// Set the number of motor steps to fully turn blinds from closed facing down to facing up.
#define MAX_STEP 12000  

When initially powering on Arduino, set the blinds position to mid open position with blinds parallel to the window to remain in sync. For manual control. I used a potentiometer to dial in a position between 0 and 100. Position 50 is when the blades of the blinds in parallel.

Once setup, I used 2 steppers with control wire in parallel to turn 2 large blinds in sync.

Outdoor Light Monitor

I hooked a light level sensor on the window side of the blind to determine day or night. I want AC powered charger activated at extreme low light. During the daytime battery is charged by solar panel.

Relay controller

I used 4 port relay. One port is connected to the AC for the charger. Rest are used for operating 12V DC devices. These are LED colored strips, White LED light bulb, and alarm.

Battery discharge monitoring

I hooked a voltage sensor and a car battery charger that outputs up to 120 Watts It'll charge the battery when specified battery voltage drop to low level. It'll trigger the charger and charge for fixed amount of time. It'll also shut off when exceeding the max voltage to avoid boiling electrolyte in Lead Acid battery.

At night, solar charged battery provides power to connected DC devices until low cut off point. Then charger will automatically turn on to charge the battery to maintain minimum level until daylight.

The activation voltage is configurable in the Battery Monitor sketch module .

#define ACTIVATE_CHARGE_VOLTAGE 12.2
#define CHARGE_CUTOFF_VOLTAGE 14.7

Colored LED Lights

I used a multi color LED strip mounted inside a L shaped door guard strip for night time decorations. Graduation of color reflected on the white window blinds makes a nice atmosphere for night time movie watching.

Alarm

I have an alarm light and bell that chimes when circuit is broken. Or when some one enter the room. It is a reed switch mounted to doors and windows.

SOFTWARE SETUP

A good portion of this project involves configuration of Amazon services. For voice activated control setup. You'll need an Amazon Echo and set up 2 Amazon accounts and services.

Before starting, I recommend reading and going through following tutorials:

  • Amazon Web Services.

Following are steps to re-create my configuration.

VOICE COMMAND Alexa Utterance

Here are some sample commands you can say to operate the switch by voice.

Alexa, ask solar switch to check status
Alexa, tell solar switch to set blinds position to 30
Alexa, tell solar switch turn blinds off
Alexa, ask solar switch turn on light
Alexa, ask solar switch turn off charger
Alexa, tell solar switch turn alarm off

Configuring Alexa Skills

  • Login, Open Alexa Console, and select Voice Skills
  • In Lambda section. Create a new function
  • Enter name for the skill, "Solar Power Switch"
  • Click on "Code Editor",
  • Copy and Paste in the Alexa Skill Utterance from code section at bottom of this page.
  • Press "Build Model"
  • Go to Configuration section.
  • Select default End point using AWS Lambda. We'll create the function that process the voice command next.

Setup Alexa Voice Input Skill

SERVER SIDE SETUP

Creating Server Lambda Function

  • Setup AWS Account
  • Login to Developer Console
  • Go to Lambda service
  • Select Create function
  • Select Author from scratch, Enter Function name "Solar Smart Home" , Runtime: Select Python 2.7, create custom role
  • Select "Interaction Model" and click "Code Editor"
  • Copy and Paste in the Lambda Code file in the code section at bottom of this page
  • Press "Save"

  • Copy the ARN address highlighted at top of the function
  • Go back to Alexa Skill Configuration page. Paste in Lambda function address into the End point Default field

Configure Lambda function on AWS

IOT Things Shadowed Setup

Things Shadowed creates a virtual version of device’s latest state. This allows applications to interact with that device even when offline.

I followed this tutorial to understand the detail on the interaction process. To set up, here are the steps:

  • Login to developer console and go to AWS Iot section.
  • On manage Things section, press "Create" button
  • Select Create a single thing
  • Give things a name "Smart Solar Home". Press NEXT
  • Click "Create certificate"
  • Download things certificate, public key, private key, and root CA for AWS files. You'll need these later
  • Press "Register Thing"

Test the Setup

AWS IoT Device Shadow. In this instance it will update the ‘desired’ section of the Device Shadows to control the device. AWS IoT In this example, the blinds will check the desired position and activate the motor based on the value. The state of the Solar Switch is kept in Shadow state file. You can verify the state of the settings with the device by checking this shadowed file's value and version number.

Arduino Yun SETUP

I followed this tutorial to set up Arduino Yun with AWS IOT Shadow. Here are basic steps:

  • Install AWS Arduino IoT SDK.
  • Place AWS Root certificate, public and private key file for Iot Things into the root directory.
  • Open project sketch and change configuration file settings
  • Modify settings in aws_iot_config.h file in the Aruduino Source (Make sure certificate, private key file names matches the ones placed in the root directory)
  • Tweak device module values based on your desired hardware setup. Blinds maximum turns step, Voltage cut off, Voltage sensor scale factor, Relay pin assignment etc,
  • Upload sketch via serial port to Arduino Yun.

Conclusion

Using Alexa with AWS Things Shadowed opens many possibilities for home automation voice control. I plan to use similar setup for other solar powered outdoor projects. For example, a rain barrel water level status monitor with auto valve flow control for irrigation.

Code

Lambda Code on AWSPython
Interpret Alexa custom skill voice input commands and set settings in IOT Things Shadows.
from __future__ import print_function

import logging
import time
import json
import uuid
import datetime

import boto3

IOT_THING_NAME = "SolarHomeController"
 
client = boto3.client('iot-data')
DEVICE_NAME_LIST= "window blinds, color l.e.d, charger, alarm, light"

def get_utc_timestamp(seconds=None):
    return time.strftime("%Y-%m-%dT%H:%M:%S.00Z", time.gmtime(seconds))

        
def update_shadow(itemKey, value): 
    response = client.update_thing_shadow(
        thingName = IOT_THING_NAME, 
        payload = json.dumps({
            'state': {
                'desired': {
                    itemKey : value
                }
            }})
        )
        
def get_shadow_value(name):
    response = client.get_thing_shadow(thingName = IOT_THING_NAME)
    streamingBody = response["payload"]
    jsonState = json.loads(streamingBody.read())
    if name == "*":
       return jsonState["state"]["reported"]
    return jsonState["state"]["reported"][name]

    

# --------------- Functions that control the skill's behavior ------------------

def get_welcome_response():
    session_attributes = {}
    card_title = "Welcome"
    speech_output = "Welcome to Solar battery power switch. Please specify a command"
    # 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 = "You can say check status. Or turn on a " + \
    "device Here are avaialbe devices names. " + DEVICE_NAME_LIST
  
    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 = "Good bye" 
                 
    # 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 create_favorite_color_attributes(favorite_color):
    return {"favoriteColor": favorite_color}

def set_color_in_session(intent, session):
    """ Sets the color of LED
    """

    card_title = intent['name']
    session_attributes = {}
    should_end_session = False

    if 'Color' in intent['slots']:
        favorite_color = intent['slots']['Color']['value']
        session_attributes = create_favorite_color_attributes(favorite_color)
        speech_output = "your favoriteis " + \
                        favorite_color + \
                        "what's my favorite color?"
        reprompt_text = "You ca"
    else:
        speech_output = "I'm not sure what your favorite color is. " \
                        "Please try again."
        reprompt_text = "I'm not sure what your favorite color is. " \
                        "You can tell me your favorite color by saying, " \
                        "my favorite color is red."
    return build_response(session_attributes, build_speechlet_response(
        card_title, speech_output, reprompt_text, should_end_session))


def get_color_from_session(intent, session):
    session_attributes = {}
    reprompt_text = None

    if session.get('attributes', {}) and "favoriteColor" in session.get('attributes', {}):
        favorite_color = session['attributes']['favoriteColor']
        speech_output = "Your favorite " + favorite_color + \
                        "Right"
        should_end_session = True
    else:
        speech_output = "Please repeat"
        should_end_session = False

    # 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.
    return build_response(session_attributes, build_speechlet_response(
        intent['name'], speech_output, reprompt_text, should_end_session))
       

def get_device_from_session(intent, session):
    session_attributes = {}
    reprompt_text = None

    if session.get('attributes', {}) and "deviceName" in session.get('attributes', {}):
        deviceName = session['attributes']['deviceName']
        speech_output = "You choose Device " + deviceName
        should_end_session = True
    else:
        speech_output = "Please specify a device. Here are list of devices. " + DEVICE_NAME_LIST
        should_end_session = False

    # 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.
    return build_response(session_attributes, build_speechlet_response(
        intent['name'], speech_output, reprompt_text, should_end_session))


        
def check_status(intent):
    card_title = "Status Query"
    keyItems = get_shadow_value("*")
    volt = keyItems["Voltage"]
    condition = "fair"
    if volt > 12.7:
        condition = "full"
    elif volt >= 12.4:
        condition = "good"
    elif volt >= 12.2:
        condition = "fair"
    elif volt >= 11.9:
        condition = "low"
    else:
        condition = "empty"
        
    try:
        speech_output = "Battery is " + condition + " at " + str(volt) + " Volts" 
        speech_output += ". Alarm is " + keyItems["alarm"] 
        speech_output += ". Charger is " + keyItems["charger"] 
        speech_output += ". Light is " + keyItems["light"]
        speech_output += ". l. e. d. Light is " + keyItems["led"]
        speech_output += ". Blinds Position at " + str(keyItems["BlindsPos"]) 
        
    except  KeyError, r:
        speech_output += " Key error"
    should_end_session = True
    return build_response({}, build_speechlet_response(
        card_title, speech_output, None, should_end_session))
    

def set_blinds_position(intent):
    card_title = "Blinds Position"
    should_end_session = True

    speech_output = "" 
    if 'percent' in intent['slots']:  
        if 'value' in intent['slots']['percent']:
            percent = intent['slots']['percent']['value']
            speech_output = "setting position to " + percent
            update_shadow('BlindsPos', int(percent))
    if 'open_close' in intent['slots']:
        if 'value' in intent['slots']['open_close']:

            isOpen = intent['slots']['open_close']['value']
            speech_output = "setting position to " + isOpen
            if isOpen == "open":
                percent = 50
            else:
                percent = 0
            update_shadow('BlindsPos', percent)
        
    if percent is None:
        speech_output = "Position not set."
    return build_response({}, build_speechlet_response(
        card_title, speech_output, None, should_end_session))

def switch_relay(intent, state = "none"):
    card_title = "Switch Device"
    speech_output = "Relay not set" 
    should_end_session = True
    ItemName = "unknown"

    if 'relay_number' in intent['slots']:    
        if 'value' in intent['slots']['relay_number']:
            ItemName = intent['slots']['relay_number']['value']
            speech_output = "setting " + ItemName + " to " + state
    if 'relay_name' in intent['slots']:    
        if 'value' in intent['slots']['relay_name']:
            ItemName = intent['slots']['relay_name']['value']
            speech_output = "setting " + ItemName + " to " + state
        
    if  state in {"On","Off"}:
        if ItemName in {"led","charger","alarm","light"}:
            update_shadow(ItemName, state)
        elif ItemName == "blinds":
            percent = 0
            if state is "On":
                percent = 50
            update_shadow('BlindsPos', percent)
            speech_output = "OK"
        else:
            speech_output = "Please choose one of following device. "  + DEVICE_NAME_LIST
            should_end_session = False
    return build_response({}, build_speechlet_response(
        card_title, speech_output, None, should_end_session))

# --------------- 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
    }
# --------------- 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):
    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=None):
    if session <> None:
        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 == "CheckBatteryCapacityIntent":
        return check_status(intent)
    elif intent_name == "SwitchOnRelayIntent":
        return switch_relay(intent,"On")
    elif intent_name == "SwitchOffRelayIntent":
        return switch_relay(intent,"Off")
    elif intent_name == "SetBlindsPositionIntent":
        return set_blinds_position(intent)
    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):
    print("on_session_ended requestId=" + session_ended_request['requestId'] +
          ", sessionId=" + session['sessionId'])
    # add cleanup logic here

def lambda_handler(event, context):
    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'])
Alexa Skill UtteranceJSON
{
  "languageModel": {
    "types": [
      {
        "name": "OpenPosition",
        "values": [
          {
            "id": "1",
            "name": {
              "value": "open",
              "synonyms": []
            }
          },
          {
            "id": "0",
            "name": {
              "value": "close",
              "synonyms": [
                "shut"
              ]
            }
          }
        ]
      },
      {
        "name": "RelayName",
        "values": [
          {
            "id": "4",
            "name": {
              "value": "LedLight",
              "synonyms": [
                "led light",
                "led",
                "color",
                "l.e.d."
              ]
            }
          },
          {
            "id": "3",
            "name": {
              "value": "AcLight",
              "synonyms": [
                "ac light",
                "main light",
                "light"
              ]
            }
          },
          {
            "id": "2",
            "name": {
              "value": "Alarm",
              "synonyms": []
            }
          },
          {
            "id": "1",
            "name": {
              "value": "Charger",
              "synonyms": [
                "battery charger"
              ]
            }
          },
          {
            "id": "0",
            "name": {
              "value": "blinds",
              "synonyms": [
                "window",
                "shades",
                "window shades"
              ]
            }
          }
        ]
      }
    ],
    "intents": [
      {
        "name": "AMAZON.CancelIntent",
        "samples": []
      },
      {
        "name": "AMAZON.HelpIntent",
        "samples": []
      },
      {
        "name": "AMAZON.StopIntent",
        "samples": []
      },
      {
        "name": "CheckBatteryCapacityIntent",
        "samples": [
          "check voltage",
          "battery level",
          "remaining capacity"
        ],
        "slots": []
      },
      {
        "name": "SetBlindsPositionIntent",
        "samples": [
          "set Blinds to {percent}",
          "turn blinds {percent}",
          "set blinds position to {percent}",
          "blinds {open_close}"
        ],
        "slots": [
          {
            "name": "percent",
            "type": "AMAZON.NUMBER"
          },
          {
            "name": "open_close",
            "type": "OpenPosition"
          }
        ]
      },
      {
        "name": "SwitchOffRelayIntent",
        "samples": [
          "turn off {relay_name}",
          "switch off {relay_name}",
          "turn {relay_name} off",
          "switch {relay_name} off",
          "turn off switch number {relay_number}",
          "close {relay_name}",
          "shut off {relay_name}",
          "off {relay_name}"
        ],
        "slots": [
          {
            "name": "relay_name",
            "type": "RelayName"
          },
          {
            "name": "relay_number",
            "type": "AMAZON.NUMBER"
          }
        ]
      },
      {
        "name": "SwitchOnRelayIntent",
        "samples": [
          "turn {relay_name} on",
          "switch on {relay_name}",
          "switch {relay_name} on",
          "open {relay_name}",
          "turn on {relay_name}",
          "on {relay_name}"
        ],
        "slots": [
          {
            "name": "relay_number",
            "type": "AMAZON.NUMBER"
          },
          {
            "name": "relay_name",
            "type": "RelayName"
          }
        ]
      }
    ],
    "invocationName": "solar switch"
  }
}
Arduino Yun Sketch
Sketch for battery charging, window blinds control, alarm light trigger, and accessing AWS IoT Things Shadowed.

Custom parts and enclosures

Blind Motor Rod Adaptor
Adapter to fit end of blind wand to stepper motor rod.

Schematics

Wire Diagram
Diagram eknjihgblj

Comments

Similar projects you might like

Lightpipe 7-Segment Display

Project tutorial by Brian Lough

  • 2,712 views
  • 3 comments
  • 8 respects

ElBanquos 1D Pong

Project showcase by ElBanquo

  • 157 views
  • 2 comments
  • 2 respects

Arduino - Control Arm Robot via Web

Project tutorial by phpoc_man

  • 27 views
  • 0 comments
  • 4 respects

Arduino Spider Robot (Quadruped)

Project tutorial by MEGA DAS

  • 2,102 views
  • 1 comment
  • 14 respects

PENXZYL: Arduino 3-Axis Brush Plotter

Project showcase by Guiye Perez Bongiovanni

  • 4,483 views
  • 4 comments
  • 20 respects

Google Chrome Dinosaur Game on 16x2 LCD Shield

Project showcase by brzi

  • 388 views
  • 1 comment
  • 7 respects
Add projectSign up / Login