Author: Sam Storino

Hi, I'm Sam. I'm a programmer and a DIYer. When I'm not finding things to build I enjoy cooking, hiking, camping and traveling the world with my best friend. Say Hello!
Assemble the Raspberry Pi Cat Laser Pointer Toy

Assemble the Raspberry Pi Cat Laser Pointer Toy

In part one we discussed what exactly we’re trying to build, and what’s needed to begin assembling our cat laser pointer toy. Here in part two we’re going to take all the pieces we’ve gathered and assemble them into one beautiful creation.

The goal of this part?

  • Assemble the pan/tilt mount and servo motors.
  • Solder the laser diode to the NPN transistor, and attach it to the shark(!!!).
  • Solder the physical push button.
  • Connect all the pieces together

If words like solder and transistor are a bit intimidating, don’t let them be! It’s really not that difficult, promise.

Later on in part three we’ll cover how to write code to fire the laser in a pseudo-random fashion at the touch of a button. As a bonus, part four will take this one more over-engineered step and demonstrate how to trigger the laser remotely as well as on a schedule, should you be so inclined.

Assemble the Pan Tilt Mount and Servo Motors

The pan/tilt mount provides a convenient way for us to use our servo motors as a means to navigate the cartesian plane. We will be using one of the servos as the x-axis, and the other as the y-axis.

Our servo motors can rotate from 0 to 180 degrees, no more no less. In order to provide a full range of motion for our laser contraption after we assemble all the pieces, we should first ensure the starting point of each servo is where we want it.

The servo traveling along the x axis will be responsible for moving the laser left and right. In order to accomplish this we should have a center position once mounted of 90 degrees, which allows it to turn left to 0 degrees, and right to 180 degrees.

The servo traveling along the y axis will handle moving the laser up and down. For this, we’ll want the starting position to be 0 degrees, which will aim the laser straight out once mounted, while 90 degrees will aim straight down and 180 degrees will, well, aim the laser backwards. We won’t have much use for anything over 90 degrees on the y axis, but it’s good to note.

Calibrating the X and Y Servos

The first step to calibrating our servos is to designate one as the x axis and the other as the y axis. I wrapped a piece of electrical tape around the x axis wires for quick identification, which helped avoid confusing the two later on.

Now that we’ve identified the servos, it’s time to write some code to set them in the correct starting positions before we actually attach them to the pan/tilt mount. If you’re using the same servos as me, they have powerground and control wires.

Connect three of your male-to-female jumper cables to your servo pins. Now, using the GPIO diagram below as reference, connect the female connectors to the Pi’s GPIO headers as follows:

GPIO Pinout Diagram

X Servo Y Servo
  • power connects to header pin 2 (5v power)
  • ground connects to header pin 6 (Ground)
  • control connects to header pin 7 (GPIO4)
  • power connects to header pin 4 (5v power)
  • ground connects to header pin 9 (Ground)
  • control connects to header pin 11 (GPIO17)

The servos are now connected and ready to be calibrated. Nice work!

Writing Code to Calibrate the Servos

Before getting started, let’s update our Pi to the latest packages and install the GPIO library. Run the below commands in the terminal:

Note: if this is the first time you’re running these commands it may take a bit to complete, so hang tight.

sudo apt-get update 
sudo apt-get upgrade 
sudo apt-get install rpi.gpio

We’re all set, it’s time to create the calibration script. Create a new file called Calibrate.py with the following code in it:

#!/usr/bin/env python

import RPi.GPIO as GPIO
import time

GPIO_X_SERVO = 4
GPIO_Y_SERVO = 17

if __name__ == '__main__':
    GPIO.setmode(GPIO.BCM)
    
    GPIO.setup(GPIO_X_SERVO, GPIO.OUT)
    GPIO.setup(GPIO_Y_SERVO, GPIO.OUT)

    print "calibrating..."
    try:
        x_servo = GPIO.PWM(GPIO_X_SERVO, 50)
        y_servo = GPIO.PWM(GPIO_Y_SERVO, 50)
        
        x_servo.start(7.5)  # X Servo: 7.5 is 90 degrees
        y_servo.start(2.5)  # Y Servo: 2.5 is 0 degrees

        time.sleep(1) # give the servos a chance to move
        
    finally:
        GPIO.cleanup()

The above script is is pretty minimal: each time it runs the x servo will be set to its center at 90 degrees, while the y servo gets set to its starting point of 0 degrees.

You need only run this once.

In the terminal, navigate to the directory that has this newly created python script in it. Once there, type chmod +x Calibrate.py and press enter to make the script an executable, then type ./Calibrate.py. If there were no issues in your script, you should have heard the motors spin to the desired positions.

Excellent! We now have two servos pointing in the correct starting positions. You can unplug the servo wires from the GPIO pins, it’s time to attach them to the pan/tilt mount.

Attach the Servos to the Pan Tilt Mount

Attaching the servos to the pan/tilt mount went fairly smooth with the exception of one rather important part: the two white plastic servo attachments, despite coming with the pan/tilt mount, didn’t fit perfectly in their designated spots. You may not have this issue, but I ended up using scissors and a box cutter to pare them back a bit where necessary, which is apparent in the below image:

Pan Tilt and Servo Motor Pieces Ready to Assemble

When piecing this together it’s important to keep in mind that the servos are calibrated right now to their starting positions, but we haven’t attached the white plastic arms yet. When it comes time to attach them, the servo should be positioned in a way that when the plastic arm is attached, it’s aligned as follows, without the need to manually rotate the motor:

I demonstrate this a bit further a couple images down. As for the assembly itself, just in case you’re having trouble piecing it together here’s a few screenshots of the progress as I went.

X and Y Servos Attached
X (bottom) and Y (top) servo’s connected to their respective pieces
Y Servo mounted and X Servo in Place
Final wall set in place, and the Y servo’s plastic arm attached and screwed in.

The previous image showcases a couple of important points:

  • I consider the front of this pan/tilt mount to be the side where the wires leave the servo motors. This allows sufficient movement when the mount is on a flat surface. Alternatively, the back in this case (opposite side) could be used when your intention is to have the mount on its side (e.g. on a wall, or fridge).
  • The top of the mount where the y axis servo is attached was positioned as flat as can be before screwing in the servo arm. If you recall, we want the y axis to have a starting position of 0 degrees. If you can imagine a laser sitting on top of the mount in this position, aiming the direction of where the wires are coming out, it would be shooting straight. Tilting the mount to 90 degrees would cause the laser to aim straight down, and so on. 
Pan Tilt Mount Assembled
X servo’s plastic arm connected to the base plate, and jumper cables set to extend the servo wires.

I decided to use similarly colored jumper cables as the built-in servo wires to help reduce the amount of confusion while connecting them later. Red is power, brown is ground and orange is control.

Now that we have motors calibrated and attached to the mount, it’s time to setup the laser.

Prepare the Laser Diode

The laser diode has two wires: power and ground. It does not have a third wire for control. The GPIO pins on your Raspberry Pi output 3.3v when turned on but don’t provide enough current to make the laser glow to a brightness worth chasing. The 3V3 regulated pin (e.g. pin 1 on the Pi) provides the necessary current, but cannot be logically toggled on and off by our code. On one hand we have control but insufficient current, and on the other hand we have sufficient current without control. How do we get around this?

Enter the NPN transistor. We’ll leverage this little device as a way to introduce control into the equation. The NPN transistor has three connections: the base, which is what our control wire will connect to, the collector and the emitter. The transistor is used as an electronic switch, which logically connects the collector with the emitter once electricity hits the base.

With that said, we’re going to attach the laser diode to the shark (please tell me you bought the shark!), then we’ll solder the laser and some additional wiring to the NPN transistor.

Using a hot glue gun (or some other form of adhesive), attach the laser diode to the sharks head, like so:

Once the glue dries, take three (3) wires that have at least one female connector and snip off the opposite end of the female connector. Then, strip about an inch of this newly snipped end from its casing to expose the wiring inside. You should be left with three wires having one female end and one stripped end. These three wires will be used to connect the laser/ transistor to the Raspberry Pi’s GPIO pins.

Take one more wire, snip off both ends and strip about an inch of casing from either side. This wire will be used as an extension between the laser diode and the transistor to provide some needed slack.

The screenshot below demonstrates the three female wires and one extension wire soldered to the transistor as follows:

  • The green wire is soldered to the transistor’s emitter, and will eventually connect to the Pi’s ground GPIO.
  • The purple wire is soldered directly to the laser diode’s power wire, and will provide power to the laser. 
  • The blue wire (not the one connected to the laser diode) is connected to the transistors base (middle connection), and will be used as our control.
  •  The laser diode’s ground wire is connected to an extension wire (yellow below), which is then connected to the transistor. Again, this is to provide additional slack so the wire can reach the GPIO pins once mounted.

I wrapped the soldered parts with electrical tape to give them a bit of protection.

Now that everything is soldered together, let’s give it a test drive! Using the above image, wire coloring and previously shown GPIO diagram as reference, connect the laser to the Raspberry Pi as follows:

  • Purple connects pin 1 (3V3 Power)
  • Blue connects to pin 13 (GPIO27)
  • Green connects to pin 25 (Ground)

