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!
Living in the city has shown me just how hard it can be to find an affordable apartment with a reasonable amount of space. Having two cats in such a small place doesn’t really give them much room to roam or pseudo hunt. Inspired by this cat tower, I decided to build an automatic cat laser pointer toy using my Raspberry Pi. The cats had one condition though, that it be a shark with a frikin’ laser beam attached to its heads (sound warning!).
If you’ve come across my automatic cat feeder post you might be thinking I’m automating myself out of the equation. Fear not, for we are still their primary source of entertainment, as they are ours.
Goal
The goal may be obvious at this point, but I like being explicit: tobuild an automatic cat laser pointer toy that is configurable, and can be triggered by a button.
Bonus Goal: configure and trigger this remotely. We’ll focus on the simpler solution first, and optionally over-engineer the project later on.
Prerequisites
You should have a configured Raspberry Pi ready to go. I used my Raspberry Pi 3B (40 GPIO pins), a 32gb MicroSD card (you could certainly go smaller) and the Raspbian distro (highly recommended). This guide was pretty straightforward and got me up and running in no time.
The Parts
As with my automatic cat feeder post, I decided to break this down into four distinct parts:
Here in part one we’re introducing the project and what you’ll need to get it done.
In part two we’ll assemble all the pieces of the cat laser pointer toy.
Part three will focus on writing the python code, which will be responsible for moving the servo motors and turning on the laser beam at the push of a button.
Part four will cover the bonus goal of configuring and triggering remotely [optional].
Things You’ll Need for the Automatic Cat Laser Pointer Toy
6x male-to-female, 6x any-to-female jumper wires to connect everything to the GPIO pins. What I mean by “any” here is that “it doesn’t matter“, because we’ll be cutting off that end of the wire and soldering it anyway.
1x NPN Transistor to logically control the Pi’s 3v3 power rail through a GPIO pin, and toggle the laser on/ off. You only need one of these. eBay may have better options in terms of quantity, or hey, even stop at a physical store like Radioshack!
5v 2.5A power supply to power the Pi, servos and lasers. The servos and laser draw a good amount of current, so we need to make sure the power supply is capable of supporting it.
Raspberry Pi Case to house the Pi and wiring. This isn’t strictly necessary, but it made for a convenient way to mount the laser and control the wiring.
Velcro to mount the shark (or laser if you opted out of adding a shark, which saddens me) to the pan/tilt mount. I used velcro here so I can conveniently point the laser in the opposite direction, which provides an entirely different available range for the pan/tilt mount.
[Optional] Power drill to make holes in the Raspberry Pi case to fasten the pan/tilt mount to it. If I did this project over, I would have used velcro as my fastener as it’s much simpler and less intrusive. Your call.
Hot glue gun to glue the laser to the shark. Again, any other adhesive would suffice if you don’t already have a hot glue gun.
At this point we covered the basics of what it is we’re trying to build here in part one, and what you’ll need to accomplish it. Head over to part two where we’ll be focusing on assembling all the pieces.
Ahead of the game? Nice! Part three has the Python code, and part four sets up a way to remotely control the whole thing.
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!
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 power, ground 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:
X Servo
Y Servo
powerconnects to header pin 2 (5v power)
ground connects to header pin 6 (Ground)
controlconnects to header pin 7 (GPIO4)
powerconnects to header pin 4 (5v power)
ground connects to header pin 9 (Ground)
controlconnects 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.
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:
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.
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.
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
powerconnects to header pin 2 (5v power)
ground connects to header pin 6 (Ground)
controlconnects to header pin 7 (GPIO4)
powerconnects to header pin 4 (5v power)
ground connects to header pin 9 (Ground)
controlconnects to header pin 11 (GPIO17)
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.
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)
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.
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 FourI’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.
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!
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.
Under the Signing in to Google section, click the 2-Step Verification menu, then follow the instructions to enable 2-Step Verification
Back in the Sign-in and security page right under the 2-Step Verification button you’ll see App passwords.
Generate a password
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!
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:
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:
Setup an account if you haven’t already (free, free, free).
Use IFTTT website or download the app to your phone and log in.
In the My Applets section, add a new applet.
You’ll see a screen saying if +this then that: click the +this button, then select the Date & Time service.
Select the Every day at trigger, and select the time you’d like the cat laser pointer toy to activate, then hit next
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.
Don’t forget, you have the option of adding configuration to the subject, too.
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.
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!
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:
Property
Description
run_time
How long, in seconds, the cat laser pointer toy should operate each time the button is pressed.
min_movement
The 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_min
The 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_max
The 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_min
The 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_max
The 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:
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:
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.
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!
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
Notes
Commands
clone an existing repository
git clone "repository-path"
create a new local repository
git init
Branch Management
Notes
Commands
list local branches
git branch
list local and remote branches
git branch -av
create local branch
git branch "branch-name"
create and checkout local branch
git 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 branch
git branch -D "branch-name"
delete local and remote branch
git push origin --delete "branch-name"
checkout branch
git checkout "branch-name"
rename currently checked out local branch
git branch -m "new-branch-name"
Handling Changes
Notes
Commands
see changes made to local branch
git status
track and stage local changes
git add .
commit staged only with a commit message
git commit -m "commit-message"
commit unstaged and staged with a commit message
git 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 branch
git stash pop
Undoing (oops)
Notes
Commands
discard all changes in working directory
git reset --hard
reset HEAD to a specific commit and discard local changes
git reset --hard
reset HEAD to a specific commit but preserve local changes
git reset "commit-hash"
discard all changes to specific file
git checkout HEAD
revert commit by applying previous commit changes to HEAD then committing
git revert
Tracking History
Notes
Commands
show all commits for current HEAD
git log
show all commits for current HEAD for given file
git log -p "file"
show all commits that HEAD has pointed to
git reflog
Fetch, Pull & Push
Notes
Commands
grab changes from remote, but don't merge into current HEAD
git 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 HEAD
git pull
create and track remote branch, push all local changes to it
git push -u "remote" "branch-name"
push all local changes to tracked remote branch for current HEAD
git push
Merge & Rebase
Notes
Commands
merge commits from branch into current HEAD
git merge "source-branch-name"
merge commits from source branch into target branch
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!
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.
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!
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:
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.
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!
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:
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:
Navigate to Tools -> NuGet Package Manager -> Manage NuGet Packages for this Solution...
Click the Browse window, then search for Xamarin.Android.Support.v13
In the resulting window, install both Xamarin.Android.Support.v13 and Xamarin.Android.Support.v4 libraries
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:
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:
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:
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:
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.
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!
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.
In Visual Studio, open the Android SDK Manager: Tools -> Android -> Android SDK Manager
Under any API that’s less than 24 (I chose API 21 here), install the SDK Platform and at least one System Image
Open the Android Emulator Manager (also called the Android Virtual Device Manager): Tools -> Android -> Android Emulator Manager
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.
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.
In the Android Virtual Device Manager (Tools -> Android -> Android Emulator Manager), start your newly created virtual device
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
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.
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!
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:
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:
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!