Dave Wilson
Published © MIT

Alexa Voice Alarm Clock

21st century alarm clocks need to be smart, voice-enabled, integrate with the Internet of Things, and you only need a swipe to silent them.

IntermediateWork in progress2 hours1,556
Alexa Voice Alarm Clock

Things used in this project

Hardware components

Raspberry Pi 3 Model B
Raspberry Pi 3 Model B
×1
Adafruit 0.56" 4-Digit 7-Segment Display w/I2C Backpack - Yellow
×1
Adafruit VCNL4010 Proximity/Light sensor
×1
PIR Motion Sensor (generic)
PIR Motion Sensor (generic)
×1
Linear Regulator (Low Dropout)
Linear Regulator (Low Dropout)
×1
Breadboard (generic)
Breadboard (generic)
×1
AYL Portable Mini Capsule Speaker System
×1
128x64 OLED display with I2C
×1
USB microphone
×1
Jumper wires (generic)
Jumper wires (generic)
×1

Software apps and online services

Alexa Voice Service
Amazon Alexa Alexa Voice Service
Apple Garage Band

Story

Read more

Custom parts and enclosures

Inside the project box

All of the components fit into the 106 year old antique box. I used an acrylic stand to hold the displays and sensors.

Code

VCNL4010.py

Python
This is a python class to access the VCNL-4010 proximity/light sensor. It is based on Adafruit's library and the code example from Vishay
# The MIT License (MIT)
#
# Modern Art Electronics Project
#
# Vishay Proximity/Ambient Light Sensor VCNL4010 Class
#
# David Wilson
# Word derived from the Vishay and Adafruit library.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import time

VCNL4010_ADDRESS               = 0x13   # 001 0011 shifted left 1 bit = 0x26
 
# registers 
REGISTER_COMMAND               = 0x80
REGISTER_ID                    = 0x81
REGISTER_PROX_RATE             = 0x82
REGISTER_PROX_CURRENT          = 0x83
REGISTER_AMBI_PARAMETER        = 0x84
REGISTER_AMBI_VALUE            = 0x85
REGISTER_PROX_VALUE            = 0x87
REGISTER_INTERRUPT_CONTROL     = 0x89
REGISTER_INTERRUPT_LOW_THRES   = 0x8a
REGISTER_INTERRUPT_HIGH_THRES  = 0x8c
REGISTER_INTERRUPT_STATUS      = 0x8e
REGISTER_PROX_TIMING           = 0x8f
REGISTER_AMBI_IR_LIGHT_LEVEL   = 0x90   # This register is not intended to be use by customer
 
# Bits in Command register = 0x80

COMMAND_ALL_DISABLE            = 0x00
COMMAND_SELFTIMED_MODE_ENABLE  = 0x01
COMMAND_PROX_ENABLE            = 0x02
COMMAND_AMBI_ENABLE            = 0x04
COMMAND_PROX_ON_DEMAND         = 0x08
COMMAND_AMBI_ON_DEMAND         = 0x10
COMMAND_MASK_PROX_DATA_READY   = 0x20
COMMAND_MASK_AMBI_DATA_READY   = 0x40
COMMAND_MASK_LOCK              = 0x80
 
# Bits in Product ID Revision Register = 0x81

PRODUCT_MASK_REVISION_ID       = 0x0f
PRODUCT_MASK_PRODUCT_ID        = 0xf0
 
# Bits in Prox Measurement Rate register = 0x82

PROX_MEASUREMENT_RATE_2        = 0x00   # DEFAULT
PROX_MEASUREMENT_RATE_4        = 0x01
PROX_MEASUREMENT_RATE_8        = 0x02
PROX_MEASUREMENT_RATE_16       = 0x03
PROX_MEASUREMENT_RATE_31       = 0x04
PROX_MEASUREMENT_RATE_62       = 0x05
PROX_MEASUREMENT_RATE_125      = 0x06
PROX_MEASUREMENT_RATE_250      = 0x07
PROX_MASK_MEASUREMENT_RATE     = 0x07
 
# Bits in Procimity LED current setting = 0x83

PROX_MASK_LED_CURRENT          = 0x3f   # DEFAULT = 2
PROX_MASK_FUSE_PROG_ID         = 0xc0
 
# Bits in Ambient Light Parameter register = 0x84

AMBI_PARA_AVERAGE_1            = 0x00
AMBI_PARA_AVERAGE_2            = 0x01
AMBI_PARA_AVERAGE_4            = 0x02
AMBI_PARA_AVERAGE_8            = 0x03
AMBI_PARA_AVERAGE_16           = 0x04
AMBI_PARA_AVERAGE_32           = 0x05   # DEFAULT
AMBI_PARA_AVERAGE_64           = 0x06
AMBI_PARA_AVERAGE_128          = 0x07
AMBI_MASK_PARA_AVERAGE         = 0x07
 