With the connections in place, create a new file called LaserTestDrive.py and paste the following code snippet inside:

#!/usr/bin/env python

import RPi.GPIO as GPIO
import time

GPIO_LASER = 27

if __name__ == '__main__':
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(GPIO_LASER, GPIO.OUT)

    print 'blinking laser...'
    try:  
        # Test turning the laser on and off
        time.sleep(1)
        GPIO.output(GPIO_LASER, 1)
        time.sleep(1)
        GPIO.output(GPIO_LASER, 0)
        time.sleep(1)
        GPIO.output(GPIO_LASER, 1)
        time.sleep(1)
        GPIO.output(GPIO_LASER, 0)

    finally:
        GPIO.output(GPIO_LASER, 0)
        GPIO.cleanup()

In the terminal, navigate to the folder that contains the above script and type chmod +x LaserTestDrive.py to make it an executable. Now, type ./LaserTestDrive.py to kick off the script. If done correctly, you should see the laser flash multiple times. Boom baby!

We now have a shark with a freakin’ laser beam attached to it’s head. Dr. Evil would be proud. It’s time to set ourselves up with a physical push button so, later on, we can actually engage the cat laser pointer toy at the press of this button.

Setup a Physical Push Button

The physical push button I used came as part of a starter kit, but you can purchase just the buttons if the kit isn’t your thing. Take two (2) female-to-any wires from your stash, snip off the non-female end, then strip about an inch of casing to expose the wires. If you’re using the same button as me, flip it over and you’ll see two parallel plastic lines running the length of the button, inline with the metal connectors. Twist and solder your wires to the buttons metal connectors, one wire per plastic line. Here’s how mine came out:

With the soldering in place, use the previously mentioned GPIO diagram as a reference and connect the wires as follows:

  • Black connects to pin 39 (Ground)
  • White connects to pin 37 (GPIO26)

Now that everything’s wired in, let’s test our little button out. Create a new file called Button.py with the following code in it:

#!/usr/bin/env python

import RPi.GPIO as GPIO
import time

GPIO_BUTTON = 26
    
if __name__ == '__main__':
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(GPIO_BUTTON, GPIO.IN, pull_up_down=GPIO.PUD_UP)

    print 'Press the button 3 times to exit...'

    press_count = 0
    while press_count < 3:
        # Test pressing of the button!
        button_pressed = GPIO.input(GPIO_BUTTON) == False
        if(button_pressed):
            print 'Button Pressed!'
            press_count += 1

            # pause to avoid the button press being picked up multiple times
            time.sleep(0.5)
    
    GPIO.cleanup()

In the terminal, navigate to the directory where this new script lives and type chmod +x Button.py to make it executable. Afterwards, run the script by typing ./Button.py. Once running, pressing the button should print Button Pressed! in the terminal. Press the button three total times to have the script exit.

Fantastic work! You’ve accomplished a lot so far. Now it’s time to put all those disparate pieces together.

Connecting All the Pieces

At this point we prepared all the individual pieces and ran some tests to ensure they’re functioning as we expect. Now it’s time to put it all together. If you’re using the same Pi case as me, open it up and place the Pi inside, but leave the top piece detached. The top of the case has a rectangular opening which exposes the GPIO section of the Pi, we’ll use this to fish some wires through. With the Pi in place, let’s get to assembling:

We’ll start off by wiring up the pan/tilt mount. Fish the servo wires through the rectangular opening in the top of the Pi case, then connect them to the GPIO’s as follows:

X Servo Y Servo
  • power connects to header pin 2 (5v power)
  • ground connects to header pin 6 (Ground)
  • control connects to header pin 7 (GPIO4)
  • power connects to header pin 4 (5v power)
  • ground connects to header pin 9 (Ground)
  • control connects to header pin 11 (GPIO17)

 

Servos Connected to Pi

Next up is attaching the shark and laser to the pan/tilt mount. I used velcro which allowed for a less permanent solution should I decide I want to turn the shark around. As was mentioned before, the servos have a finite range of motion. Right now we’re treating the front of our mount as the side where the wires exit the servos. Deciding to treat that as the back of our mount would allow for an entirely different range of motion.

I used a hacksaw to trim away some of the plastic on top of the pan/tilt mount prior to mounting the shark. This allowed for less interference and better positioning, but is not strictly necessary.

Shark Laser Mounted

Once the shark is mounted, fish the wires and NPN transistor through that same rectangular opening in the Pi case, then connect them as follows:

  • Purple connects pin 1 (3V3 Power)
  • Blue connects to pin 13 (GPIO27)
  • Green connects to pin 25 (Ground)

The mount and shark laser are primed and ready, time for the push button. I fished these wires through a different hole on the case to avoid overcrowding the GPIO opening. Fish them through wherever works, then connect as follows:

  • Black connects to pin 39 (Ground)
  • White connects to pin 37 (GPIO26)

Push Button Attached

I didn’t use any adhesive or other trickery to keep the button in place. The wiring and positioning of the button actually made it naturally stationary, enough for my needs anyway.

Finally, it’s time to attach the pan/tilt mount to the case itself. For this I went the overly complicated route of drilling out holes in the case to screw the base of the mount into. A much simpler and less permanent solution would be to use velcro. Velcro is plenty strong enough to support the weight of our little contraption.

Cat Toy Laser Assembly Complete

Congratulations, you now have a super evil cat laser pointer toy assembled and ready for action. Now we just need to give it life in the form of python code.

Conclusion and Next Steps

Part one set the goals and outline. Now with part two wrapped up we have a fully assembled cat toy laser. I’m heading to part three where we’ll write the python code to make all these parts function together at the push of a button.

Later in Part Four I’ll be covering how to trigger our cat laser pointer toy remotely as well as on a schedule. First, let’s figure out how to trigger it by pushing that button we just installed.

 

Trigger the Automatic Cat Laser Pointer Toy Remotely

Trigger the Automatic Cat Laser Pointer Toy Remotely

You made it here to part four because pushing that button isn’t enough. No, you want to trigger this cat laser pointer toy remotely, or even on a schedule. Well you’re in luck, because I too wanted more ways to do the same thing.

Part one got us on our way, part two was all about building the laser contraption, and part three brought it life. That means you have a fully functional cat laser pointer toy capable of being triggered at the push of a button.

What are we going to be doing here exactly?

  • Set up your Gmail account with 2-step authentication for a secure way to interact with it through code.
  • Introduce a modified version of the GmailWrapper.py script from my automatic cat feeder series to scan for an email with a given subject and, when found, trigger the cat laser pointer toy.
  • Use IFTTT to provide support for remote triggering and scheduling.

Enough said. It’s go time.

Preparing Your Gmail Account with 2-step Authentication

We’re going to be using email as the trigger to engage the cat laser pointer toy. Before that’s possible we’ll need a way to securely log into our Gmail account from code. You could store your credentials in plain text within the code we write, but I strongly discourage that. Instead, make use of Gmail’s two-step authentication and app passwords

I created a new Gmail account for this purpose.

  1. Log into your Gmail account
  2. Navigate to the Sign-in and security page
  3. Under the Signing in to Google section, click the 2-Step Verification menu, then follow the instructions to enable 2-Step Verification
  4. Back in the Sign-in and security page right under the 2-Step Verification button you’ll see App passwords
  5. Generate a password
    1. Note: this password will be 16 digits with spaces separating every 4 digits (e.g. xxxx xxxx xxxx xxxx). Please be sure to include the spaces!
  6. You can only view a generated password once, so copy it to the side for now.

The password you generated can be used to log into your Gmail account. We don’t need it right this second, but we’ll be using it to scan for an email that demands we initiate the laser sequence!

Writing Code to Read A Gmail Account

Now that we have a Gmail account ready to rock, let’s write some code to interrogate it. We’re going to be using the IMAPClient python library for this purpose, and we’ll wrap the calls to this client in our own Python class.

Install IMAPClient now from the terminal: sudo pip install imapclient

Create the GmailWrapper.py Script

Now let’s create our Gmail wrapper class: create a new file called GmailWrapper.py with the following code:

#!/usr/bin/env python
 
from imapclient import IMAPClient, SEEN
 
SEEN_FLAG = 'SEEN'
UNSEEN_FLAG = 'UNSEEN'
 
class GmailWrapper:
    def __init__(self, host, userName, password):
        #   force the user to pass along username and password to log in as 
        self.host = host
        self.userName = userName
        self.password = password
        
        self.login()
 
    def login(self):
        print('Logging in as ' + self.userName)
        server = IMAPClient(self.host, use_uid=True, ssl=True)
        server.login(self.userName, self.password)
        self.server = server
 
    #   The IMAPClient search returns a list of Id's that match the given criteria.
    #   An Id in this case identifies a specific email
    def getIdsBySubject(self, subject, unreadOnly=True, folder='INBOX'):
        #   search within the specified folder, e.g. Inbox
        self.setFolder(folder)  
 
        #   build the search criteria (e.g. unread emails with the given subject)
        self.searchCriteria = [UNSEEN_FLAG, 'SUBJECT', subject]
 
        if(unreadOnly == False):
            #   force the search to include "read" emails too
            self.searchCriteria.append(SEEN_FLAG)
 
        #   conduct the search and return the resulting Ids
        return self.server.search(self.searchCriteria)

    def getIdsByGmailSearch(self, search, folder='INBOX'):
        # powerful search enabled by Gmail. Examples: `in:unread, subject: <subject>`
        self.setFolder(folder)
        return self.server.gmail_search(search)
    
    def getFirstSubject(self, mailIds, folder='INBOX'):
        self.setFolder(folder)
        
        data = self.server.fetch(mailIds, ['ENVELOPE'])
        for msgId, data in data.items():
            envelope = data[b'ENVELOPE']
            return envelope.subject
            
        return None
 
    def markAsRead(self, mailIds, folder='INBOX'):
        self.setFolder(folder)
        self.server.set_flags(mailIds, [SEEN])
 
    def setFolder(self, folder):
        self.server.select_folder(folder)

Verifying the GmailWrapper.py Class Works

Let’s do a test: our script is going to log into the Gmail account, search for email with a specific subject, retrieve that subject, then mark the email as read. Before running the code, send yourself an email with the subject begin laser ignition (the search is case-insensitive, by the way).

We’re going to use the Python interpreter to run our tests. In your terminal make sure you’re in the same directory as the GmailWrapper.py script we just created, then:

# press enter after each line for the interpreter to engage
 
# invoke the interpreter
python
 
# import our wrapper class for reference
from GmailWrapper import GmailWrapper
 
# create an instance of the class, which will also log us in
# the <password> should be the 2-step auth App Password, or your regular password
gmailWrapper = GmailWrapper('imap.gmail.com', '<your gmail username>', '<password>')
 
# search for any unread emails with the subject 'begin laser ignition', and return their Ids
ids = gmailWrapper.getIdsByGmailSearch('begin laser ignition')
 
# have the interpreter print the ids variable so you know you've got something
ids

# grab the full subject of the first id returned
subject = gmailWrapper.getFirstSubject(ids)

# have the interpreter print the subject variable to see what you've got
subject
 
# we successfully found and read our email subject, now lets mark the email as read
gmailWrapper.markAsRead(ids)
 
# exit the interpreter
quit()

If everything went as planned your email should now be marked as read. Pretty neat! 

LaserWrapper, Meet GmailWrapper: Putting the Two Together

As we know by now, the LaserWrapper.py script is constantly watching the physical button and waiting for it to be pressed. We’re going to modify it a bit so it also watches the Gmail account and waits for an email with the correct subject to come through. In the event it finds this email we want it to trigger the cat laser pointer toy.

Alright, time for some modifications. Open your LaserWrapper.py script and replace its contents with the following:

#!/usr/bin/env python
 
from Laser import Laser
from GmailWatcher import GmailWrapper
import json
import datetime
import RPi.GPIO as GPIO
import time
from imaplib import IMAP4

HOSTNAME = 'imap.gmail.com'
USERNAME = 'your username'
PASSWORD = 'your password'
 
# seconds to wait before searching Gmail
CHECK_DELAY = 30
 
# seconds to wait before logging into gmail. if we don't wait, we run the risk of trying to 
# log in before the Pi had a chance to connect to wifi.
GMAIL_CONNECT_DELAY = 20

# minutes to wait before reconnecting our Gmail instance.
GMAIL_RECONNECT_DELAY = 60
GPIO_BUTTON = 26
 
FIRE_LASER_SUBJECT = 'begin laser ignition'
DISMANTLE_LASER_SUBJECT = 'dismantle laser'
 
engage = False
gmailWrapper = None
laser = Laser()
 
start_time = datetime.datetime.now()
run_time = 0
last_gmail_check_time = datetime.datetime.now()
last_gmail_connect_attempt = datetime.datetime.now()
last_reconnect_attempt = datetime.datetime.now()
 
default_configuration = '{"run_time": 30, "min_movement": 12, "x_min": 0, "x_max": 90, "y_min": 0, "y_max": 22}'
 
def initiateLaserSequence():
    global gmailWrapper
    global engage
    
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(GPIO_BUTTON, GPIO.IN, pull_up_down=GPIO.PUD_UP)
 
    stop = False
    run = True
    
    # wire up a button press event handler to avoid missing a button click while the loop below
    # is busy processing something else.
    GPIO.add_event_detect(GPIO_BUTTON, GPIO.FALLING, callback=__button_handler, bouncetime=200)
    
    last_gmail_check = datetime.datetime.now().time()
    print 'running, press CTRL+C to exit...'
    try:
        while run:
            try:
                __check_gmail_connection()
                
                # avoid pinging gmail too frequently so they don't lock us out.
                if(__should_check_gmail(CHECK_DELAY)):
                    print 'Checking Gmail!'
                    stop = __should_stop_firing()
                    
                    ids = __get_email_ids_with_subject(FIRE_LASER_SUBJECT)
                    if(len(ids) > 0):
                        print 'Email found, initiating laser sequence!'
                        engage = True
            
                        laser.stop()
            
                        # grab any config options from the email and begin
                        __calibrate_laser(__get_configuration(ids))
                        gmailWrapper.markAsRead(ids)
                
                if(stop):
                    engage = False
                    stop = False
                    laser.stop()
                
                if(engage):
                    if(__run_time_elapsed()):
                        stop = True
                    else:
                        laser.fire()
                else:
                    # sleep here to lessen the CPU impact of our infinite loop
                    # while we're not busy shooting the laser. Without this, the CPU would
                    # be eaten up and potentially lock the Pi.
                    time.sleep(1)
                
            except IMAP4.abort, e:
                # Gmail will expire your session after a while, so when that happens we
                # need to reconect. Setting None here will trigger reconnect on the
                # next loop.
                gmailWrapper = None
                print 'IMAP4.abort exception: {0}'.format(str(e))
            except Exception, e:
                # swallowing exceptions isn't cool, but here we provide an opportunity to
                # print the exception to an output log, should crontab be configured this way
                # for debugging.
                print 'Unhandled exception: {0}'.format(str(e))
            except KeyboardInterrupt:
                run = False
                print 'KeyboardInterrupt: user quit the script.'
                break
    finally:
        print 'Exiting program'
        laser.stop()
        GPIO.cleanup()
        
def __button_handler(channel):
    global engage
    
    print 'Button pressed! '.format(str(channel))
    
    if(engage):
        print 'Already firing the laser, button press ignored'
    else:
        print 'Initiating Firing Sequence!'
        # only start a new firing sequence if we're not already in the middle of one.
        engage = True
        __calibrate_laser(None)
 
def __check_gmail_connection():
    if(gmailWrapper is None):
        __connect_gmail()
    
def __connect_gmail():
    global gmailWrapper
    global last_gmail_connect_attempt
    
    now = datetime.datetime.now()
    next_connect_time = (last_gmail_connect_attempt + datetime.timedelta(seconds=GMAIL_CONNECT_DELAY)).time()
    if(now.time() > next_connect_time):
        print '__connect_gmail: Attempting to login to Gmail'
        try:
            last_gmail_connect_attempt = now
            gmailWrapper = GmailWrapper(HOSTNAME, USERNAME, PASSWORD)
        except Exception, e:
            print '__connect_gmail: Gmail failed during login, will retry automatically.'.format(str(e))

def __should_check_gmail(delay):
    global last_gmail_check_time
    
    if(gmailWrapper is None):
        # we haven't yet successfully connected to Gmail, so exit
        return
    
    now = datetime.datetime.now()
    next_check_time = (last_gmail_check_time + datetime.timedelta(seconds=delay)).time()
    
    if(now.time() > next_check_time):
        last_gmail_check_time = now
        return True
    
    return False
 
def __run_time_elapsed():
    now = datetime.datetime.now()
    end_time = (start_time + datetime.timedelta(seconds=run_time)).time()
    
    if(now.time() > end_time):
        return True
    
    return False
 
def __calibrate_laser(configuration):
    global start_time
    global run_time
    
    if(configuration is None):
        # no user defined config, so we'll go with the defaults
        configuration = json.loads(default_configuration)
    
    print "starting laser with config: {0}".format(configuration)
    
    start_time = datetime.datetime.now()
    
    run_time = configuration.get('run_time')
    min_movement = configuration.get('min_movement')
    x_max = configuration.get('x_max')
    x_min = configuration.get('x_min')
    y_max = configuration.get('y_max')
    y_min = configuration.get('y_min')
 
    laser.calibrate_laser(min_movement, x_min, x_max, y_min, y_max)
        
def __get_email_ids_with_subject(subject):
    return gmailWrapper.getIdsByGmailSearch('in:unread subject:{0}'.format(subject))
    
def __get_configuration(emailIds):
    subject = gmailWrapper.getFirstSubject(emailIds)
    config_start_index = subject.find('{')
    
    # no config found in subject, so return nothing
    if(config_start_index == -1):
        return None
    
    # grab the substring from opening { to the end
    return json.loads(subject[config_start_index:None])
 
def __should_stop_firing():
    ids = __get_email_ids_with_subject(DISMANTLE_LASER_SUBJECT)
    return len(ids) > 0

if __name__ == '__main__':
    initiateLaserSequence()

