Skip to content

Commit 83ae55a

Browse files
authored
Merge pull request #52 from flynneva/devel
added stream gcode function
2 parents d0f5325 + 8d1126b commit 83ae55a

File tree

6 files changed

+116
-130
lines changed

6 files changed

+116
-130
lines changed

grbl_ros/__init__.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,17 @@
2828
# Future implementations might include control for other
2929
# GCODE compatible systems
3030

31+
from ._logging import MODE
32+
3133

3234
class grbl:
3335

36+
from ._logging import getStatus, decodeStatus, STATUS, MODE
37+
from ._configure import getPose, setSpeed, setOrigin, ensureMovementMode, \
38+
blockUntilIdle, clearAlarm, enableSteppers, disableSteppers
39+
from ._control import home, moveTo, moveRel, moveToOrigin
40+
from ._command import startup, shutdown, gcode, stream
41+
3442
def __init__(self):
3543
"""
3644
Summary line.
@@ -39,6 +47,7 @@ def __init__(self):
3947
4048
"""
4149
# Default parameter values set in startup
50+
self.mode = MODE.NORMAL
4251
self.s = None # serial port object
4352
self.abs_move = None # GRBL has 2 movement modes: relative and absolute
4453
self.baudrate = 0
@@ -59,9 +68,3 @@ def __init__(self):
5968
self.angular = [0.0, 0.0, 0.0, 0.0] # quaterion [X, Y, Z, W]
6069
self.origin = [0.0, 0.0, 0.0] # minimum coordinates [X, Y, Z]
6170
self.limits = [0.0, 0.0, 0.0] # maximum coordinates [X, Y, Z]
62-
63-
# Import Methods
64-
from ._logging import getStatus, decodeStatus, STATUS
65-
from ._configure import getPose, setSpeed, setOrigin, ensureMovementMode, blockUntilIdle, \
66-
clearAlarm, enableSteppers, disableSteppers
67-
from ._control import home, moveTo, moveRel, moveToOrigin

grbl_ros/_command.py

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,23 @@ def startup(self, port, baud, acc, maxx, maxy, maxz,
6060
self.y_steps_mm = stepsy
6161
self.z_steps_mm = stepsz
6262
self.limits = [self.x_max, self.y_max, self.z_max]
63-
# initiates the serial port
64-
self.s = serial.Serial(self.port, self.baudrate)
6563

66-
# set movement to Absolute coordinates
67-
self.ensureMovementMode(True)
68-
# start homing procedure
69-
# TODO(flynneva): should this be done at startup?
70-
# should probably be configurable by user
71-
# self.home()
72-
# TODO(flynneva): could cause issues if user is resuming a job
73-
# set the current position as the origin (GRBL sometimes starts with z not 0)
74-
# self.setOrigin()
64+
# initiates the serial port
65+
try:
66+
self.s = serial.Serial(self.port, self.baudrate)
67+
# set movement to Absolute coordinates
68+
self.ensureMovementMode(True)
69+
# start homing procedure
70+
# TODO(flynneva): should this be done at startup?
71+
# should probably be configurable by user
72+
# self.home()
73+
# TODO(flynneva): could cause issues if user is resuming a job
74+
# set the current position as the origin (GRBL sometimes starts with z not 0)
75+
# self.setOrigin()
76+
except serial.serialutil.SerialException:
77+
# Could not detect GRBL device on serial port ' + self.port
78+
# Are you sure the GRBL device is connected and powered on?
79+
return
7580

7681

7782
def shutdown(self):
@@ -80,6 +85,23 @@ def shutdown(self):
8085

8186

8287
def gcode(self, gcode):
83-
# TODO(evanflynn): need to add some input checking
84-
self.s.write(str.encode(gcode + '\r\n'))
85-
return self.decodeStatus(self.s.readline().decode('utf-8').strip())
88+
# TODO(evanflynn): need to add some input checking to make sure its valid GCODE
89+
if(len(gcode) > 0):
90+
if(self.mode == self.MODE.NORMAL):
91+
self.s.write(str.encode(gcode + '\r\n'))
92+
return self.decodeStatus(self.s.readline().decode('utf-8').strip())
93+
elif(self.mode == self.MODE.DEBUG):
94+
# in debug mode just return the GCODE that was input
95+
return 'Sent: ' + gcode
96+
else:
97+
return 'Input GCODE was blank'
98+
99+
100+
def stream(self, gcode_fpath):
101+
f = open(gcode_fpath, 'r')
102+
103+
for raw_line in f:
104+
line = raw_line.strip() # strip all EOL characters for consistency
105+
status = gcode(self, line)
106+
if(self.mode == self.MODE.DEBUG):
107+
print(' ' + status)

grbl_ros/_logging.py

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,23 @@
2424

2525
def getStatus(self):
2626
# TODO(evanflynn): status should be ROS msg?
27-
self.s.write(b'?\n')
28-
time.sleep(0.1)
29-
buffer_status = []
30-
status = ''
31-
while True:
32-
if (self.s.inWaiting()):
33-
status = self.decodeStatus(self.s.readline().decode('utf-8').strip())
34-
buffer_status.append(status)
35-
status = ''
36-
else:
37-
self.s.flushInput()
38-
return buffer_status
27+
if(self.mode == self.MODE.NORMAL):
28+
self.s.write(b'?\n')
29+
time.sleep(0.1)
30+
buffer_status = []
31+
status = ''
32+
while True:
33+
if (self.s.inWaiting()):
34+
status = self.decodeStatus(self.s.readline().decode('utf-8').strip())
35+
buffer_status.append(status)
36+
status = ''
37+
else:
38+
self.s.flushInput()
39+
return buffer_status
40+
elif(self.mode == self.MODE.DEBUG):
41+
return 'DEBUG GRBL device is happy!'
42+
else:
43+
return 'UNDEFINED GRBL MODE'
3944

4045

4146
def decodeStatus(self, status):
@@ -44,6 +49,12 @@ def decodeStatus(self, status):
4449
return status
4550

4651

52+
class MODE(Enum):
53+
# enum class for operation modes helpful for debugging
54+
NORMAL = 0
55+
DEBUG = 1
56+
57+
4758
class STATUS(Enum):
4859
# enum class for grbl error list
4960
# http://domoticx.com/cnc-machine-grbl-error-list/

grbl_ros/grbl_device.py

Lines changed: 0 additions & 85 deletions
This file was deleted.

grbl_ros/node.py

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,20 @@ class grbl_node(Node):
3636

3737
def __init__(self):
3838
super().__init__(grbl_node_name)
39+
self.get_logger().info('Initializing Publishers & Subscribers')
3940
self.pub_pos_ = self.create_publisher(Pose, grbl_node_name + '/position', 10)
4041
self.pub_status_ = self.create_publisher(String, grbl_node_name + '/status', 10)
4142
self.sub_cmd_ = self.create_subscription(
4243
String, grbl_node_name + '/send_gcode', self.gcodeCallback, 10)
44+
self.sub_stream_ = self.create_subscription(
45+
String, grbl_node_name + '/send_gcode_file', self.streamCallback, 10)
4346
self.sub_pose_ = self.create_subscription(
4447
Pose, grbl_node_name + '/set_pose', self.poseCallback, 10)
4548
self.sub_stop_ = self.create_subscription(
4649
String, grbl_node_name + '/stop', self.stopCallback, 10)
4750
self.sub_stop_ # prevent unused variable warning
4851

49-
# declare parameters
52+
self.get_logger().info('Declaring ROS parameters')
5053
self.declare_parameter('port', '/dev/ttyUSB0')
5154
self.declare_parameter('baudrate', 115200)
5255
self.declare_parameter('acceleration', 1000)
@@ -61,6 +64,7 @@ def __init__(self):
6164
self.declare_parameter('y_steps_mm', 100)
6265
self.declare_parameter('z_steps_mm', 100)
6366

67+
self.get_logger().info('Setting ROS parameters')
6468
port = self.get_parameter('port')
6569
baud = self.get_parameter('baudrate')
6670
acc = self.get_parameter('acceleration') # axis acceleration (mm/s^2)
@@ -75,7 +79,9 @@ def __init__(self):
7579
steps_y = self.get_parameter('y_steps_mm') # axis steps per mm
7680
steps_z = self.get_parameter('z_steps_mm') # axis steps per mm
7781

82+
self.get_logger().info('Initializing GRBL Device')
7883
self.grbl_obj = grbl()
84+
self.get_logger().info('Starting up GRBL Device...')
7985
self.grbl_obj.startup(port.get_parameter_value().string_value,
8086
baud.get_parameter_value().integer_value,
8187
acc.get_parameter_value().integer_value,
@@ -89,9 +95,21 @@ def __init__(self):
8995
steps_x.get_parameter_value().integer_value,
9096
steps_y.get_parameter_value().integer_value,
9197
steps_z.get_parameter_value().integer_value)
92-
self.wake()
93-
self.refreshStatus()
94-
self.refreshPosition()
98+
if(self.grbl_obj.s):
99+
self.wake()
100+
self.refreshStatus()
101+
self.refreshPosition()
102+
self.get_logger().info('GRBL device ready')
103+
104+
else:
105+
self.get_logger().warn('Could not detect GRBL device '
106+
'on serial port ' + self.grbl_obj.port)
107+
self.get_logger().warn('Are you sure the GRBL device '
108+
'is connected and powered on?')
109+
# TODO(evanflynn): set this to a different color so it stands out?
110+
self.get_logger().info('Node running in `debug` mode')
111+
self.get_logger().info('GRBL device operation may not function as expected')
112+
self.grbl_obj.mode = self.grbl_obj.MODE.DEBUG
95113

96114
def wake(self):
97115
self.grbl_obj.s.write(b'\r\n\r\n')
@@ -100,13 +118,19 @@ def wake(self):
100118

101119
def refreshStatus(self):
102120
status = self.grbl_obj.getStatus()
103-
for stat in status:
104-
self.get_logger().info(stat)
105-
if ('error' in stat):
106-
# TODO(evanflynn): temp code to clear alarm error, should be user decision
107-
self.get_logger().warn(self.grbl_obj.clearAlarm())
121+
if(self.grbl_obj.mode == self.grbl_obj.MODE.NORMAL):
122+
for stat in status:
123+
self.get_logger().info(stat)
124+
if ('error' in stat):
125+
# TODO(evanflynn): temp code to clear alarm error, should be user decision
126+
self.get_logger().warn(self.grbl_obj.clearAlarm())
127+
ros_status = String()
128+
ros_status.data = stat
129+
self.pub_status_.publish(ros_status)
130+
else:
131+
self.get_logger().info(status)
108132
ros_status = String()
109-
ros_status.data = stat
133+
ros_status.data = status
110134
self.pub_status_.publish(ros_status)
111135

112136
def refreshPosition(self):
@@ -137,11 +161,22 @@ def stopCallback(self, msg):
137161
elif msg.data == 'f':
138162
self.grbl_obj.enableSteppers()
139163

164+
def streamCallback(self, msg):
165+
self.get_logger().info('Sending GCODE file: ')
166+
self.get_logger().info(' ' + msg.data)
167+
# stream gcode file to grbl device
168+
status = self.grbl_obj.stream(msg.data)
169+
# TODO(evanflynn): have stream method return something useful
170+
self.get_logger().info(status)
171+
self.get_logger().info('GCODE file complete!')
172+
self.refreshStatus()
173+
self.refreshPosition()
174+
140175

141176
def main():
142177
rclpy.init()
143-
interface = grbl()
144-
rclpy.spin(interface)
178+
node = grbl_node()
179+
rclpy.spin(node)
145180

146181

147182
if __name__ == '__main__':

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
tests_require=['pytest'],
2727
entry_points={
2828
'console_scripts': [
29-
'interface = grbl_ros.node:main'
29+
'grbl_node = grbl_ros.node:main'
3030
],
3131
},
3232
)

0 commit comments

Comments
 (0)