AMBI_PARA_AUTO_OFFSET_ENABLE   = 0x08   # DEFAULT enable
AMBI_MASK_PARA_AUTO_OFFSET     = 0x08
 
AMBI_PARA_MEAS_RATE_1          = 0x00
AMBI_PARA_MEAS_RATE_2          = 0x10   # DEFAULT
AMBI_PARA_MEAS_RATE_3          = 0x20
AMBI_PARA_MEAS_RATE_4          = 0x30
AMBI_PARA_MEAS_RATE_5          = 0x40
AMBI_PARA_MEAS_RATE_6          = 0x50
AMBI_PARA_MEAS_RATE_8          = 0x60
AMBI_PARA_MEAS_RATE_10         = 0x70
AMBI_MASK_PARA_MEAS_RATE       = 0x70
 
AMBI_PARA_CONT_CONV_ENABLE     = 0x80
AMBI_MASK_PARA_CONT_CONV       = 0x80   # DEFAULT disable
 
# Bits in Interrupt Control Register = x89

INTERRUPT_THRES_SEL_PROX       = 0x00
INTERRUPT_THRES_SEL_ALS        = 0x01
 
INTERRUPT_THRES_ENABLE         = 0x02
 
INTERRUPT_ALS_READY_ENABLE     = 0x04
 
INTERRUPT_PROX_READY_ENABLE    = 0x08
 
INTERRUPT_COUNT_EXCEED_1       = 0x00   # DEFAULT
INTERRUPT_COUNT_EXCEED_2       = 0x20
INTERRUPT_COUNT_EXCEED_4       = 0x40
INTERRUPT_COUNT_EXCEED_8       = 0x60
INTERRUPT_COUNT_EXCEED_16      = 0x80
INTERRUPT_COUNT_EXCEED_32      = 0xa0
INTERRUPT_COUNT_EXCEED_64      = 0xc0
INTERRUPT_COUNT_EXCEED_128     = 0xe0
INTERRUPT_MASK_COUNT_EXCEED    = 0xe0 
 
# Bits in Interrupt Status Register = x8e

INTERRUPT_STATUS_THRES_HI      = 0x01
INTERRUPT_STATUS_THRES_LO      = 0x02
INTERRUPT_STATUS_ALS_READY     = 0x04
INTERRUPT_STATUS_PROX_READY    = 0x08
INTERRUPT_MASK_STATUS_THRES_HI = 0x01
INTERRUPT_MASK_THRES_LO        = 0x02
INTERRUPT_MASK_ALS_READY       = 0x04
INTERRUPT_MASK_PROX_READY      = 0x08
 

class VCNL4010(object):
    """VCNL40xx proximity sensors."""

    def __init__(self, address=VCNL4010_ADDRESS, i2c=None, **kwargs):
        """Initialize the VCNL40xx sensor."""
        # Setup I2C interface for the device.
        if i2c is None:
            import Adafruit_GPIO.I2C as I2C
            i2c = I2C
        self._device = i2c.get_i2c_device(address, **kwargs)
        self.reset()
        
    def reset(self,):
        byte = self.getProductIDRegister()
        # print "Product ID=",byte
        self.setCommandRegister(COMMAND_ALL_DISABLE);
        self.setProximityRate (PROX_MEASUREMENT_RATE_31);
        # enable proximity and ambiant in selftimed mode
        self.setCommandRegister(COMMAND_PROX_ENABLE|COMMAND_AMBI_ENABLE|COMMAND_SELFTIMED_MODE_ENABLE)
        # set interrupt control for threshold
        self.setInterruptControl(INTERRUPT_THRES_SEL_PROX|INTERRUPT_THRES_ENABLE|INTERRUPT_COUNT_EXCEED_1)
        #set ambient light measurement parameter
        self.setAmbientConfiguration(AMBI_PARA_AVERAGE_32|AMBI_PARA_AUTO_OFFSET_ENABLE|AMBI_PARA_MEAS_RATE_2)
        
    def calibrate(self,):
        sum = 0
        for x in xrange(1, 30):
            sum = sum + self.getProximityOnDemand()
        sum = sum / 30
        offset = sum + 100
        self.reset()
        self.setHighThreshold(offset)
        # print('Proximity={0}, Threshold={1}'.format(sum, offset))
        # enable proximity and ambiant in selftimed mode
        self.setCommandRegister(COMMAND_PROX_ENABLE|COMMAND_AMBI_ENABLE|COMMAND_SELFTIMED_MODE_ENABLE)

    def getProductIDRegister(self,):
        result = self._device.readU8(REGISTER_ID)
        return result
         
    def setCommandRegister(self, command):
        self._device.write8(REGISTER_COMMAND,command)
        
    def getCommandRegister(self, ):
        return self._device.readU8(REGISTER_COMMAND)
        
    def setProximityRate(self, command):
        self._device.write8(REGISTER_PROX_RATE,command)     
        
    def setProximityCurrent(self, command):
        self._device.write8(REGISTER_PROX_CURRENT,command)  
    
    def setInterruptControl(self, command):
        self._device.write8(REGISTER_INTERRUPT_CONTROL,command)
        
    def getInterruptControl(self,):
        result = self._device.readU8(REGISTER_INTERRUPT_CONTROL)
        return result
        
    def setInterruptStatus (self, command):
        self._device.write8(REGISTER_INTERRUPT_STATUS,command)
        
    def getInterruptStatus (self,):
        result = self._device.readU8(REGISTER_INTERRUPT_STATUS)
        return result
        
    def setAmbientConfiguration (self, command):
        self._device.write8(REGISTER_AMBI_PARAMETER,command)
        
    def setLowThreshold (self, command):
        loByte = (command & 0xff)
        hiByte = ((command >> 8) & 0xff)
        self._device.write8(REGISTER_INTERRUPT_LOW_THRES,hiByte)
        self._device.write8(REGISTER_INTERRUPT_LOW_THRES+1,loByte)
        
    def setHighThreshold (self, command):
        loByte = (command & 0xff)
        hiByte = ((command >> 8) & 0xff)
        self._device.write8(REGISTER_INTERRUPT_HIGH_THRES,hiByte)
        self._device.write8(REGISTER_INTERRUPT_HIGH_THRES+1,loByte)
    
    def setModulatorTimingAdjustment (self, command):  
        self._device.write8(REGISTER_PROX_TIMING,command)
        
    def getProximityValue (self,):
        return self._device.readU16BE(REGISTER_PROX_VALUE); 
        
    def getAmbientValue (self,):
        return self._device.readU16BE(REGISTER_AMBI_VALUE); 

    def getProximityOnDemand (self,):
        self.setCommandRegister (COMMAND_PROX_ENABLE | COMMAND_PROX_ON_DEMAND)
        command = self.getCommandRegister ()   
        while ( (command & COMMAND_MASK_PROX_DATA_READY) == 0 ):
            command = self.getCommandRegister ()  
        result = self._device.readU16BE(REGISTER_PROX_VALUE)                        
        # self.reset() 
        return result
        
    def getAmbientOnDemand (self,):
        self.setCommandRegister (COMMAND_AMBI_ENABLE | COMMAND_AMBI_ON_DEMAND)
        command = self.getCommandRegister ()   
        while ( (command & COMMAND_MASK_AMBI_DATA_READY) == 0 ):
            command = self.getCommandRegister ()  
        result = self._device.readU16BE(REGISTER_AMBI_VALUE)                        
        # self.reset()
        return result
    

if __name__ == '__main__':
  print "VCNL4010 Driver Running"
  vcnl = VCNL4010()
  vcnl.calibrate()
  print "modes a,b,i,p"
  mode=raw_input()
  if mode.lower()=='a':
    while True:
      val = vcnl.getAmbientValue()
      print "Ambient=",val
      time.sleep(1.0)
  elif mode.lower()=='p':
    while True:
      val = vcnl.getProximityValue()
      print "proximity=",val
      time.sleep(1.0)
  else:
    while True:
      proximity = vcnl.getProximityOnDemand()
      ambient = vcnl.getAmbientOnDemand()
      print('Proximity={0}, Ambient light={1}'.format(proximity, ambient))
      time.sleep(1.0)
 

Seven Segment LED Clock Class

Python
Python class that encapsulates a simple digital clock. It uses the Adafruit I2C libraries
#!/usr/bin/python

# ===========================================================================
# ClockLED
# ===========================================================================

import time
from Adafruit_LED_Backpack import SevenSegment
from threading import Thread

class LED_Clock(object):

    def __init__(self,):
        # Create display instance on default I2C address (0x70) and bus number.
        self.display = SevenSegment.SevenSegment()
        # Initialize the display. Must be called once before using the display.
        self.display.begin()
        self.brightness = 7
        self.display.set_brightness(self.brightness)
        self.colon = False
        self.alarm = False
        self.timeUpdateRunning = False
        self.timeFormat = "%l%M"

    def timeUpdateThread(self,):
        # print "started timeUpdateThread"
        while self.timeUpdateRunning:
            time.sleep(1.0)
            digitString = time.strftime(self.timeFormat)
            self.display.clear()
            self.display.print_number_str(digitString)
            self.display.set_colon(self.colon)
            if (self.colon):
                self.colon = False
            else:
                self.colon = True
            self.display.set_decimal(3, self.alarm)
            self.display.write_display()

    def setTime24(self,Time24=True):
        if Time24:
            self.timeFormat = "%I%M"
        else:
            self.timeFormat = "%l%M"

    def setAlarm(self,val):
        self.alarm = val

    def setBrightness(self,val):
        # print("set brightness="+str(val))
        self.brightness = val
        self.display.set_brightness(self.brightness)

    def increaseBrightness(self,):
        self.brightness = self.brightness + 1
        if self.brightness > 15:
            self.brightness = 15
        self.display.set_brightness(self.brightness)

    def decreaseBrightness(self,):
        self.brightness = self.brightness - 1
        if self.brightness < 0:
            self.brightness = 0
        self.display.set_brightness(self.brightness)

    def run(self):
        self.timeUpdateRunning = True
        self.tuThread = Thread(target=self.timeUpdateThread )
        self.tuThread.daemon = True
        self.tuThread.start()

if __name__ == '__main__':
    # print "ClockLED"
    clock = LED_Clock()
    clock.run()
    while True:
        # simple input driver
        choice = raw_input("> ")

        if choice == 'x' :
            print "exiting"
            clock.timeUpdateRunning = False
            time.sleep(2.0)
            break
        elif choice == 'a':
            if clock.alarm:
                clock.alarm = False
            else:
                clock.alarm = True
        elif choice == '+':
            clock.increaseBrightness()
            print "brightness=",clock.brightness
        elif choice == '-':
            clock.decreaseBrightness()
            print "brightness=",clock.brightness
        else:
            print ("e)xit or a)larm or + or -")

OLED I2C Display Class

Python
Simple python class that encapsulates a basic UI for the alarm clock - Day of week, IP address of Pi, etc.
#!/usr/bin/python

# ===========================================================================
# DisplayOLED
# ===========================================================================

import time
import Adafruit_GPIO.SPI as SPI
import Adafruit_SSD1306

from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw

import socket

hostname = socket.gethostname()
IP = socket.gethostbyname(hostname)

from threading import Thread

class OLED_Display(object):

    def __init__(self,):
        # Raspberry Pi pin configuration:
        RST = 24
        # 128x64 display with hardware I2C:
        self.disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST)
        self.disp.begin()
        self.disp.clear()
        self.disp.display()
        # Create image buffer. Make sure to create image with mode '1' for 1-bit color.
        self.image = Image.new('1', (self.disp.width, self.disp.height))
        self.font = ImageFont.truetype("/home/pi/Fonts/Tahoma.ttf",12)
        # self.font = ImageFont.load_default()
        # Alternatively load a TTF font.  Make sure the .ttf font file is in the same directory as this python script!
        # Some nice fonts to try: http://www.dafont.com/bitmap.php
        self.draw = ImageDraw.Draw( self.image )
        self.hostname = socket.gethostname()
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        #connect to any target website
        s.connect(('google.com', 0))
        self.ipAddress = s.getsockname()[0]
        s.close()
        self.oledUpdateRunning = False
        self.sleepingOled = False
        self.line3 = ""
        self.line4 = ""
        self.wakeUpImage = Image.open('/home/pi/Projects/wakeUpImage.jpg').convert('1')
        self.speakImage = Image.open('/home/pi/Projects/speakImage.jpg').convert('1')
        self.mode = 'text'

    def updateDisplay(self,):
        self.disp.clear()
        # Clear image buffer by drawing a black filled box.
        self.draw.rectangle((0,0,self.disp.width,self.disp.height), outline=0, fill=0)
        if not self.sleepingOled:
            if self.mode == 'speakImage':
                self.disp.image(self.speakImage)
            elif self.mode == 'wakeUpImage':
                self.disp.image(self.wakeUpImage)
            else:
                dayOfWeekString = time.strftime("%A %b %-d")
                self.draw.text((4,1), dayOfWeekString, font=self.font, fill=255)
                self.draw.text((2,16), self.ipAddress, font=self.font, fill=255)
                self.draw.text((2,32), self.line3, font=self.font, fill=255)
                self.draw.text((2,48), self.line4, font=self.font, fill=255)
                self.disp.image(self.image)
        self.disp.display()

    def oledUpdateThread(self,):
        # print "started oledeUpdateThread"
        while self.oledUpdateRunning:
            self.updateDisplay()
            time.sleep(0.5)

    def setImageMode(self,newMode='text'):
        self.mode = newMode
        self.updateDisplay()

    def setLine3(self,val=""):
        self.line3 = val
        self.updateDisplay()

    def setLine4(self,val=""):
        self.line4 = val
        self.updateDisplay()

    def setSleepMode(self,sleep):
        # print("set sleep mode="+str(sleep))
        self.sleepingOled = sleep
        self.updateDisplay()

    def run(self):
        self.oledUpdateRunning = True
        self.oThread = Thread(target=self.oledUpdateThread )
        self.oThread.daemon = True
        self.oThread.start()