The above script is almost ready for use, we just need to make a few tweaks:

  • Replace lines 12 and 13 with your Gmail username (that’s your email without @gmail), and the app password you generated.
  • Replace lines 26 and 27 with the email subjects that you want to use to start or stop the laser sequence. That’s right, the ability to remotely stop the laser is baked in as well, just in case it’s running a bit longer than desired.

Once these changes are in place, give it a test drive. Run the script and press the physical button to ensure we didn’t break anything there. If all looks good, send yourself an email with the subject you chose (I used begin laser ignition). After about 30 seconds the laser should have began firing.

Subject Matters: Override the Default Configuration

At this point we’re able to trigger the cat laser pointer toy remotely by sending an email. This is pretty powerful stuff. If you recall from the previous posts, we built in the ability to configure the cat laser pointer toy in the form of a JSON string. What if we want to set the run time, or the minimum amount of movement, remotely?

You’re in luck! The script we just wrote above is smart enough to do that. If you send an email with just the trigger key in the subject (e.g. begin laser ignition), then it will use the default_configuration variable on line 39 of the script. However, you can override that by adding your own configuration into the subject itself.

Here’s a sample email subject to showcase what I mean:

begin laser ignition {"run_time": 30, "min_movement": 12, "x_min": 0, "x_max": 90, "y_min": 0, "y_max": 22}

The script we wrote will parse this more complex subject and use the configuration settings you provide here over the default ones.

Scheduling an Email to be Sent Regularly

We have the code ready to rock. Sending an email on demand will cause the cat laser pointer toy to start firing, but what about firing it on a schedule? That’s where we’ll make use of IFTTT. IFTTT stands for if this then that and allows you to connect two disparate systems based on what they call a “recipe” (trigger and action). For our purpose, we need the clock to trigger an email to be sent (action).

Here’s what to do:

  1. Setup an account if you haven’t already (free, free, free).
  2. Use IFTTT website or download the app to your phone and log in.
  3. In the My Applets section, add a new applet.
  4. You’ll see a screen saying if +this then that: click the +this button, then select the Date & Time service.
  5. Select the Every day at trigger, and select the time you’d like the cat laser pointer toy to activate, then hit next
  6. Now you’ll see +that, click it and find the Gmail service. You’ll need to connect the service to your Gmail account. Once finished, set the action to Send yourself an email with the subject  Begin Laser Ignition.
    1. Don’t forget, you have the option of adding configuration to the subject, too.
  7. Hit next, then Finish

There you have it, every day at the time you specified an email will be sent to your account. If you had any issues setting up the IFTTT recipe, check out this post for a really nice and in-depth walk-through.

Having fun? Here’s Other Ways to Trigger

Alexa

Alexa (and the Echo Dot) integrates nicely with IFTTT. In the IFTTT app, create a new recipe with the trigger (+this) connecting to Alexa. You’ll need to connect the service like you did for Gmail. Once connected, select the option to Say a specific phrase and enter a phrase like cat laser. Once the Alexa side is setup, set the action (+that) to send an email, like we did in the previous section.

Hands free laser initiation at the ready, just say: Alexa, trigger the cat laser.

The DO Button App

Created by the IFTTT team, the DO Button app and accompanying widget provides a straightforward way to trigger the action. You! You’re the trigger. You setup a recipe, same as before, except you’ll notice there’s no +this. You are +this. You open the app and click the button, it then triggers an email which triggers the cat laser. This app can also be configured to show on your iPhone or Androids home screen, so triggering the laser is even easier.

Conclusion

I hope you enjoyed this project as much as I did. It was a blast seeing a shark with a laser attached to its head causing mayhem throughout my apartment. I think the cats enjoy it too, almost as much as I do.

As always, a little shoutout to the previous sections in case you need to get there:

  • Part one where we found out what we were building and what it would take.
  • Part two which helped guide us to creating a machine of chaos.
  • Part three where our inner mad scientist gave life to this machine in the form of code.
  • And of course, the bonus part, part four where we couldn’t live without a remote way to control our cat laser pointer toy.

I encourage you to leave feedback in the comments below. If you’re stuck, reach out! And of course, thank you for reading.

Write Code to Control the Raspberry Pi Cat Laser Pointer Toy

Write Code to Control the Raspberry Pi Cat Laser Pointer Toy

At this point you’ve covered part one and part two (nice job!), or you didn’t and just happen to stumble upon part three of this guide (it’s good to have you). Here in part three we’ll be focusing primarily on writing the necessary python code to make our super evil laser contraption fully functional at the push of a button. This could technically be your last part of the series (it’s sad, but it had to end sometime). If you’re hankering for more like I was, part four will cover the bonus feature of triggering the cat laser pointer toy remotely and on a schedule.

With that said, let’s dig in!

Create the Scripts to Automate the Cat Laser Pointer Toy

We’re going to create two scripts:

  • Laser.py, which will contain the code responsible for configuring our laser and servo motors, and moving the laser from one position to another.
  • LaserWrapper.py, which as the name suggests will wrap the Laser.py functionality, and will primarily be responsible for deciding when the cat laser pointer toy should be enabled as well as which settings it should use during runtime.

Create a new file called Laser.py with the following code:

#!/usr/bin/env python

import time
import RPi.GPIO as GPIO
import random

DEFAULT_RUN_TIME = 90
DEFAULT_MIN_MOVEMENT = 10
DEFAULT_X_MIN_POSITION = 40
DEFAULT_X_MAX_POSITION = 120
DEFAULT_Y_MIN_POSITION = 20
DEFAULT_Y_MAX_POSITION = 60

# define which GPIO pins to use for the servos and laser
GPIO_X_SERVO = 4
GPIO_Y_SERVO = 17
GPIO_LASER = 27

class Laser:
    def __init__(self):
        GPIO.setmode(GPIO.BCM)
        
        self.x_servo = None
        self.y_servo = None

        GPIO.setup(GPIO_X_SERVO, GPIO.OUT)
        GPIO.setup(GPIO_Y_SERVO, GPIO.OUT)
        GPIO.setup(GPIO_LASER, GPIO.OUT)

    def calibrate_laser(self, min_movement, x_min, x_max, y_min, y_max):
        # set config variables, using the defaults if one wasn't provided
        self.min_movement = DEFAULT_MIN_MOVEMENT if min_movement is None else min_movement
        self.x_min = DEFAULT_X_MIN_POSITION if x_min is None else x_min
        self.x_max = DEFAULT_X_MAX_POSITION if x_max is None else x_max
        self.y_min = DEFAULT_Y_MIN_POSITION if y_min is None else y_min
        self.y_max = DEFAULT_Y_MAX_POSITION if y_max is None else y_max
        
        # start at the center of our square/ rectangle.
        self.x_position = x_min + (x_max - x_min) / 2
        self.y_position = y_min + (y_max - y_min) / 2
        
        # turn on the laser and configure the servos
        GPIO.output(GPIO_LASER, 1)
        self.x_servo = GPIO.PWM(GPIO_X_SERVO, 50)
        self.y_servo = GPIO.PWM(GPIO_Y_SERVO, 50)
        
        # start the servo which initializes it, and positions them center on the cartesian plane
        self.x_servo.start(self.__get_position(self.x_position))
        self.y_servo.start(self.__get_position(self.y_position))

        # give the servo a chance to position itself
        time.sleep(1)

    def fire(self):
        self.movement_time = self.__get_movement_time()
        print "Movement time: {0}".format(self.movement_time)
        print "Current position: X: {0}, Y: {1}".format(self.x_position, self.y_position)

        # how many steps (how long) should we take to get from old to new position
        self.x_incrementer = self.__get_position_incrementer(self.x_position, self.x_min, self.x_max)
        self.y_incrementer = self.__get_position_incrementer(self.y_position, self.y_min, self.y_max)

        for index in range(self.movement_time):
            print "In For, X Position: {0}, Y Position: {1}".format(self.x_position, self.y_position)
            self.x_position += self.x_incrementer
            self.y_position += self.y_incrementer

            self.__set_servo_position(self.x_servo, self.x_position)
            self.__set_servo_position(self.y_servo, self.y_position)

            time.sleep(0.02)

        # leave the laser still so the cat has a chance to catch up
        time.sleep(self.__get_movement_delay())

    def stop(self):
        # always cleanup after ourselves
        print ("\nTidying up")
        if(self.x_servo is not None):
            self.x_servo.stop()
        
        if(self.y_servo is not None):
            self.y_servo.stop()
            
        GPIO.output(GPIO_LASER, 0)
        
    def __set_servo_position(self, servo, position):
        servo.ChangeDutyCycle(self.__get_position(position))

    def __get_position(self, angle):
        return (angle / 18.0) + 2.5

    def __get_position_incrementer(self, position, min, max):
        # randomly pick new position, leaving a buffer +- the min values for adjustment later
        newPosition = random.randint(min + self.min_movement, max - self.min_movement)
        print "New position: {0}".format(newPosition)

        # bump up the new position if we didn't move more than our minimum requirement
        if((newPosition > position) and (abs(newPosition - position) < self.min_movement)):
            newPosition += self.min_movement
        elif((newPosition < position) and (abs(newPosition - position) < self.min_movement)):
            newPosition -= self.min_movement

        # return the number of steps, or incrementer, we should take to get to the new position
        # this is a convenient way to slow the movement down, rather than seeing very rapid movements
        # from point A to point B
        return float((newPosition - position) / self.movement_time)

    def __get_movement_delay(self):
        return random.uniform(0, 1)

    def __get_movement_time(self):
        return random.randint(10, 40)

