Brad Martin
Published © GPL3+

Stepino

Old linear stage + Old HDD case = Modern piece of test equipment

IntermediateShowcase (no instructions)3 hours576
Stepino

Things used in this project

Hardware components

Zaber Stage
I had an old, PS/2 stage that I could not find the drivers for - so I removed all the Zaber I/O and soldered onto the stepper and limit sensor wires.
×1
Seeeduino Lite
Seeeduino Lite
×1
Stepper motor driver board A4988
SparkFun Stepper motor driver board A4988
There are many choices... I got a cheap set of 5 on Amazon a while back
×1
60W PCIe 12V 5A Power Supply
Digilent 60W PCIe 12V 5A Power Supply
×1

Software apps and online services

Visual Studio 2017
Microsoft Visual Studio 2017
2019, actually with .NET Core 3.0
Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Schematics

How to control a stepper motor with A4988 driver and Arduino

https://www.makerguides.com/a4988-stepper-motor-driver-arduino-tutorial/

Code

Arduino Code

Arduino
// MOTOR SETUP
#include <AccelStepper.h>
#define dirPin 5
#define stepPin 4
#define motorInterfaceType 1
AccelStepper stepper = AccelStepper(motorInterfaceType, stepPin, dirPin);

// SPEEDS
int fast = 800;
int med = 400;
int slow = 100;

// HOMING
int sensor = 10;
int range = 4750;

// COMMS
long pos = 0;
bool CMD_G = false;

void setup()
{
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  establishContact();  // send a byte to establish contact until receiver responds
  Serial.println("HELLO");

  pinMode(sensor, INPUT_PULLUP); // INIT HALL SENSOR
  stepper.setMaxSpeed(1000); // MOTOR CONFIG
}

void loop()
{
  int bytes = Serial.available();
  if (bytes > 0) {
    int inByte = Serial.read();
    if (inByte == 72) // H
    {
      homeStage();
    }
    else if (inByte == 71) // G
    {
      CMD_G = true;
    }
    else if (CMD_G)
    {
      pos += (long(inByte - '0') * (pow(10, bytes)) + 1) / 10;
    }
  }
  else if (CMD_G)
  {
    moveStage();
    pos = 0;
    CMD_G = false;
  }
}

void establishContact() {
  while (Serial.available() <= 0) {
    Serial.println("CONNECTING...");
    delay(300);
  }
}

void homeStage()
{
  Serial.println("HOMING");
  
  int homing = 1;
  while (homing == 1)
  {
    stepper.setSpeed(-med);
    stepper.runSpeed();
    homing = digitalRead(10);
  }
  stepper.setCurrentPosition(0);

  while (stepper.currentPosition() != range / 2)
  {
    stepper.setSpeed(fast);
    stepper.runSpeed();
  }

  Serial.println("HOMED");
}

void moveStage()
{
  if (0 <= pos <= range)
  {
    int dir = 1;
    if (stepper.currentPosition() > pos)
    {
      dir = -1;
    }
    while (stepper.currentPosition() != pos)
    {
      stepper.setSpeed(dir * fast);
      stepper.runSpeed();
    }
  }

  Serial.println("OK");
}

Desktop App

C#
using System;
using System.Windows.Forms;
using System.IO.Ports;
using System.Drawing;

delegate void StringArgReturningVoidDelegate(string text);

namespace Stepino
{
    public partial class FormMain : Form
    {
        private SerialPort _Port;

        public FormMain()
        {
            InitializeComponent();
            FormClosed += new FormClosedEventHandler(Form1_FormClosed);

            _Port = new SerialPort
            {
                BaudRate = 9600,
                DataBits = 8,
                DiscardNull = false,
                DtrEnable = false,
                RtsEnable = true,
                Handshake = Handshake.None,
                ReadBufferSize = 4096,
                ReadTimeout = -1,
                WriteTimeout = -1,
                StopBits = StopBits.One
            };

            _Port.DataReceived += _Port_DataReceived;
        }

        void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            _Port.Close();
        }

        private void btnHome_Click(object sender, EventArgs e)
        {
            _Port.Write("H");
            InMotion(true);
        }

        private void btnGo_Click(object sender, EventArgs e)
        {
            statusLabel.Text = "";
            InMotion(true);
            _Port.Write("G" + numericUpDown1.Value.ToString());
        }

        private void InMotion(bool moving)
        {
            gbxMotion.Enabled = !moving;
        }

        private void btnScan_Click(object sender, EventArgs e)
        {
            portComboBox.Items.Clear();
            foreach (string s in SerialPort.GetPortNames())
            {
                portComboBox.Items.Add(s);
            }
            portComboBox.SelectedIndex = portComboBox.Items.Count - 1;
            _Port.PortName = portComboBox.Text;
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            try
            {
                statusLabel.Text = "";
                _Port.Open();
                System.Threading.Thread.Sleep(100);
                _Port.Write(" ");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void btnDisconnect_Click(object sender, EventArgs e)
        {
            statusLabel.Text = "";
            _Port.Close();
        }

        private void portComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (_Port.IsOpen)
                return;

            _Port.PortName = portComboBox.Text;
        }

        private void _Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            SerialPort serialPort = (SerialPort)sender;
            string msg;
            msg = serialPort.ReadLine();
            MsgIn(msg);
        }

        private void MsgIn(string text)
        {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (statusStrip.InvokeRequired)
            {
                StringArgReturningVoidDelegate d = new StringArgReturningVoidDelegate(MsgIn);
                Invoke(d, new object[] { text });
            }
            else
            {
                statusLabel.Text = text.Trim();
                if (text.Contains("OK") || text.Contains("HOMED"))
                {
                    InMotion(false);

                    if (text.Contains("HOMED"))
                        btnHome.BackColor = Color.LawnGreen;
                }
            }
        }
    }
}

Stepino

All the C++ and C# code is here

Credits

Brad Martin

Brad Martin

3 projects • 3 followers
I like making gizmos

Comments

Add projectSign up / Login