Digital I/O

cancel
Showing results for 
Search instead for 
Did you mean: 

Control a stepper motor using nidaqmx and digital output in Python

Solved!
Go to solution

Hello,

I have a problem using nidaqmx digital output to control a stepper motor in Python. I have read through the Python documentation (http://nidaqmx-python.readthedocs.io/en/latest/index.html ) and this example (https://github.com/ni/nidaqmx-python/blob/master/nidaqmx_examples/do_sw_timed.py) already, but I am running into issues getting the stepper motor to rotate. So far it seems like the digital output is being sent to the motor, but the motor is only vibrating and not rotating.

 

System:

Windows 7, Python 3.7, NI USB-6009

 

The code:

import nidaqmx
import time
from nidaqmx.constants import (
    LineGrouping)

with nidaqmx.Task() as task:
	task.do_channels.add_do_chan('Dev1/port1/line0:3')
	task.start()
	while True:
		task.write([8,0,0,0])
		time.sleep(.002)
		task.write([0,8,0,0])
		time.sleep(.002)
		task.write([0,0,8,0])
		time.sleep(.002)
		task.write([0,0,0,8])
		time.sleep(.002)

 When I run the code, the stepper motor will vibrate but it will not rotate like I want it to. Any suggestions to fix this problem? I have already tried changing the length of the pause from .002 to .02 to .2 (and values in between). I have also tried writing different values of bits such as [8,0,8,0] and [0,8,0,8]. Changing the length of the pause will change how frequently it vibrates, but I cannot get the motor to actually rotate. I assume it is an issue with what bits I am writing to the task, because the digital output works.

 

For reference, here is a version of the code in matlab that works like it is supposed to on the same computer/system:

 

clear all
close all
clc
pt=2/100;
s=daq.createSession('ni')
%%
addDigitalChannel(s,'Dev1','Port1/Line0','OutputOnly');
addDigitalChannel(s,'Dev1','Port1/Line1','OutputOnly');
addDigitalChannel(s,'Dev1','Port1/Line2','OutputOnly');
addDigitalChannel(s,'Dev1','Port1/Line3','OutputOnly');
n=0
while(n<100)
outputSingleScan(s,[1 0 0 0]);
pause(pt)
outputSingleScan(s,[0 1 0 0]);
pause(pt)
outputSingleScan(s,[0 0 1 0]);
pause(pt)
outputSingleScan(s,[0 0 0 1]);
pause(pt)
n=n+1
str='loop'
end
clear all
close all

Thanks in advance, any advice is appreciated.

0 Kudos
Message 1 of 3
(3,922 Views)
Solution
Accepted by topic author gobears1

Hi GoBears1, 

 

So glad to see you are using the nidaqmx Python module. I love playing around with it. Let me take a shot at explaining what could be going on even though I am not sure how you are supposed to control the stepper motor. 

 

So task.write([8,0,0,0]) will write multiple samples to the DAQ device and return after generating all the samples. The nidaqmx documentation says, "If the task uses on-demand timing, this method returns only after the device generates all samples." The example you provided shows that the write method returns the integer four, meaning four samples were written, and it requires autostart to be set to true or the task to be started for the write to work. You can write to tasks before starting them when you have timing configured, but you can't do waveform generation with the USB-6009. I am not sure what it means to write multiple samples to a task which uses on-demand timing. With this type of timing, you normally write to the channel when you want to update it. I imagine it runs through the samples very quickly and holds the last value written to it. For the first array, there will be a high on line three and then a low on all four lines. Essentially the same thing will happen for the other arrays except the last one, which may hold high on line three until the sleep is over. 

 

If I had to rewrite the Python code to mimic the other code, I would do something like this. 

 

import nidaqmx
import time

pt = 2/100
with nidaqmx.Task() as task:
    task.do_channels.add_do_chan('Dev1/port1/line0:3')
    n = 0
    task.start() #This should be optional
    while n < 100 :
        task.write(1)
        time.sleep(pt)
        task.write(2)
        time.sleep(pt)
        task.write(4)
        time.sleep(pt)
        task.write(8)
        time.sleep(pt)
        n = n + 1

 

I am more confident that the integer write will work than any other method. I would think that [True, False, True, False] would write a boolean value to each line like it does in LabVIEW, but running the example code indicates that it is treated as four samples instead of one to each line. Let me know if you are still having issues, and I can try to replicate it myself as I used simulated hardware to test everything. You can also route the output into an input to test the code for the desired behavior.  

 

Best,

 

David F.
Technical Support Engineer
National Instruments
www.ni.com/support

 

 

Message 2 of 3
(3,874 Views)

I got the code to work in the pretty much the original form. The correct task.do_channels.add_do_chan() declaration is: task.do_channels.add_do_chan("Dev1/port1/line0:3", line_grouping=LineGrouping.CHAN_PER_LINE), the" line_grouping=LineGrouping.CHAN_PER_LINE" was missing. After adding the missing declaration, the booleans worked fine. Verified on a NI USB-6501 with an oscilloscope. 

 

Full code:

 

#Imports

import nidaqmx
import time
import msvcrt

 

from nidaqmx.constants import LineGrouping

 

#Stepper Motor Control

with nidaqmx.Task() as task:
task.do_channels.add_do_chan("Dev1/port1/line0:3", line_grouping=LineGrouping.CHAN_PER_LINE)
task.start()
while True:
    task.write([True,False,False,False])
    time.sleep(.05)
    task.write([False,True,False,False])
    time.sleep(.05)
    task.write([False,False,True,False])
    time.sleep(.05)
    task.write([False,False,False,True])
    time.sleep(.05)

     if msvcrt.kbhit(): # Check the keyboard and exit if any key pressed.
         key_stroke = msvcrt.getch()
         #print(key_stroke) # will print which key is pressed.
         if key_stroke:
             break

0 Kudos
Message 3 of 3
(2,155 Views)