This code is pretty self contained. The only reason you’d need to adjust anything above is if you decided to use different GPIO pins than I described in the previous sections. If so, you’ll need to set them accordingly (lines 15 – 17).

One script down, that was easy! Create another file called LaserWrapper.py with the following code:

#!/usr/bin/env python

from Laser import Laser
import json
import datetime
import RPi.GPIO as GPIO
import time

GPIO_BUTTON = 26

laser = Laser()
start_time = datetime.datetime.now()
run_time = 0
engage = False

default_configuration = '{"run_time": 30, "min_movement": 12, "x_min": 0, "x_max": 90, "y_min": 0, "y_max": 22}'

def initiateLaserSequence():
    global engage
    # setup the push button GPIO pins
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(GPIO_BUTTON, GPIO.IN, pull_up_down=GPIO.PUD_UP)

    run = True
    
    # wire up a button press event handler to avoid missing a button click while the loop below
    # is busy processing something else.
    GPIO.add_event_detect(GPIO_BUTTON, GPIO.FALLING, callback=__button_handler, bouncetime=200)
    
    print "running, press CTRL+C to exit..."
    try:
        while run:
            try:
                if(engage):
                    if(__run_time_elapsed()):
                        # we ran out of time for this run, shutdown the laser
                        engage = False
                        laser.stop()
                    else:
                        laser.fire()
                else:
                    # sleep here to lessen the CPU impact of our infinite loop
                    # while we're not busy shooting the laser. Without this, the CPU would
                    # be eaten up and potentially lock the Pi.
                    time.sleep(1)
                        
            except Exception, e:
                # swallowing exceptions isn't cool, but here we provide an opportunity to
                # print the exception to an output log, should crontab be configured this way
                # for debugging.
                print 'Unhandled exception: {0}'.format(str(e))
            except KeyboardInterrupt:
                run = False
                print 'KeyboardInterrupt: user quit the script.'
                break
    finally:
        print 'Exiting program'
        laser.stop()
        GPIO.cleanup()

def __button_handler(channel):
    global engage
    
    print 'Button pressed! '.format(str(channel))
    
    if(engage):
        print 'Already firing the laser, button press ignored'
    else:
        print 'Initiating Firing Sequence!'
        # only start a new firing sequence if we're not already in the middle of one.
        engage = True
        __calibrate_laser(None)

def __run_time_elapsed():
    # figure out if the laser has ran its course, and should be stopped.
    now = datetime.datetime.now()
    end_time = (start_time + datetime.timedelta(seconds=run_time)).time()
    
    if(now.time() > end_time):
        return True
    
    return False

def __calibrate_laser(configuration):
    global start_time
    global run_time
    
    if(configuration is None):
        # no user defined config, so we'll go with the defaults
        configuration = json.loads(default_configuration)
    
    print "starting laser with config: {0}".format(configuration)
    
    start_time = datetime.datetime.now()
    
    run_time = configuration.get('run_time')
    min_movement = configuration.get('min_movement')
    x_max = configuration.get('x_max')
    x_min = configuration.get('x_min')
    y_max = configuration.get('y_max')
    y_min = configuration.get('y_min')
 
    laser.calibrate_laser(min_movement, x_min, x_max, y_min, y_max)

if __name__ == '__main__':
    initiateLaserSequence()

As mentioned previously, this script defines a few settings which tell the Laser.py script information about how it should move. This script is also responsible for triggering the movement, as it’s continuously looking for the press of that physical button we wired up. 

The default settings are defined as a JSON string on line 16, and are stored in the default_configuration variable. I chose to use JSON instead of individual variables to prepare the script for future changes introduced in part four, where we trigger the laser remotely. 

Here’s a bit of documentation around what exactly these configuration variables mean:

PropertyDescription
run_timeHow long, in seconds, the cat laser pointer toy should operate each time the button is pressed.
min_movementThe minimum amount of movement that needs to occur when the laser moves from point A to point B on both the x and y axis.
x_minThe minimum position, in degrees, the laser is allowed to move to along the x axis.
Must be greater than or equal to 0 degrees.
x_maxThe maximum position, in degrees, the laser is allowed to move to along the x axis.
Must be less than or equal to 180 degrees. 
y_minThe minimum position, in degrees, the laser is allowed to move to along the y axis
Must be greater than or equal to 0 degrees.
y_maxThe maximum position, in degrees, the laser is allowed to move to along the y axis.
Must be less than or equal to 180 degrees. 

 

To really drive home the point, let’s dissect the default_configuration variable in the above script:

default_configuration = '{"run_time": 30, "min_movement": 12, "x_min": 0, "x_max": 90, "y_min": 0, "y_max": 22}'

With these settings, the cat laser pointer toy will:

  • Run for 30 seconds when the physical button is pressed, stopping after that time has elapsed.
  • Move at least 12 degrees along both the x axis (left/ right) and y axis (up/ down).
  • Move within the bounds of 0 degrees (minimum) and 90 degrees (maximum) along the x axis. Remember, our servo’s can move between 0 and 180 degrees if we wanted. Here we can restrict that range to our needs.
  • Move within the bounds of 0 degrees (minimum) and 22 degrees (maximum) along the y axis.

The x and y min/ max variables are nothing more than a way for us to define a square or rectangular shape for the laser to move within. I adjusted these settings until they fit the space where I have the cat laser pointer toy set up. You’ll likely need to tailor them to your own play space.

Now that we have all the code in place we can finally test this puppy out, hell yeah!

Begin Laser Ignition!

Frau Farbissina said it best, it’s time we begin laser ignition. Open up the terminal and navigate to the directory where you created the Laser.py and LaserWrapper.py scripts, then type the following commands:

chmod +x Laser.py
chmod +x LaserWrapper.py

Our scripts are now executable. The LaserWrapper.py script is the one we want to kick off, as it’s responsible for configuring and running Laser.py internally. Run the script by typing ./LaserWrapper.py. If all went well you should see an error-free terminal, which means the script is running and awaiting your command.

Press the button already!

If everything’s wired up correctly and the code is in place, the laser should have started moving. Congratulations, you’ve built a fully functional world dominating cat laser pointer toy!

If nothing happened or you had errors while executing the script: take a step back and double-check all your GPIO connections from the previous section. Did you use the same GPIO’s as me? If you did, great, that’s probably not the problem, but if you didn’t then you’ll need to adjust the GPIO variables in the scripts to point to the ones you chose. If the pins are in place and still no luck, don’t be discouraged! Reach out in the comments or contact me directly and we can take a crack at it together.

Using Cron to Automatically Run our Script

Our cat laser pointer toy has a push button to trigger the movement of the laser, but that button is no good to us if our LaserWrapper.py script isn’t constantly running. We need this script to run on a regular basis, which means starting it when the Raspberry Pi boots up. For that, we can use cron.

Open a terminal and type crontab -e to open the cron job editor in nano. Add the following line to the end of this file:

@reboot sudo python /path/to/your/script/LaserWrapper.py

It may seem obvious, but make sure you replace /path/to/your/script above with the actual path to your script. Move your cursor to the end of the newly added line (after .../LaserWrapper.py) and press Enter to create a new line under it. A known quirk with cron is that it requires the command to be followed by a new line, else it won’t run our script.

Since we’re in nano, save and exit by pressing CTRL+X, then Y, then Enter. This script will now be executed anytime the Pi reboots. Give it a go: reboot your Pi and give it time to load up. Once loaded, try triggering the cat laser pointer toy without manually running the script first.  

Conclusion and Next Steps

You did it! We now have the awesome power of an automatic cat laser pointer toy. I hope you have a cat to use it on (I won’t judge if you don’t).

Part one got us started, while part two really made the project interesting once we had a fully assembled contraption. Here in part three we were able to bring life to our laser with a little help from our friend python. 

Not satisfied with stopping here? Too lazy to physically push the button on the laser? Me too. If you haven’t had enough yet I’ll be over in part four over-engineering this project with some remote capabilities.

I want to reiterate that if you’re stuck, that’s ok! Don’t be discouraged. This project caught me up a few times, it’s all part of the process. Just reach out!

If you won’t be joining me for part four, then I want to thank you for taking the time to read through this series; I hope you found it most excellent. If you have any feedback, good or bad, I implore you to reach out in the comments.

A Git Cheat Sheet For That Quick Reminder

A Git Cheat Sheet For That Quick Reminder

Switching from subversion to Git was a paradigm shift that took some getting use to, but looking back now, the switch was worth it. Rather than getting tied to any one Git UI, I wanted to get a grasp of plain old Git and the commands it offers.

With that, I’ve created this Git Cheat Sheet to help along the way.

Cloning a Repository

NotesCommands
clone an existing repositorygit clone "repository-path"
create a new local repositorygit init

 

Branch Management