if __name__ == '__main__':
    # print "DisplayOLED"
    oled = OLED_Display()
    oled.run()
    while True:
        time.sleep(5.0)
        oled.setImageMode('speakImage')
        time.sleep(5.0)
        oled.setImageMode('text')
        time.sleep(5.0)
        oled.setImageMode('wakeUpImage')
        time.sleep(5.0)
        oled.setImageMode('text')

Alexa Voice Class

Python
Modified version of the Alexa Voice class
#!/usr/bin/python

# ===========================================================================
# Alexa Voice Handler
# ===========================================================================

import time
from threading import Thread
from Queue import Queue
import subprocess

import os
#import random
import alsaaudio
import wave
from creds import *
import requests
import json
import re
from memcache import Client

servers = ["127.0.0.1:11211"]
mc = Client(servers, debug=1)
path = os.path.realpath(__file__).rstrip(os.path.basename(__file__))
device = "plughw:1" # Name of your microphone/soundcard in arecord -L

class AlexaVoice(object):

    def __init__(self,):
        # Create display instance on default I2C address (0x70) and bus number.
        self.commandQueue = Queue()
        self.running = False
        self.defaultCommand = "/home/pi/Voices/time.wav"

    def internetAvailable(self,):
        # print "Checking Internet Connection"
        try:
            r =requests.get('https://api.amazon.com/auth/o2/token')
            # print "Connection OK"
            return True
        except:
            # print "Connection Failed"
            return False
        
    def getAmazonToken(self,):
        # print "getAmazonToke"
        token = mc.get("access_token")
        refresh = refresh_token
        if token:
		    return token
        elif refresh:
		    payload = {"client_id" : Client_ID, "client_secret" : Client_Secret, "refresh_token" : refresh, "grant_type" : "refresh_token", }
		    url = "https://api.amazon.com/auth/o2/token"
		    r = requests.post(url, data = payload)
		    resp = json.loads(r.text)
		    mc.set("access_token", resp['access_token'], 3570)
		    return resp['access_token']
        else:
            return False
		    
    def getVerbalCommand(self,):
	    inp = alsaaudio.PCM(alsaaudio.PCM_CAPTURE, alsaaudio.PCM_NORMAL, device)
	    inp.setchannels(1)
	    inp.setrate(16000)
	    inp.setformat(alsaaudio.PCM_FORMAT_S16_LE)
	    inp.setperiodsize(500)
	    audio = ""
	    t_end = time.time() + 5
	    while time.time() < t_end:
	        l, data = inp.read()
	        if l:
	            audio += data
	    rf = open('/home/pi/Recordings/recording.wav', 'w')
	    rf.write(audio)
	    rf.close()
	    inp = None
	    
    def startVerbalCommand(self,command='/home/pi/Recordings/recording.wav'):
        self.commandQueue.put(command)
        
    def startDialog(self,command='/home/pi/Voices/time.wav'):
       self.commandQueue.put(command)
    
    def alexaVoiceHandler(self,):
        while self.running:
            command = self.commandQueue.get(block=True)
            url = 'https://access-alexa-na.amazon.com/v1/avs/speechrecognizer/recognize'
            headers = {'Authorization' : 'Bearer %s' % self.getAmazonToken()}
            d = {
   	            "messageHeader": {
       		        "deviceContext": [
           		        {
               		        "name": "playbackState",
               		        "namespace": "AudioPlayer",
               		        "payload": {
                   		        "streamId": "",
        			   	        "offsetInMilliseconds": "0",
                                "playerActivity": "IDLE"
                            }
                        }
                    ]
                },
   	            "messageBody": {
                    "profile": "alexa-close-talk",
                    "locale": "en-us",
                    "format": "audio/L16; rate=16000; channels=1"
                }
            }

            with open(command) as inf:
                files = [
                        ('file', ('request', json.dumps(d), 'application/json; charset=UTF-8')),
                        ('file', ('audio', inf, 'audio/L16; rate=16000; channels=1'))
                        ]	
                r = requests.post(url, headers=headers, files=files)
            if r.status_code == 200:
                for v in r.headers['content-type'].split(";"):
                    if re.match('.*boundary.*', v):
                        boundary =  v.split("=")[1]
                data = r.content.split(boundary)
                for d in data:
                    if (len(d) >= 1024):
                        audio = d.split('\r\n\r\n')[1].rstrip('--')
                with open(path+"response.mp3", 'wb') as f:
                    f.write(audio)
                os.system('mpg123 -q {}1sec.mp3 {}response.mp3 {}1sec.mp3'.format(path, path, path))
            else:
                print "http status=",r.status_code
            
    def run(self,):
        while self.internetAvailable() == False:
		    print "."
        self.getAmazonToken()
        self.running = True
        self.tuThread = Thread(target=self.alexaVoiceHandler )
        self.tuThread.daemon = True
        self.tuThread.start()  

if __name__ == '__main__':
    # print "ClockLED"
    alexa = AlexaVoice()
    alexa.run()
    while True:
        # simple input driver
        choice = raw_input("> ")

        if choice == 'x' :
            print "exiting"
            alexa.running = False
            time.sleep(2.0)
            break
        elif choice == 't':
            alexa.startDialog("/home/pi/Voices/time.wav")
        
        elif choice == 'w':
            alexa.startDialog("/home/pi/Voices/weather.wav")
            
        else:
            print ("e)xit or a)larm or + or -")

My Logger Class

Python
Simple python class that encapsulates logging
#!/usr/bin/python

# ===========================================================================
# Rotating Logger Class
# ===========================================================================

import logging
from logging.handlers import RotatingFileHandler

class MyLogger(object):

  def __init__(self, fileName='logging.log', fileSize=1024*1024, fileCopies=5):
    self.logger = logging.getLogger(fileName)
    self.logger.setLevel(logging.INFO)
    self.handler = RotatingFileHandler(filename=fileName,maxBytes=fileSize,backupCount=fileCopies)
    self.formatter = logging.Formatter('%(asctime)s :  %(message)s')
    self.handler.setFormatter(self.formatter)
    self.logger.addHandler(self.handler)
    
  def write(self,msg):
    self.logger.info(msg)

if __name__ == '__main__':
  print "Logging from main"

PIR and VCNL4010 Interrupt Handler

Python
Simple class to handle interrupts from PIR an VNCL sensors
#!/usr/bin/python
  
import sys
import smbus
from Queue import Queue
import threading 
import datetime
import time

import RPi.GPIO as GPIO
from Adafruit_I2C import Adafruit_I2C
import VCNL4010

# ===========================================================================
# AVAC HW Class
# =========================================================================== 
    
class AVAC_HW(object):
  
    def __init__(self):
        #define 3 queues - log entries, send and receive messages
        self.pirQueue = Queue()
        self.vcnlQueue = Queue()
        self.interruptQueue = Queue()
        self.vcnl4010 = VCNL4010.VCNL4010()
        self.vcnl4010.calibrate()
        self.ambient = 0
        self.newEvent = time.time()
        self.lastEvent = time.time()
        # setup the GPIO bus and 2 interrupts
        GPIO.setmode(GPIO.BCM) 
        GPIO.setup(24, GPIO.IN,pull_up_down=GPIO.PUD_UP)  # GPIO pin 24 is the PIR interrupt
        GPIO.setup(23, GPIO.IN)  # GPIO pin 23 is the VCNL4010 interrupt
        GPIO.add_event_detect(24, GPIO.FALLING, callback=self.pirInterruptHandler) 
        GPIO.add_event_detect(23, GPIO.FALLING, callback=self.vcnlInterruptHandler)
 
    def pirWorkAround(self,):
        self.newEvent = time.time()
        self.elapsed = self.newEvent - self.lastEvent
        # print "seconds=",self.elapsed
        self.lastEvent = self.newEvent
        
    def interruptHelper(self,iQ,pQ,vQ):
        # print "interruptHandler running"
        while True:
            event = self.interruptQueue.get()
            if event == "PIR":
                m = time.strftime("PIR %y-%m-%d %H:%M:%S")
                self.pirQueue.put(m)
                # print m
            else:
                m = time.strftime("VCNL %y-%m-%d %H:%M:%S")
                self.vcnlQueue.put(m)
                # print m
                
    # define threaded callback function for the atmega328pu interrupt 
    def pirInterruptHandler(self,channel):  
        self.interruptQueue.put("PIR")
        print "PIR INT"
    
     # define threaded callback function for the atmega328pu interrupt 
    def vcnlInterruptHandler(self,channel): 
        val = self.vcnl4010.getInterruptStatus()
        self.vcnl4010.setInterruptStatus(val)
        self.interruptQueue.put("VCNL")
        # print "VCNL INT"
        
    def pirWait(self,blocking):
        if blocking:
            print "1 pirWait: blocking"
            data = self.pirQueue.get(blocking)
            # print "2 pirWait: data=",data
            self.pirWorkAround()
            return data
        else:
            if self.pirQueue.empty():
                print "3 pirWait non block: EMPTY"
                return ""
            else:
                data = self.pirQueue.get(False)
                print "4 pirWait non block: data=",data
                self.pirWorkAround()
                return data
        
    def vcnlWait(self,blocking):
        if blocking:
            data = self.vcnlQueue.get(True)
            return data
        else:
            if self.vcnlQueue.empty():
                return ""
            else:
                data = self.vcnlQueue.get(False)
                return data

    def run(self,):
        hwThread = threading.Thread(target=self.interruptHelper,args=(self.interruptQueue,self.pirQueue,self.vcnlQueue))
        hwThread.start()
        
    def exitCleanUp(self):
        self.running = False
        GPIO.cleanup()           # clean up GPIO on normal exit  

