- What's new
InboxReady x Salesforce: The Key to a Stronger Email Deliverability
Before we get technical, if you haven’t read our previous post about our wonderous snack machine, pop over and check that out first! We bought an off-the-shelf snack dispenser and turned it into a robot, but now it’s time to give it some brains. And by brains, we mean a raspberry pi.
If you’ve read up on some of our other mechanic projects, you may have noticed that we tend to use a Raspberry Pi for any hardware hack. They’re easy to set up, have a ton of extensions, and just about everyone loves playing with them. It was the perfect choice for what we wanted to accomplish — ultimate automated snackage.
Speaking of extensions, one of the extensions out there is Adafruit DC & Stepper Motor HAT, which looks to be perfectly suited for us.
Electrical system as we’re going to use it
The board consists of a Pulse Width Modulation controller PCA9685 and 2 motor drivers based on TB6612FNG. The controller receives the commands over the I2C bus from the Pi and translates them to proper PWM signals. These low voltage signals enter the drivers and control the 12V outputs to power the motors.
Our motors are bi-polar steppers, which means they consist of 2 groups of coils that have to be energized with alternating polarity. Luckily, TB6612FNG is designed for working with dual motors, so each driver will be dedicated to its own stepper. The drivers have a separate power distribution circuit, which we’ll cover in the next section.
We got our Pi from CanaKit and it came with a basic micro USB 2.5A 5V power adapter. The motors on the hat need 12V and should be powered separately. Since they draw 350mA each, we need to provide at least 700mA to them. The drivers draw about 2mA each, which is not that significant. However, when designing any system (even a software one), we highly recommend the safety factor of 2-3 or even more.
To build safe robust systems, we have to take our ignorance about the real world into account. Take, for example, the Tacoma Narrows Bridge vs. the Brooklyn Bridge. The Tacoma Narrows Bridge didn’t have a safety factor and it inevitably collapsed. Conversely, the Brooklyn Bridge was constructed with a safety factor of a 5-6 and is still around to this day.
Fortunately, there are tons of options when it comes to power supplies to keep our snack machine nice and safe. We just went with a 5A power supply. If you’re still worried about how to hook up the power supply safely, this guide can help.
Now that we had everything we needed to complete the snack machine, it was time to put it all together. Picture time!
The LED rings are for Snack Machine v2.0.0
Before we really get going, we had to solder the HAT.
GPIO inputs are in the front, power and motor terminals are in the back.
Then it was time to attach the power connector.
With the power in place, we could attach the motors to the HAT.
It’s so beautiful.
To control the HAT over I2C, we need to install Adafruit CircuitPython MotorKit. The installation is pretty straightforward, and we ended up using this guide to get the steppers working correctly. In our case, we came up with a decent back-and-forth algorithm and tuned it to dispense the right amount of snack in a single run.
1#!/usr/bin/python2# -*- coding: utf-8 -*-3# vim: set ai et ts=4 sts=4 sw=4 syntax=python:45import atexit6import logging7import sys8import time9from collections import OrderedDict10from contextlib import contextmanager11from datetime import datetime, timedelta1213from Adafruit_MotorHAT import Adafruit_MotorHAT, Adafruit_DCMotor, Adafruit_StepperMotor1415_log = logging.getLogger(__name__)16_mh = None17_last_dispensed_time = None1819DEFAULT_SPR = 200 # Default Steps-per-Revolution20DEFAULT_RPM = 30 # Default Revolutions-per-Minute21DISPENSE_COOLDOWN = timedelta(seconds=30)22VALID_CHANNELS = [1, 2]23CHANNEL_MOTOR_MAP = {24 1: [1, 2],25 2: [3, 4],26}27DISPENSER_SERVE_STRATEGY = {28 # RIGHT29 #1: [30 # OrderedDict([31 # (Adafruit_MotorHAT.FORWARD, 50),32 # (Adafruit_MotorHAT.BACKWARD, 25),33 # ]),34 # OrderedDict([35 # (Adafruit_MotorHAT.BACKWARD, 50),36 # (Adafruit_MotorHAT.FORWARD, 75),37 # ]),38 #],39 1: [40 OrderedDict([41 (Adafruit_MotorHAT.FORWARD, 66),42 (Adafruit_MotorHAT.BACKWARD, 50),43 ]),44 ],45 # LEFT46 2: [47 OrderedDict([48 (Adafruit_MotorHAT.FORWARD, 100),49 (Adafruit_MotorHAT.BACKWARD, 50),50 ]),51 OrderedDict([52 (Adafruit_MotorHAT.BACKWARD, 125),53 (Adafruit_MotorHAT.FORWARD, 50),54 ]),55 ],56}575859def init(hat_config=None):6061 global _mh6263 # create a default object, no changes to I2C address or frequency64 if hat_config is None:65 hat_config = {}6667 _mh = Adafruit_MotorHAT(**hat_config)6869 # recommended for auto-disabling motors on shutdown!70 atexit.register(turn_off_motors)717273def turn_off_motors():74 """ Release all of the motors75 """7677 global _mh7879 _log.info("Shutting down all motors")8081 for i in range(1, 5):82 _mh.getMotor(i).run(Adafruit_MotorHAT.RELEASE)838485def can_dispense():86 """ Figure out if snacks can be dispensed87 """8889 global _last_dispensed_time9091# if _last_dispensed_time is None:92# return True93# 94# if (_last_dispensed_time + DISPENSE_COOLDOWN) < datetime.utcnow():95# return True9697 return True9899100@contextmanager101def using_stepper(channel, steps_per_rev=DEFAULT_SPR, rpm=DEFAULT_RPM):102103 global _mh104105 # initialize the stepper106 _log.info("Create stepper controller: {} (spr={}, rpm={})".format(107 channel,108 steps_per_rev,109 rpm,110 ))111 stepper = _mh.getStepper(steps_per_rev, channel)112 stepper.setSpeed(rpm)113114 # pass the stepper up to the parent context115 yield stepper116117 # release the stepper118 _log.info("Releasing stepper: {}".format(channel))119 motor_nums = CHANNEL_MOTOR_MAP[channel]120 for motor in motor_nums:121 _mh.getMotor(motor).run(Adafruit_MotorHAT.RELEASE)122123124def dispense_to(motor_channel):125126 if motor_channel not in VALID_CHANNELS:127 raise ValueError("Invalid motor channel: {}, expected one of: {}".format(128 motor_channel,129 VALID_CHANNELS,130 ))131132 if not can_dispense():133 raise RuntimeError("Dispenser cooldown period - {}".format(str(DISPENSE_COOLDOWN)))134135 with using_stepper(motor_channel) as stepper:136 # Get the dispense rates137 dispenser_strategy = DISPENSER_SERVE_STRATEGY[motor_channel]138 for operations in dispenser_strategy:139 _log.info("Executing operation set: {}".format(operations))140141 for direction, steps in operations.items():142 stepper.step(steps, direction, Adafruit_MotorHAT.DOUBLE)
Here it is! A working prototype!
Magic in action!
As usual, we connected the machine to Mailgun. This could be done in a few different ways and since we already covered it in other posts we are not going to concentrate on it here. However, there is an interesting opportunity to turn it into a real IoT device.
So far we just use it in the kitchen so a fellow mailgunners can feast on M&Ms like a boss. Still, it’s brought plenty of people delicious candy, and quite a few laughs when it became unplugged and then delivered too many M&Ms once plugged back in.
Last updated on May 04, 2021
InboxReady x Salesforce: The Key to a Stronger Email Deliverability
Black History Month in Tech: 7 Visionaries Who Shaped The Future
How To Improve Your Email Deliverability In 2022
Mailgun Joins Sinch: The Future of Customer Communications Is Here
Mailpets: For The Love Of Animals
The Mailgun Maverick Program Is Here!
Force for Change: It's Time to Speak Out
When Should You Use An Email API?
Mailgun’s COVID-19 Plan of Action
What we've been up to: Mailgun's 2019 Year in Review
InboxReady x Salesforce: The Key to a Stronger Email Deliverability
Become an Email Pro With Our Templates API
Google Postmaster Tools: Understanding Sender Reputation
Navigating Your Career as a Woman in Tech
Implementing Dmarc – A Step-by-Step Guide
Email Bounces: What To Do About Them
Announcing InboxReady: The deliverability suite you need to hit the inbox
Black History Month in Tech: 7 Visionaries Who Shaped The Future
How To Create a Successful Triggered Email Program
Designing HTML Email Templates For Transactional Emails
InboxReady x Salesforce: The Key to a Stronger Email Deliverability
Implementing Dmarc – A Step-by-Step Guide
Announcing InboxReady: The deliverability suite you need to hit the inbox
Designing HTML Email Templates For Transactional Emails
Email Security Best Practices: How To Keep Your Email Program Safe
Mailgun’s Active Defense Against Log4j
Email Blasts: The Dos And Many Don’ts Of Mass Email Sending
Email's Best of 2021
5 Ideas For Better Developer-Designer Collaboration
Mailgun Joins Sinch: The Future of Customer Communications Is Here
Always be in the know and grab free email resources!
By sending this form, I agree that Mailgun may contact me and process my data in accordance with its Privacy Policy.