NotesCommands
list local branchesgit branch
list local and remote branchesgit branch -av
create local branchgit branch "branch-name"
create and checkout local branchgit checkout -b "branch-name"
delete local branch that has had its changes pushed to a remote branch (cannot currently be checked out)git branch -d "branch-name"
delete local branch, ignoring whether or not its changes were pushed to a remote branchgit branch -D "branch-name"
delete local and remote branchgit push origin --delete "branch-name"
checkout branchgit checkout "branch-name"
rename currently checked out local branchgit branch -m "new-branch-name"

 

Handling Changes

NotesCommands
see changes made to local branchgit status
track and stage local changesgit add .
commit staged only with a commit messagegit commit -m "commit-message"
commit unstaged and staged with a commit messagegit commit -a -m "commit-message"
amend changes to previous commit (before pushing)git commit --amend
temporarily stash local changes (resets local branch but preserves changes)git stash
apply previously stashed changes to the current branchgit stash pop

 

Undoing (oops)

NotesCommands
discard all changes in working directorygit reset --hard
reset HEAD to a specific commit and discard local changesgit reset --hard
reset HEAD to a specific commit but preserve local changesgit reset "commit-hash"
discard all changes to specific filegit checkout HEAD
revert commit by applying previous commit changes to HEAD then committinggit revert

 

Tracking History

NotesCommands
show all commits for current HEADgit log
show all commits for current HEAD for given filegit log -p "file"
show all commits that HEAD has pointed togit reflog

 

Fetch, Pull & Push

NotesCommands
grab changes from remote, but don't merge into current HEADgit fetch
grab changes from remote, don't merge, and clean-up remote references (known as pruning, prune)git fetch -p
grab changes and merge into current HEADgit pull
create and track remote branch, push all local changes to itgit push -u "remote" "branch-name"
push all local changes to tracked remote branch for current HEADgit push

 

Merge & Rebase

NotesCommands
merge commits from branch into current HEADgit merge "source-branch-name"
merge commits from source branch into target branchgit merge "source-branch-name" "target-branch-name"
rebase current HEAD onto branch (e.g. make that branch the new base of your changes)git rebase "branch"

 

WordPress Error Thrown: Call to Undefined Function

WordPress Error Thrown: Call to Undefined Function

Suddenly I began receiving a “call to undefined function” error when accessing my website. The admin section worked without issue, and there were no changes to the website aside from the automatic updates, but the website decided to no longer function correctly.

The full error message was as follows:

Error thrown
Call to undefined function lighthouse_posted_on()

The fact that the function lighthouse_posted_on() was in the error message was a pretty great clue: for some reason my theme-specific function was not being found, which points fingers toward the issue being with my theme rather than the WordPress install.

Note: if you’re seeing a WordPress specific function in the error, it’s likely that your automatic install failed and you should try manually installing the latest version again.

Verifying Your Theme Parent Theme

I’m using the lighthouse theme which I customized through child theming. After realizing the issue had to be with my theme, I navigated to Appearance -> Theme in the WordPress admin and noticed a parent/child theme related error at the very bottom stating:

ERROR: The parent theme is missing. Please install the “Lighthouse” parent theme.

This was verified when I looked for the theme and couldn’t find it anywhere. Somehow, the Lighthouse parent theme was deleted!

WordPress conveniently provided a button to download and install the theme again. After doing so, I was able to re-activate my child theme, which immediately resolved the Call to Undefined Function error, making my website accessible once again.

Entity Framework Decimal Scale and Precision Convention

Entity Framework Decimal Scale and Precision Convention

Entity Framework very much relies on conventions in order to accomplish a lot of what it does. In many cases it can be a hands-off tool: so long as you follow its conventions you should be in the clear. What do you do when the convention just isn’t cutting it?

Entity Framework Rounding to Two Decimal Places

I recently created a table that had a column setup as DECIMAL(19, 4).  That’s a precision of 19 and a scale of 4. Using Entity Framework I created a record in the table where I attempted to utilize the allotted scale (for example, 20.0015). The save was successful, but to my surprise the persisted number was rounded to two decimal places. It turns out this is by design, as Entity framework defaults to saving two decimal places. 

Entity Framework Decimal Scale and Precision Convention

Fortunately there’s a straightforward approach to circumventing this behavior by defining a new convention for Entity Framework to follow when dealing with any given property. Let’s see what that looks like.

Create the Decimal Precision Attribute Class

A new attribute will be created that serves the purpose of defining a precision and scale. This attribute can later be used to decorate a property of an entity object, which we’ll configure EF to recognize.

Add a new class DecimalPrecisionAttribute with the following:

[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
public sealed class DecimalPrecisionAttribute : Attribute
{
    public DecimalPrecisionAttribute(byte precision, byte scale)
    {
        Precision = precision;
        Scale = scale;
    }

    public byte Precision { get; set; }
    public byte Scale { get; set; }
}

Create the Decimal Precision Attribute Convention Class

The attribute is created, it’s time to make a new decimal convention class which will later be wired into the overall conventions Entity Framework is aware of.

Add a new class DecimalPrecisionAttributeConvention with the following:

public class DecimalPrecisionAttributeConvention
    : PrimitivePropertyAttributeConfigurationConvention<DecimalPrecisionAttribute>
{
    public override void Apply(ConventionPrimitivePropertyConfiguration configuration, DecimalPrecisionAttribute attribute)
    {
        if (attribute.Precision < 1 || attribute.Precision > 38)
        {
            throw new InvalidOperationException("Precision must be between 1 and 38.");
        }

        if (attribute.Scale > attribute.Precision)
        {
            throw new InvalidOperationException("Scale must be between 0 and the Precision value.");
        }

        configuration.HasPrecision(attribute.Precision, attribute.Scale);
    }
}

Add Convention to the Model Builder

Now that we’ve created a new convention to handle our precision and scale needs, we need to add it to the model builder. 

In your DbContext class, override the OnModelCreating method with the following:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Add(new DecimalPrecisionAttributeConvention());
}

Decorate a Property with the Decimal Precision Attribute

We’ve created a property to define precision and scale and added a convention to Entity Framework to use this precision and scale when found. Now all we have to do is decorate a property on one of our entity objects. Here’s what that might look like:

public class PayInfo {
    [DecimalPrecision(19, 4)]
    public decimal Rate { get; set; }
}

There you have it: properties decorated with the DecimalPrecision attribute will now persist with the predefined precision and scale.

Setting Opacity of Android Toolbar Overlay Based on ViewPager Position

Setting Opacity of Android Toolbar Overlay Based on ViewPager Position

I’ve recently delved into the world of mobile development using Xamarin with Visual Studio. A tutorial on their website introduced me to the Android toolbar, and how it can be used as a more robust and flexible action bar. This was fairly straightforward to implement but I had a requirement that the toolbar overlay shouldn’t be visible on certain fragments of a ViewPager.

The Goal

What were trying to achieve here is a way to change the opacity of the toolbar overlay based on the ViewPager’s current position and offset. As paging occurs, the toolbar should become more or less transparent depending on the fragment we’re paging to. Here’s an example of what I mean:

Android Toolbar Overlay with ViewPager

The above is what we’ll be covering in this post. Here’s some additional resources that helped get me to this point. These go a bit more in depth in their respective areas and will prove useful in case anything here isn’t quite detailed enough:

The full solution has been added to my GitHub repository as well. Get to forking!

Let’s get to it!

Prerequisites

Before getting started you’ll need to create a new Xamarin Android project in Visual Studio. You’ll also need to add the v13 and v4 Android Support libraries as references.

While this post is targeted toward Xamarin for Visual Studio, the majority of its content is very translatable to Xamarin and Android Studio

In Visual Studio, adding the support libraries can be done via the NuGet Package Manager:

  1. Navigate to Tools -> NuGet Package Manager -> Manage NuGet Packages for this Solution...
  2. Click the Browse window, then search for Xamarin.Android.Support.v13
  3. In the resulting window, install both Xamarin.Android.Support.v13 and Xamarin.Android.Support.v4 libraries

Android Support Libraries NuGet Package Manager

Adding the ViewPager and Fragment Layouts

We’ll start by creating the ViewPager and its accompanying fragments which will enable that smooth swiping transition between the fragment views. This requires that we create a couple new fragments to be used by the ViewPager control, and add the ViewPager itself to the Main.axml layout.

Under the Resources -> layout folder of your project let’s modify Main.axml and add a couple new layouts:

Modify the Main Layout

This is the starting view of our activity and will encompass our toolbar overlay and the ViewPager. For now we’re just going to focus on adding the ViewPager. We’ll circle back to adding the toolbar overlay to this layout once we’ve built it.

Double-click the Main.axml layout file to edit it. The layout will likely open in the visual designer so you’ll want to click the Source button at the bottom left of the designer, then paste the following:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent">
    <android.support.v4.view.ViewPager
      android:id="@+id/viewPager"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent" />
</RelativeLayout>

Add the FragmentOne Layout

This layout will be the one where we don’t want the toolbar overlay to be visible, allowing the view itself to utilize the screen in its entirety. An example of where this might be handy is when showing a live camera stream in the view; you wouldn’t want the toolbar overlay to take precious real-estate.

Add a new layout file called FragmentOne under the Resources -> layout folder with the following inside:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:background="#CCC">
    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="This view should HIDE the toolbar."
      android:textColor="#000"
      android:textSize="14dp"
      android:layout_centerHorizontal="true" />
</RelativeLayout>

Add the FragmentTwo Layout

This layout will embrace the toolbar overlay. Since the toolbar will indeed be an overlay (that is, it’s floating on top of other content, rather than pushing other content below), we’ll need to force the view’s content to begin below the toolbar. This can be achieved in a number of ways, but for this example we’re just adding padding to the top of the fragment.

Add a new layout file called FragmentTwo under the Resources -> layout folder with the following inside:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="#FFF"
  android:paddingTop="?android:attr/actionBarSize">
  <TextView 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="This view should SHOW the toolbar."
    android:textColor="#000"
    android:textSize="14dp"
    android:padding="5dp"
    android:layout_centerHorizontal="true" />
</LinearLayout>

Implement the FragmentPagerAdapter for the ViewPager

At this point we have the fragment layouts for our ViewPager in place and the ViewPager itself added to the main layout. Now we need to implement a FragmentPagerAdapter for our ViewPager so it knows about our fragments, and how to view them.

Create the ViewPageFragmentAdapter Class

As I mentioned, the ViewPager control requires a concrete implementation of the FragmentPagerAdapter so it knows how to handle paging. In your project, add a new class called ViewPageFragmentAdapter with the following:

 public class ViewPageFragmentAdapter : FragmentPagerAdapter {
    public ViewPageFragmentAdapter(FragmentManager fragmentManager, List<Fragment> fragments) : base(fragmentManager) {
        this.fragments = fragments;
    }

    private readonly List<Fragment> fragments;

    public override int Count => fragments.Count;

    public override Fragment GetItem(int position) {
        if (position < 0) {
            position = 0;
        }

        if (position >= Count) {
            position = Count - 1;
        }

        return fragments[position];
    }
 }

To resolve the missing reference error you’re seeing on the FragmentPagerAdapter, add the using statement using Android.Support.Android.V13.App;.

Modify the MainActivity Class

Our MainActivity class needs to be updated to do the following:

  • Grab a reference to our ViewPager
  • Implement the ViewPager.IOnPageChangeListener interface, which is responsible for handling ViewPager paging and will eventually be where we set the opacity of our toolbar overlay
  • Set the ViewPageFragmentAdapter as the official adapter of our ViewPager.

Modify your MainActivity class to look as follows:

[Activity(Label = "ToolbarOverlay", MainLauncher = true)]
public class MainActivity : Activity, ViewPager.IOnPageChangeListener {
    public void OnPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    }

    public void OnPageScrollStateChanged(int state) {
    }

    public void OnPageSelected(int position) {
    }

    private ViewPager viewPager;
    protected override void OnCreate(Bundle savedInstanceState) {
        base.OnCreate(savedInstanceState);

        // Set our view from the "main" layout resource
        SetContentView(Resource.Layout.Main);

        var fragments = new List<Fragment> {
            new FragmentOne(),
            new FragmentTwo(),
        };

        viewPager = FindViewById<ViewPager>(Resource.Id.viewPager);
        viewPager.Adapter = new ViewPageFragmentAdapter(FragmentManager, fragments);
        viewPager.AddOnPageChangeListener(this);
    }

}

To resolve the reference error you’re seeing on ViewPager.IOnPageChangeListener, add the using statement using Android.Support.V4.View.

At this point we’ve created a ViewPager, a couple fragments for it to page through and implemented the necessary code to teach the pager how to page. With all that in place we should have a fully functional pager, but the default action bar is still visible. Let’s replace it with our custom toolbar overlay!

Replacing the Default Action Bar with a Toolbar Overlay

I mentioned it before, but this guide on Xamarin’s website helped walk me through how to replace the action bar with a custom toolbar. We’ll more or less be following its implementation with a few adjustments.

Create a Custom Theme to Disable the Action Bar

Before we can use our custom toolbar overlay, we need to disable the default action bar. Disabling the action bar can be done by implementing a custom theme. In your project under the Resources -> values folder, add a new XML file called styles.xml with the following:

<?xml version="1.0" encoding="utf-8" ?>
<resources>
  <style name="StoriKnowTheme" parent="@android:style/Theme.Material.Light.DarkActionBar">
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowActionBar">false</item>
    <item name="android:colorPrimary">#5A8622</item>
  </style>
</resources>

That wasn’t too bad, but we still need to set this as the theme in the the Android App Manifest file before our application starts using it. In your project, open the AndroidManifest.xml file under the Properties tree and add the android:theme="@style/StoriKnowTheme" attribute to the application node. Here’s what mine looks like:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
          package="ToolbarOverlay.ToolbarOverlay" 
          android:versionCode="1" 
          android:versionName="1.0">
  <uses-sdk android:minSdkVersion="21" />
  <application android:theme="@style/StoriKnowTheme" android:allowBackup="true" android:label="@string/app_name">
  </application>
</manifest>

Create the Page Aware Custom Toolbar Overlay

We’re almost there! At this point we just need to:

  • Create the custom toolbar overlay layout
  • Add this layout to the Main.axml file
  • Set the opacity as we swipe between pages

Under the Resources -> layout folder, add a new layout called Toolbar with the following:

<?xml version="1.0" encoding="utf-8"?>
<Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="?android:attr/actionBarSize"/>

Now open up the Main layout under Resources -> layout and include the toolbar overlay you just created (that’s line 11 below):

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent">
    <android.support.v4.view.ViewPager
      android:id="@+id/viewPager"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent" />
  <include android:id="@+id/toolbar" layout="@layout/Toolbar" />
</RelativeLayout>

Great, Main.axml knows about the toolbar now, but we still need to tell the main activity that this is actually the new action bar. Along with that, we want to set the opacity of the toolbar overlay as we change pages.

Use the Toolbar as an Action Bar and Set its Opacity During Paging

To wrap things up, let’s modify the MainActivity one last time using the highlighted rows as a visual guide to what changed:

[Activity(Label = "ToolbarOverlay", MainLauncher = true)]
public class MainActivity : Activity, ViewPager.IOnPageChangeListener {
    private ViewPager viewPager;
    private Toolbar toolbar;

    public void OnPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        var opacity = 1.0;
        int toolbarTranslationZ = 100;
        int viewPagerTranslationZ = 99;
        if (position == 0) {
            if (positionOffset == 0) {
                //  position and positionOffset of 0 means we're on the first fragment, which should
                //  have the viewpager on top of the toolbar, so we set the z-index of the toolbar 
                //  to be slightly less than the view pager.
                toolbarTranslationZ = 99;
                viewPagerTranslationZ = 100;
            }

            opacity = positionOffset;
        }

        //  the first parameter of the Color.Argb method is what sets the opacity level
        toolbar.SetBackgroundColor(Color.Argb((int)(opacity * 255), 200, 84, 59));
        toolbar.SetTitleTextColor(Color.Argb((int)(opacity * 255), 255, 255, 255));

        //  just setting the opacity will hide the toolbar, but we'll also set its
        //  z-index for good measure, and to guarantee it doesn't intercept touch events
        //  while it's invisible.
        ViewCompat.SetTranslationZ(viewPager, viewPagerTranslationZ);
        ViewCompat.SetTranslationZ(toolbar, toolbarTranslationZ);
    }

    public void OnPageScrollStateChanged(int state) {
    }

    public void OnPageSelected(int position) {
    }

    protected override void OnCreate(Bundle savedInstanceState) {
        base.OnCreate(savedInstanceState);

        // Set our view from the "main" layout resource
        SetContentView(Resource.Layout.Main);

        var fragments = new List<Fragment> {
            new FragmentOne(),
            new FragmentTwo(),
        };

        toolbar = FindViewById<Toolbar>(Resource.Id.toolbar);
        SetActionBar(toolbar);
        ActionBar.Title = "StoriKnow Toolbar";

        viewPager = FindViewById<ViewPager>(Resource.Id.viewPager);
        viewPager.Adapter = new ViewPageFragmentAdapter(FragmentManager, fragments);
        viewPager.AddOnPageChangeListener(this);
    }

    public class ViewPageFragmentAdapter : FragmentPagerAdapter {
        public ViewPageFragmentAdapter(FragmentManager fragmentManager, List<Fragment> fragments) : base(fragmentManager) {
            this.fragments = fragments;
        }

        private readonly List<Fragment> fragments;

        public override int Count => fragments.Count;

        public override Fragment GetItem(int position) {
            if (position < 0) {
                position = 0;
            }

            if (position >= Count) {
                position = Count - 1;
            }

            return fragments[position];
        }
    }
}

Conclusion

There you have it, we’ve successfully utilized a custom toolbar overlay on a view pager and tied its opacity level to the current pages position and offset.

Working Around the Android Device Monitor File Explorer Bug

Working Around the Android Device Monitor File Explorer Bug

Using Xamarin and Visual Studio I created a sample application to test out the new Camera2ApI. The application is straightforward: provide a stream from the camera and persist the image when a button is clicked. After the image is saved it’d be nice to actually see it on the file system; that’s where the Android Device Monitor comes in. The Android Device Monitor is a debugging and analysis tool which provides a file explorer that enables you to navigate your device’s system. Unfortunately, my file explorer always returned an empty result.

