Using sequences stored in YAML to Control Stepper Motors like G-Code.

This code written in Python uses sequences stored in YAML to control an Arduino Mega 2560 hooked to 5 stepper motors. The Arduino is attached to 5 step drivers which control 5 different motors. 4 of those step drivers are TB6600’s hooked to NEMA 17 motors and the other is the 28BYJ-48 5V Stepper motor and driver.

The Arduino is programmed to move each of the motors based of 2 character codes it receives through a serial connection. Below is a list of each on the of the commands and what they do when received by the Arduino.

u1 = z-axis step up 1 increment(s)
d1 = z-axis step down 1 increment(s)
u2 = z-axis step up 2 increment(s)
d2 = z-axis step down 2 increment(s)
u3 = z-axis step up 3 increment(s)
d3 = z-axis step down 3 increment(s)
u4 = z-axis step up 4 increment(s)
d4 = z-axis step down 4 increment(s)
f1 = y-axis step forward 1 increment(s)
b1 = y-axis step backwards 1 increment(s)
f2 = y-axis step forward 2 increment(s)
b2 = y-axis step backwards 2 increment(s)
f3 = y-axis step forward 3 increment(s)
b3 = y-axis step backwards 3 increment(s)
f4 = y-axis step forward 4 increment(s)
b4 = y-axis step backwards 4 increment(s)
i1 = tilt axis step up 1 increment(s)
s1 = tilt axis step down 1 increment(s)
i2 = tilt axis step up 2 increment(s)
s2 = tilt axis step down 2 increment(s)
i3 = tilt axis step up 3 increment(s)
s3 = tilt axis step down 3 increment(s)
i4 = tilt axis step up 4 increment(s)
s4 = tilt axis step down 4 increment(s)
q1 = platter axis spin left 1 increment(s)
t1 = platter axis step right 1 increment(s)
q2 = platter axis spin left 2 increment(s)
t2 = platter axis spin right 2 increment(s)
q3 = platter axis spin left 3 increment(s)
t3 = platter axis step right 3 increment(s)
q4 = platter axis spin left 4 increment(s)
t4 = platter axis step right 4 increment(s)
x1 = pan left 1 increment(s)
y1 = pan right 1 increment(s)
x2 = pan left 2 increment(s)
y2 = pan right 2 increment(s)
x3 = pan left 3 increment(s)
y3 = pan right 3 increment(s)
x4 = pan left 4 increment(s)
y4 = pan right 4 increment(s)

Here we’re importing all the modules we need to run our script

#!/usr/bin/env python3
import subprocess
import serial
import datetime
import yaml
from time import sleep
import time
import os.path
import logging
from subprocess import Popen
import threading, queue

Basic Setup for logging


logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)


handler = logging.FileHandler('./logs/runsequence.log')
handler.setLevel(logging.INFO)

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)

logger.addHandler(handler)
current_time = datetime.datetime.now()

Setting up a serial connection between the Arduino


arduinoconnection = serial.Serial('/dev/ttyACM0', 115200, timeout=1)
arduinoconnection.flushInput()

Referencing and opening our YAML file

yaml_file = 'seq.yaml' # key: value
# Openning our YAML file
with open(yaml_file, 'r') as fh:
    python_object = yaml.load(fh, Loader=yaml.SafeLoader)

Pulls saved sequence from YAML file and converts it to a variable we can use in python

saved_sequences = python_object['savedsequences']

This function is used to remove a unwanted characters in this case a comma

def cleanoutcharacters(removecharacterfun, X):

    # Base Case
    if (len(removecharacterfun) == 0):
        return ''

    # Check the first character of removecharacterfuning
    if (removecharacterfun[0] == X):

        # Pass the rest of the removecharacterfuning to recursion Function call
        return cleanoutcharacters(removecharacterfun[1:], X)

    # Add the first character of removecharacterfun and removecharacterfuning from recursion
    return removecharacterfun[0] + cleanoutcharacters(removecharacterfun[1:], X)

# this feeds the output from yaml to the recursion code
# now the removecharacterfun is repesented by what we pulled from the YAML
removecharacterfun = saved_sequences

# characters we are removing from the output so that it will play nicely with the input 
# on arduino that runs the motor on the scanner
X = ','

# Function call to clear out set characters
removecharacterfun = cleanoutcharacters(removecharacterfun, X)

Using replace to replace characters I want to represent. Here we are replacing the character ‘r’ for a ‘\n’ (a return signal) to the Arduino

characterreplacefunc = (removecharacterfun.replace('r', '\n'))

This portion of the code gives a test handshake with the Arduino and logs the outcome

arduinoconnection.write(('idstepper\n').encode())
responsefromaruino = arduinoconnection.readline()[:-1].decode('utf-8').rstrip()
while():
    if responsefromaruino == '1':
        print('Handshake with Arduino successful')
else:
    print('Hand shake unsuccessful')
    logger.warning("Arduino handshake unsuccessful")

We wan to report on how many steps are in sequence before we start so divide the counted characters to determine the length of the recorded sequence. In order to do so we needed to float the number first.

charactercount = (len(characterreplacefunc))      
charactercountfloated = charactercount

float(charactercountfloated)

dividedby3 = (charactercountfloated/3)

print('Recorded sequence has',dividedby3,'steps\n')

After we need split the strings into chunks allowing us to to make each chunk 4 characters long (exact length of our commands to the Arduino). We then send this to the iterator to spit out each command in between a time sleep function. This prevents the computer from sending all the data from the YAML file at once.

str = characterreplacefunc
n = 3
chunks = [str[i:i+n] for i in range(0, len(str), n)]

stepscounted = dividedby3

# converting the list to an iterator
iteratorloop = iter(chunks)
print('Sequence started')
logger.info('%s Step sequence started',stepscounted)

Here we are using iterate to cycle through all of the steps stored in YAML. I took a simple loop and set it to stop at however many steps are stored in the sequence files by counting the characters in that line. Then chopping the characters up by 4 (should do it by commas but whatever for now I guess).

a = b = 1
while (a<(dividedby3)+1):
    arduinoconnection.write(next(iteratorloop).encode())
    print('Step',a,'taken')
    logger.info('%s steps taken',a)
    time.sleep(0.5)
    a = a + 1
    b = b + 1
    if (b == dividedby3):
        ()
else:
    print('Sequence complete')
    logger.info('Sequence Complete')

Posted

in

by

Tags: