Misfittech / nano_stepper

Stepper feedback controller
424 stars 179 forks source link

Just for info: easily read/write from/to Smart Stepper by raspberry pi/bash #54

Open vmario89 opened 4 years ago

vmario89 commented 4 years ago

Hi,

just wired up some bash stuff to operate Smart Steppers with scripts utilizing screen

apt install screen
#!/bin/bash
SCREEN_NAME="screen_smart_stepper"
LOG_FILE="/opt/${SCREEN_NAME}.log"

# remove old log file if existent
rm "${LOG_FILE}" > /dev/null 2>&1

# send "smart stepper command + enter (^M) keystroke" to first window (-p 0 could be applied) in a new named screen by stuffing (https://www.gnu.org/software/screen/manual/html_node/Command-Summary.html) the string into input; log the output to file
screen -d -m -L ${LOG_FILE} -S ${SCREEN_NAME} /dev/ttyUSB-SMARTSTEPPER 115200

declare -a cmdArray=("getcal" "microsteps" "readpos" "encoderdiag" "spid" "vpid" "ppid" "dirpin" "enablepinmode" "errorlimit" "ctrlmode" "maxcurrent" "holdcurrent" "homecurrent" "motorwiring" "stepsperrotation" "velocity" "looptime" "eepromerror" "eepromloc" "geterror" "getsteps" "torque")
for val in ${cmdArray[@]}; do
        screen -S ${SCREEN_NAME} -X stuff "${val}^M"
done

sleep 1
screen -S ${SCREEN_NAME} -X quit

# clean up the log with ^M symbols
sed -i 's/\r//g' ${LOG_FILE}

it looks easy but it was torture to find out how to communicate best without bugging the complete USB buffer or change stty settings. Maybe someone has smarter solution. That one works and might be a starting point.

same script can be used as basis to send commands or to build up some cyclic things.

i am using it for Hangprinter project to read encoder data without I2C/Two Wire interface, because there's no Duet support yet. And i dont want to mess around with firmware mods.

cheers, Mario

vmario89 commented 4 years ago

Some weeks later: i made a stable Python 3.7 script using PySerial library for polling out data. The following example code grabs ctrlmode and cuts out the mode number of the read line.

This script can be used to do things like sending values into an InfluxDB or whatever. It's stable enough to survive removed USB device, powered off Smart Stepper and other errors. I also configured it to set USB port to exclusive so no other connection can mess with the established one.

import serial
import time
import termios # workaround for IOError on resetting input/output buffers
import re

# note that when defining serial.Serial will automatically open the port!
ser=None
def createSerial():
        global ser
        time.sleep(1) # always wait one second before re-opening. This prevents hiccups
        try:
                #print("trying to open serial connection")
                ser=serial.Serial(
                        port="/dev/ttyUSB-SMART-A-AXIS",
                        baudrate=115200,
                        bytesize=serial.EIGHTBITS,
                        parity=serial.PARITY_NONE,
                        stopbits=serial.STOPBITS_ONE,
                        timeout=1,
                        xonxoff=False,
                        rtscts=False,
                        write_timeout=1,
                        dsrdtr=False,
                        inter_byte_timeout=None,
                        exclusive=True) # force to get behaviour "Cannot open line '/dev/ttyUSB-SMART-A-AXIS' for R/W: open() blocked, aborted." or "Error: read failed: device reports readiness to read but returned no data (device disconnected or multiple access on port?)"

        except IOError as e: # the serial creation may fail if device not connected or not ready
                print("Error:", e)
                pass
while True:
        if ser == None:
                #print("serial is set to None. Creating new serial connection")
                createSerial()
        else:
                try:
                        ser.reset_input_buffer()
                        ser.reset_output_buffer()
                        ser.write("ctrlmode".encode() + b'\r')
                        #time.sleep(0.05)
                        while ser.inWaiting() > 0:
                                line=ser.readline().decode()
                                #print(line)
                                p=re.compile(r'^controller\s.*\(\d\)$', re.M)
                                m=p.search(line)
                                if m:
                                        print("ctrlmode =",m.group(0)[-2]) #extract the number from "controller <modeString> (<number>)"
                except (IOError, termios.error) as e: # in case the USB device was removed or buffer reset fails try to close and re-open serial port
                        print("Error:", e)
                        ser.close()
                        ser = None #unset
                        createSerial()
                        pass