After researching a bit I found I wasn’t the only one experiencing this issue. This has even been reported to the Android team and accepted as a known issue. There hasn’t been a bug fix released to date, but there is a workaround which I’ll be covering in this post.

Create a Supported Android Virtual Device

This bug was introduced in the API 24 release and still exists today. Fortunately, we can still make use of the file explorer by creating a new Android Virtual Device that targets API 23 or earlier. These instructions will be geared toward a Visual Studio audience, but should translate well to the other platforms:

Already have a Virtual Device that targets API 23 or less? Fantastic, you can skip these steps entirely and use that virtual device when you need the file explorer.

  1. In Visual Studio, open the Android SDK Manager: Tools -> Android -> Android SDK ManagerOpen Android SDK Manager
  2. Under any API that’s less than 24 (I chose API 21 here), install the SDK Platform and at least one System ImageAndroid SDK Manager Install
  3. Open the Android Emulator Manager (also called the Android Virtual Device Manager): Tools -> Android -> Android Emulator ManagerOpen Android Emulator Manager
  4. On the right-hand side of the Android Emulator Manager, click the Create button to begin creating a new virtual device. You may create an entirely different device than pictured below, and that’s alright! The point here is to create one that targets API 23 or less.Create Android Virtual Device

Now that we have a virtual device created, let’s verify that our solution works.

Verify the Android Device Monitor File Explorer Works

If all goes well we should be able to explore the files on our new virtual device using the Android Device Monitor file explorer.

  1. In the Android Virtual Device Manager (Tools -> Android -> Android Emulator Manager), start your newly created virtual deviceStart a Virtual Device in Android Device Manager
  2. Once the emulator starts, navigate to the Android Device Monitor: Tools -> Android -> Android Device Monitor, select the loaded emulator (emulator-5554 below) and view the File Explorer tab
    Android Device Monitor File Explorer

There we have it, the file explorer is back in action!

Conclusion

The Android Device Monitor is a highly valuable weapon in our arsenal, and will continue to be so. Bugs like this happen and will eventually go away, but until then we have a viable workaround for those of us who are able to target a lower API.

Map to a Primitive Type from a Complex Type Using AutoMapper

Map to a Primitive Type from a Complex Type Using AutoMapper

Using AutoMapper, I created a mapping for an object with many complex properties, one of which I wanted to map to a primitive type. Here’s how I configured the map from the child property to the primitive type:

CreateMap<ComplexType, int>()
    .ForMember(dest => dest, opt => opt.MapFrom(src => src.Id));

This looks like your standard map configuration: take the Id from the complex property and map it to an int. However, when attempting to run the application I received an AutoMapperConfigurationException:

Custom configuration for members is only supported for top-level individual members on a type.

The stack trace pointed to the CreateMap<ComplexType, int> configuration above. This isn’t a one-to-one mapping, I’m trying to reduce a complex object into a primitive, which AutoMapper requires some help from us to understand. For this, we’ll need to use a custom type converter.

 

Using Custom Type Converter to Map to a Primitive Type

The custom type converter allows you to take full responsibility for defining how one type maps to another. An example of this is mapping a string that looks like an int to an int.

You can create a full-fledged custom type converter by implementing the ITypeConverter<TSource, TDestination> interface, but I used the Func<TSource, TDestination> overload like so:

CreateMap<ComplexType, int>()
    .ConvertUsing(src => src.Id);

With the ConvertUsing extension in place, the mapper configuration no longer throws an exception.

Get Instagram Posts by Tag Name

Get Instagram Posts by Tag Name

Instagram has a very robust API which will enable you to integrate with it in just about any way imaginable. However, there’s also some public endpoints that may get you all the information you need. One such public endpoint which we’ll be covering here is the ability to grab Instagram posts by tag name without the need to register for the Instagram API.

Goal

It’s good to have goals. Ours are simple:

  • Retrieve Instagram posts by tag name as JSON using the public endpoint. 

Get Instagram Posts by Tag Name as JSON

If you were to navigate to the following URL, you’d be shown public Instagram posts with the tag name selfiehttps://www.instagram.com/explore/tags/selfie/

This is a nice way to browse all posts by tag, starting with the most recent. Now, try adding ?__a=1 to the end of that same URL: https://www.instagram.com/explore/tags/selfie/?__a=1

Pretty interesting, right? By adding ?__a=1, you’re telling Instagram to return the results as JSON, which provides a universal way for us to digest the information with code. 

Why does Instagram allow this? Great question, if you have the answer I implore you to share in the comments.

Making Sense of the JSON Result

Now that we know how to grab a JSON result of the most recent Instagram posts by tag name, we just need to make sense of it. We’ll start by using an online JSON pretty printer to make the JSON blob a bit friendlier to look at. There’s a lot of data, but we’re going to hone in on the nodes property under tag > media. This nodes property contains a breakdown of actual post data.

Below, I’ve highlighted some of the more interesting properties of the post that we’ll be discussing:

{  
   "tag":{  
      "name":"selfie",
      "content_advisory":null,
      "media":{  
         "nodes":[  
            {  
               "comments_disabled":false,
               "id":"1638021685389222559",
               "dimensions":{  },
               "owner":{  
                  "id":"4736824623"
               },
               "thumbnail_src":"https://scontent-iad3-1.cdninstagram.com/t51.2885-15/e35/c0.10.500.500/22860707_489313221440833_4558795553466482688_n.jpg",
               "thumbnail_resources":[  ],
               "is_video":false,
               "code":"Ba7bAtUAuKf",
               "date":1509487423,
               "display_src":"https://scontent-iad3-1.cdninstagram.com/t51.2885-15/e35/22860707_489313221440833_4558795553466482688_n.jpg",
               "caption":"#girl #style #pretty #blogger #beautiful #beauty #fashion #ootd #makeup #instagood #like4like #cool #moda #trend #adidas #shoes #shopping #goals #selfie #inspiration #outfit #look #fit #luxury #nails #model #followme",
               "comments":{  
                  "count":0
               },
               "likes":{  
                  "count":1
               }
            },
            ...

Referencing the highlighted properties:

  • code can be used to get information about the originator of the post. You may have noticed there’s an owner property, too, but for the sake of this article we’ll focus on the code property to get originator information.
  • date is when the post was submitted, and is represented by the number of milliseconds since the epoch.
  • display_src is the URL to the posted image. You can see the image by pasting that URL in your browser.
  • caption is, well, the caption associated with the post. This particular caption is a massive list of tags, one of which happens to be #selfie!

As I mentioned, the code property can be used to obtain information about the originator of the post. This is accomplished by plugging the value into another public API. Replace the <code> tag below with your code property value, and paste the URL in your browser to obtain another block of JSON:

https://www.instagram.com/p/<code>/?__a=1

We are once again provided with a decent chunk of data. Use the online JSON pretty printer we discussed earlier to format it so it’s easier to follow. Here’s a sample of the output, which I’ve shortened a bit:

{  
   "graphql":{  
      "shortcode_media":{  
         "__typename":"GraphImage",
         "id":"1638021685389222559",
         "shortcode":"Ba7bAtUAuKf",
         ...
         ...
         ...
         "owner":{  
            "id":"4736824623",
            "profile_pic_url":"https://scontent-iad3-1.cdninstagram.com/t51.2885-19/s150x150/18161617_288442401613475_2938897407410176000_a.jpg",
            "username":"andysprite",
            "blocked_by_viewer":false,
            "followed_by_viewer":false,
            "full_name":"Andy",
            "has_blocked_viewer":false,
            "is_private":false,
            "is_unpublished":false,
            "is_verified":false,
            "requested_by_viewer":false
         },
         ...

The post owner’s information is under the graphql > shortcode_media > owner property. From there you can get information such as username and full_name, which I’ve highlighted above.

We now have a fairly straightforward way to:

  • Grab the most recent Instagram posts by tag name as JSON.
  • Identify individual post data, and use that data to obtain additional information such as the poster’s name and username.

This is pretty great, we have a lot to work with here. However, the JSON results are paged which means at this point we’ve only looked at the very first set of results. A bit more investigation into the JSON result shows that paging is built in.

Requesting the Next Page of Results

Looking back at the JSON under the tags > media property, you’ll see another property called page_info (it may be easier if you collapse the nodes property, as that can take a lot of vertical space):

{  
   "tag":{  
      "name":"selfie",
      "content_advisory":null,
      "media":{  
         "nodes":[  ],
         "count":321717673,
         "page_info":{  
            "has_next_page":true,
            "end_cursor":"J0HWfjzPwAAAF0HWfjzOAAAAFiYA"
         }
      },
      "top_posts":{  }
   }
}

The page_info property has its own children, has_next_page and end_cursor. You probably guessed this, but if has_next_page is set to false, we’re at the very end of the results. Otherwise, the end_cursor property can be appended to the original URL we used via the max_id query parameter to retrieve the very next set of results. Here’s an example of what that might look like:

https://www.instagram.com/explore/tags/selfie/?__a=1&max_id=J0HWfjzPwAAAF0HWfjzOAAAAFiYA

That’s all there is to paging the result set.

Conclusion

We discussed using a public API to retrieve Instagram posts by tag name as a JSON result. After some investigation, we were able to understand the structure of this result and identify some of the more exciting properties. We’re now equipped with enough information to consume the JSON result in code.