if __name__ == '__main__':
  print "AVAC HW Main Running"
  hw = AVAC_HW()
  hw.run()
  while True:
    # time.sleep(0.5)
    m = hw.pirWait(True)
    print m
    if len(m) > 0:
        m = hw.pirWait(False)
        

Alexa Voice Alarm Clock

Python
Work in progress. 21st century alarm clock based on Alexa Voice Service and Raspberry Pi hardware
# Alexa Voice Alarm Clock
# Dave Wilson
# MIT license
# Based on Adafruit exmaples and other great developers

# Derived from - Example of using the MQTT client class to subscribe to and publish feed values.
# Author: Tony DiCola

# Import standard python modules.
import os
import random
import sys
import time
import subprocess
import threading
import alsaaudio

# Import Adafruit IO MQTT client.
from Adafruit_IO import MQTTClient

# Set to your Adafruit IO key & username below.
ADAFRUIT_IO_KEY      = '---'
ADAFRUIT_IO_USERNAME = '---'  # See https://accounts.adafruit.com

import AlexaVoice
import LED_Clock
import OLED_Display
import VCNL4010
import PIR_HW
import VCNL_HW
import MyLogger

global clock
clock = LED_Clock.LED_Clock()
clock.run()

global oled
oled = OLED_Display.OLED_Display()
oled.run()

global vcnl
vcnl = VCNL4010.VCNL4010()
vcnl.calibrate()

global vcnl_interrupt
vcnl_interrupt = VCNL_HW.VCNL_HW()

global pir_interrupt
pir_interrupt = PIR_HW.PIR_HW()

global myprocess
myprocess = None

global alarmTime
alarmTime = ""

global alarmOn
alarmOn = False

global lightOn
lightOn = False

global ID_WAKEUP
ID_WAKEUP = '600509'
global ID_MUSIC
ID_MUSIC = '600620'
global ID_ALARM_TIME
ID_ALARM_TIME = '601507'
global ID_LIGHT_STRIP
ID_LIGHT_STRIP = '601387'
global ID_SLEEP_MODE
ID_SLEEP_MODE = '601554'
global ID_MOTION
ID_MOTION = '600511'


#set up the status logger
global logger
logger = MyLogger.MyLogger(fileName="/home/pi/LogFiles/RotatingLog.log",fileSize=1024*1024)
logger.write("MQTT_AVAC STARTED")


# Define callback functions which will be called when certain events happen.
def connected(client):
    client.subscribe(ID_WAKEUP) # wake up alarm swith
    client.subscribe(ID_MUSIC) # music switch
    client.subscribe(ID_ALARM_TIME) # alarm time
    client.subscribe(ID_LIGHT_STRIP) # light strip
    client.subscribe(ID_SLEEP_MODE) # System sleep mode

def disconnected(client):
    sys.exit(1)

def message(client, feed_id, payload):
    global myprocess
    global alarmTime
    # wake up alarm switch
    if feed_id == ID_WAKEUP:
        alarmOn = False
        if payload == "ON":
            clock.alarm = True
            logger.write("WAKEUP ON")
        else:
            clock.alarm = False
            logger.write("WAKEUP OFF")
    # music switch
    elif feed_id == ID_MUSIC:
        if payload == "ON":
            dirStr = '/home/pi/Music/'
            fileStr = time.strftime("%A")
            pathStr = dirStr + fileStr + '.m4a'
            myprocess = subprocess.Popen(['omxplayer','-b',pathStr],
            stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
            oled.setLine3("Music Player")
            logger.write("MUSIC ON")
        else:
            logger.write("MUSIC OFF")
            if myprocess != None:
                try:
                    myprocess.stdin.write('q')
                except IOError as e:
                    logger.write("BROKEN PIPE: OMXPLAYER")
                oled.setLine3("")
    # light switch
    elif feed_id == ID_LIGHT_STRIP:
        if payload == "ON":
            lightOn = True
            logger.write("LIGHT STRIP ON")
            oled.setLine3("Night Light: ON")
        else:
            lightOn = False
            logger.write("LIGHT STRIP OFF")
            oled.setLine3("Night Light: OFF")
    # brighthness controls during sleep
    elif feed_id == ID_SLEEP_MODE:
        if payload == "ON":
            logger.write("SLEEP ON")
            clock.setBrightness(0)
            oled.setSleepMode(True)
        else:
            logger.write("SLEEP OFF")
            clock.setBrightness(7)
            oled.setSleepMode(False)
    # wake up time
    elif feed_id == ID_ALARM_TIME: # alarm time
        logger.write("ALARM TIME: "+payload)
        alarmTime = payload
        oled.setLine4(alarmTime)
    else:
        logger.write("UNDEFINED MESSAGE")
        oled.setLine4("TILT")


### MAIN THREAD ###

global client
# Create an MQTT client instance.
client = MQTTClient(ADAFRUIT_IO_USERNAME, ADAFRUIT_IO_KEY)

# Setup the callback functions defined above.
client.on_connect    = connected
client.on_disconnect = disconnected
client.on_message    = message

# Connect to the Adafruit IO server.
client.connect()

client.loop_background()

# check for proximity user command
def userMonitor():
    global alarmOn
    global alarmWait
    global vcnl_interrupt
    global logger
    global alexa
    global oled
    logger.write("USER MONITOR THREAD STARTED")
    while True:
        switch = vcnl_interrupt.wait(True)
        while len(switch) > 0:
            switch = vcnl_interrupt.wait(False)
            # time.sleep(0.1)
        if alarmOn:
            alarmOn = False
            logger.write("STOP ALARM PROCESS")
        else:
            oled.setImageMode('speakImage')
            alexa.getVerbalCommand()
            oled.setImageMode('text')
            alexa.startVerbalCommand()


# check for pir movement
def motionMonitor():
    global pir_interrupt
    global logger
    global alexa
    global client
    logger.write("MOTION MONITOR THREAD STARTED")
    # print "motionMonitor"
    lastMotion = 0
    currentMotion = 0
    waitForMotion = True
    #client.publish(ID_MOTION,currentMotion)
    while True:
        time.sleep(0.1)
        motion = pir_interrupt.wait(waitForMotion)
        # logger.write(motion)
        if len(motion) > 0:
            waitForMotion = False
            while len(motion) > 0:
                # logger.write(motion)
                motion = pir_interrupt.wait(waitForMotion)
            currentMotion = 1
            logger.write("MOTION DETECTED")
        else:
            currentMotion = 0
            waitForMotion = True
            logger.write("MOTION NOT DETECTED")
        if not (lastMotion == currentMotion):
            lastMotion = currentMotion
            #client.publish(ID_MOTION,currentMotion)

alexa = AlexaVoice.AlexaVoice()
alexa.run()

# initialize the clock

time.sleep(5.0)
client.publish(ID_SLEEP_MODE, "OFF")
client.publish(ID_LIGHT_STRIP, "OFF")
client.publish(ID_WAKEUP, "OFF")
client.publish(ID_MUSIC, "OFF")
client.publish(ID_ALARM_TIME, "05:00")
time.sleep(5.0)

userThread = threading.Thread(target=userMonitor)
userThread.start()

#motionThread = threading.Thread(target=motionMonitor)
#motionThread.start()

frame = 0

### Loop forever checking for the alarm

alarmOn = False

while True:
    time.sleep(0.1)
    frame = frame + 1

    if clock.alarm:
        digitString = time.strftime("%H:%M")
        if str(alarmTime) == str(digitString):
            alarmOn = True
            logger.write("START ALARM PROCESS")
            oled.setImageMode('wakeUpMode')
            os.system('mpg123 -q /home/pi/Voices/AVAC-Hello-Time-To-Get-Up.mp3')
            time.sleep(5.0)
            if alarmOn:
                alexa.startDialog("/home/pi/Voices/time.wav")
                time.sleep(5.0)
            if alarmOn:
                alexa.startDialog("/home/pi/Voices/weather.wav")
                time.sleep(5.0)
            if alarmOn:
                client.publish(ID_MUSIC, "ON")
                time.sleep(5.0)
            if not alarmOn:
                client.publish(ID_MUSIC, "OFF")
            client.publish(ID_LIGHT_STRIP, "ON")
            alarmOn = False
        while str(alarmTime) == str(digitString):
            time.sleep(10.0)
            digitString = time.strftime("%H:%M")
            oled.setImageMode('text')
            frame = 6

    if frame >= 40:
        value = vcnl.getAmbientOnDemand()
        vcnl.calibrate()
        client.publish(600510, value)
        frame = 1

Credits

Dave Wilson

Dave Wilson

2 projects • 3 followers
Software guy that is learning to design digital home automation and wearable products using open source hardware and software.
Thanks to Adafruit, Sam Machin, and Many others who contribute Python and Arduino examples.

Comments