Skip to content

12. Machine

I worked with Aaron Logan, Jack Donnelly, and Jada Greene. The full group work for our machine can be found here. All files can be found there as well.

My contributions to the group included designing the corner connectors and y axis carriage as well as playing a large part in the design of the wood base and the x axis carriage. I also wrote the final code implementing parts that myself and other members of the team tested separately.

Concept

Our machine would take data on the location of Elon Musk’s private jet (or any other plane we choose) from a flight api and move a laser pointer to that corresponding location on a map.

Corner Connectors

The design for this was fairly simple with just a 3 pronged piece with holes to press fit the steel rods into.

Y axis carriage

The hole in the middle mounts the laser on it with the 2 slots for connecting the belt on either side on the top. The 2 holes mount it to the steel rods via linear bearings, and all the edges have a fillet to them. This was made to be as small as possible both to minimize weight the gantry would have to move and to maximize the amount of usable space in the middle.

Base

I made the design svg in cuttle while my groupmates used it to make a file for cnc cutting it. It was fairly simple with 4 holes in each of the four corners to have the vertical steel rods go into and a rectangle that would be pocketed out for the map to go into. The pocket cut for the map was made slightly bigger than the map for 2 reasons: to be able to be easily slid in and to account for the round bit not cutting all the way into the corner and causing the map not to fit, but using dogbones would not be aesthetically pleasing, so making the pocket bigger would solve the problem while keeping aesthetic elements.

X axis carriage

I worked with Aaron Logan on this. He did the CAD work while I advised him based on how I did the y axis carriage, and I determined measurements for it based on the other parts of the machine.

Code

The final code I wrote and put together was split in 3 files

apiData.py

This had functions involving getting raw data from the api and converting it from latitude longitude to inches on the map.

import requests
import math
from numpy import log as ln

def get_lat_lon(callsign='N502SX'):
    # uses the api to get the latitude and longitude and returns a list of 2 ints
    url = 'https://adsbexchange-com1.p.rapidapi.com/v2/callsign/' + callsign + '/'
    headers = {
        "X-RapidAPI-Host": "adsbexchange-com1.p.rapidapi.com",
        "X-RapidAPI-Key": "27d6e0192emsh0b0d9c0364793c1p175136jsn803c2abbbc60"
    }
    response = requests.get(url, headers=headers)
    data = response.text

    lat, lon = 0.0, 0.0
    flying = False

    for i in range(len(data)):
        if data[i: i + 6] == '"lat":':
            lenNum = 1
            while data[i + lenNum] != ',':
                lenNum += 1
            lat = float(data[i + 6: i + lenNum])
            flying = True
        elif data[i: i + 6] == '"lon":':
            lenNum = 1
            while data[i + lenNum] != ',':
                lenNum += 1
            lon = float(data[i + 6: i + lenNum])
            flying = True

    return [lon, lat], flying

def lat_lon_to_xy(pos): 
# Define the size of map
    mapWidth    = 584.2
    mapHeight   = 431.8


    # get x value
    x = (pos[0] + 180) * (mapWidth / 360)

    # convert from degrees to radians
    latRad = (pos[1] * math.pi) / 180

    # get y value
    mercN = ln(math.tan((math.pi / 4) + (latRad / 2)))
    y     = (mapHeight / 2) - (mapWidth * mercN / (2 * math.pi))

    return [x, mapHeight-y] # mapHeight - y makes the origin point from the bottom left corner for the latitude 

stepper.py

This had a class for the stepper motor that used a combination of code from my outputs week (converted from arduino to python of course) and work from Aaron and Jada.

import RPi.GPIO as GPIO
from time import sleep

class Stepper():
    def __init__(self, chan, delayTime):
        self.chan = chan
        self.delayTime = delayTime
        self.phase = 0

    def run_step(self):
        if self.phase == 0:
            GPIO.output(self.chan, (GPIO.HIGH, GPIO.LOW, GPIO.HIGH, GPIO.LOW))
        elif self.phase == 1:
            GPIO.output(self.chan, (GPIO.LOW, GPIO.HIGH, GPIO.HIGH, GPIO.LOW))
        elif self.phase == 2:
            GPIO.output(self.chan, (GPIO.LOW, GPIO.HIGH, GPIO.LOW, GPIO.HIGH))
        elif self.phase == 3:
            GPIO.output(self.chan, (GPIO.HIGH, GPIO.LOW, GPIO.LOW, GPIO.HIGH))
        sleep(self.delayTime)

    def step_cw(self, num_steps):
        for i in range(num_steps):
            self.phase = (self.phase + 1) % 4
            self.run_step()

    def step_ccw(self, num_steps):
        for i in range(num_steps):
            self.phase = (self.phase - 1) % 4
            self.run_step()

    def clear(self):
        GPIO.output(self.chan, (GPIO.LOW, GPIO.LOW, GPIO.LOW, GPIO.LOW))

main.py

This put everything together with keeping track of the gantry while taking data from the api and moving accordingly.

import apiData #hello
from stepper import Stepper
import math
from time import sleep
import RPi.GPIO as GPIO

__name__ = 'main'

if __name__ == 'main':
    gear_diameter = 12
    gear_cirumference = 12 * math.pi
    step_dist = gear_cirumference / 200

    GPIO.setmode(GPIO.BOARD)
    motorx_chan = (29, 31, 33, 35)
    motory_chan = (32, 36, 38, 40)
    GPIO.setup(motorx_chan, GPIO.OUT)
    GPIO.setup(motory_chan, GPIO.OUT)
    stepperx = Stepper(motorx_chan, 0.005)  # add pins and stuff later
    steppery = Stepper(motory_chan, 0.005) # add pins and stuff later
    callsign = input('Enter a callsign of a plane to track: ')

    # steps from corner 9.42477796076938mm each direction to get to map 0
    #stepperx.step_cw(50)
    #steppery.step_cw(50)
    pos = [0, 0]

    while True:
        latLon, flying = apiData.get_lat_lon(callsign)
        print(latLon)
        if not flying:
            stepperx.clear()
            steppery.clear()
            sleep(60)
        else:
            new_pos = apiData.lat_lon_to_xy(latLon)
            new_pos[1] -= 31.75
            new_pos[1] %= 431.8
            print(new_pos)
            while new_pos[0] > pos[0]:
                stepperx.step_cw(1)
                pos[0] += step_dist
            while new_pos[0] < pos[0]:
                stepperx.step_ccw(1)
                pos[0] -= step_dist
            while new_pos[1] > pos[1]:
                steppery.step_ccw(1)
                pos[1] += step_dist
            while new_pos[1] < pos[1]:
                steppery.step_cw(1)
                pos[1] -= step_dist
            stepperx.clear()
            steppery.clear()
            sleep(60)

Last update: June 30, 2022