Elmwood no more, long live Elmwood! Elmwood Electronics and PiShop are now together!
Please order via PiShop.ca, as we are no longer taking orders through this site.
More details are in our blog!

Driving a Mechanical Speedometer with a Raspberry Pi Part 2 of 2: The Code and Electrical Bits

February 20, 2019

Driving a Mechanical Speedometer with a Raspberry Pi  Part 2 of 2: The Code and Electrical Bits

In our first installment, you learned how we put together our Raspberry Pi-powered Mechanical Speedometer, now we will share how the device works.

First, a bit about the Adafruit Motor HAT.  This is a great add-on board for the Pi, and it allows you to control up to 4 regular motors.  You have to solder the headers to the HAT which takes a few minutes.  The HAT should be powered separate from the Pi, and the HAT can take 5V to 12V.  The HAT does not have a barrel jack connector, but you can use an adapter and some wire.   In our case, we ran the HAT at 5V because our Beefy Hobby Motor drove the speedometer quite well with 5V.  

In classic Adafruit fashion, they have an excellent tutorial for the Motor HAT, as well as an easy to use Python library.  The library is in Python 3, and you install it with the following command: 

sudo pip3 install adafruit-circuitpython-motorkit

Now we'll step you through our code.

First, we import all the libraries we need:

  • "json" is the Javascript Object Notation library, and it is how Shopify's API formats its data.  
  • "requests" is how we do an HTTPS request to Shopify's API
  • "time" allows us to put in a "sleep" message, telling the code to pause
  • "sqlite3" is the library for our SQL database of orders
  • "MotorKit" is the library we imported earlier that controls the Adafruit Motor HAT
import json, requests, time, sqlite3
from adafruit_motorkit import MotorKit
We define the function we will use to drive the motor:

kit = MotorKit()

We like using databases to handle data in any setting, so we are using a database to track the orders on our Odometer Pi.  This simple database tracks the orders numbers, so we know when a new order has arrived.  We create a database if it does not exist, keeping the order number as a unique integer.  We then select the largest order number from the database, which we will use later on when we compare to the "new" largest order number.

order_num INT UNIQUE,
order_num_str TEXT

cur.execute('SELECT MAX(order_num) FROM Orders')
order_max_list = cur.fetchone()
start_max_order = order_max_list[0]

Here we are establishing input_orders as the URL we'll use to obtain the JSON file of our latest orders.  Replace your_api_key and "your_store" as appropriate.  We then create a request with the URL and parse the JSON data with r.json()

input_orders = "https://your_api_key@your_store.myshopify.com/admin/orders.json?limit=250"

r = requests.get(input_orders)

data_orders = r.json()

In this block of code we are pulling useful information out of the JSON file.  In the world of Shopify, the order number is a string called "name", so we have to parse out the numeric portion.  We then insert the new orders into the database, if applicable.  With the "INSERT OR IGNORE" statement, it will only insert new data if the latest order number is different from all the other order numbers, since order_number is a "unique" item in our database schema.

for item_order in data_orders["orders"]:

this_order_str = item_order["name"]
this_order_number = int(this_order_str[1:6])
cur.execute('''INSERT OR IGNORE INTO Orders (order_num, order_num_str) VALUES ( ?, ?)''', (this_order_number, this_order_str))

After we have updated the  the database with any new entries, we do another check of the database to get the largest number:

cur.execute('SELECT MAX(order_num) FROM Orders')
order_max_list = cur.fetchone()
end_max_order = order_max_list[0] 

In this block of code, we are checking the "ending max order" against the "starting max order".  If the ending number is greater, we calculate how many new orders with the variable "increment".  We then print to the screen that we are incrementing the odometer.

Through trial and error, we have determined that running the motor 10.5 seconds at 100% throttle will increase the odometer by 0.1 miles, or for us, 1 order.  So we run the motor at 100%, pause code execution for 10.5 seconds with time.sleep, and then turn off the motor.

If no new orders came in, we print a message to the console.

if (end_max_order > start_max_order):

increment = (end_max_order - start_max_order)
print ("Incrementing odometer by: ", increment)
runtime = increment * 10.5
kit.motor1.throttle = 1
kit.motor1.throttle = 0
print ("Nothing to report, last order E" + str(end_max_order))

For our program to run automatically, we add it as a Cron job.  To do this, we edit our crontab with the following command:

  • crontab -e

We then add the following line to our crontab, which causes our script to run every 2 minutes and then appends the text output to a log file.  

*/2 * * * * python3 /home/pi/odo_order.py >> /home/pi/cron.log 2>&1

This was a super fun project for me, as it combines an old mechanical part with my favourite single board computer, the Raspberry Pi.  If you are interested in learning Python, I highly recommend Dr. Charles Severance's excellent Python for Everybody Specialization from the University of Michigan via Coursera.  I personally did the entire specialization, and it has been priceless as we have built a bunch of Python scripts to help run our business.

Finally, here is the end result in action:

And here's what happens when we get an order, now shipping order 9366:

Also in News

Elmwood no more, long live Elmwood! Elmwood Electronics and PiShop are now together!

October 03, 2022

Elmwood no more, long live Elmwood! Elmwood Electronics and PiShop are now together! Please see our blog for more info.

Continue Reading

Adding RGB LEDs to an illuminated arcade button

March 14, 2022

Continue Reading

We Made a Media Controller!

October 12, 2021

Continue Reading