blob: 4a637ab975c3689a3e8a962012fedace14cb5eab [file] [log] [blame]
Luigi Santivetti69972f92019-11-12 22:55:40 +00001#!/usr/bin/env python
2"""\
3
4Stream g-code to grbl controller
5
6This script differs from the simple_stream.py script by
7tracking the number of characters in grbl's serial read
8buffer. This allows grbl to fetch the next line directly
9from the serial buffer and does not have to wait for a
10response from the computer. This effectively adds another
11buffer layer to prevent buffer starvation.
12
13CHANGELOG:
14- 20170531: Status report feedback at 1.0 second intervals.
15 Configurable baudrate and report intervals. Bug fixes.
16- 20161212: Added push message feedback for simple streaming
17- 20140714: Updated baud rate to 115200. Added a settings
18 write mode via simple streaming method. MIT-licensed.
19
20TODO:
21- Add realtime control commands during streaming.
22
23---------------------
24The MIT License (MIT)
25
26Copyright (c) 2012-2017 Sungeun K. Jeon
27
28Permission is hereby granted, free of charge, to any person obtaining a copy
29of this software and associated documentation files (the "Software"), to deal
30in the Software without restriction, including without limitation the rights
31to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
32copies of the Software, and to permit persons to whom the Software is
33furnished to do so, subject to the following conditions:
34
35The above copyright notice and this permission notice shall be included in
36all copies or substantial portions of the Software.
37
38THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
39IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
40FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
41AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
42LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
43OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
44THE SOFTWARE.
45---------------------
46"""
47
48import serial
49import re
50import time
51import sys
52import argparse
53import threading
54
55RX_BUFFER_SIZE = 128
56BAUD_RATE = 115200
57ENABLE_STATUS_REPORTS = True
58REPORT_INTERVAL = 1.0 # seconds
59
60is_run = True # Controls query timer
61
62# Define command line argument interface
63parser = argparse.ArgumentParser(description='Stream g-code file to grbl. (pySerial and argparse libraries required)')
64parser.add_argument('gcode_file', type=argparse.FileType('r'),
65 help='g-code filename to be streamed')
66parser.add_argument('device_file',
67 help='serial device path')
68parser.add_argument('-q','--quiet',action='store_true', default=False,
69 help='suppress output text')
70parser.add_argument('-s','--settings',action='store_true', default=False,
71 help='settings write mode')
72parser.add_argument('-c','--check',action='store_true', default=False,
73 help='stream in check mode')
74args = parser.parse_args()
75
76# Periodic timer to query for status reports
77# TODO: Need to track down why this doesn't restart consistently before a release.
78def send_status_query():
79 s.write('?')
80
81def periodic_timer() :
82 while is_run:
83 send_status_query()
84 time.sleep(REPORT_INTERVAL)
85
86
87# Initialize
88s = serial.Serial(args.device_file,BAUD_RATE)
89f = args.gcode_file
90verbose = True
91if args.quiet : verbose = False
92settings_mode = False
93if args.settings : settings_mode = True
94check_mode = False
95if args.check : check_mode = True
96
97# Wake up grbl
98print "Initializing Grbl..."
99s.write("\r\n\r\n")
100
101# Wait for grbl to initialize and flush startup text in serial input
102time.sleep(2)
103s.flushInput()
104
105if check_mode :
106 print "Enabling Grbl Check-Mode: SND: [$C]",
107 s.write("$C\n")
108 while 1:
109 grbl_out = s.readline().strip() # Wait for grbl response with carriage return
110 if grbl_out.find('error') >= 0 :
111 print "REC:",grbl_out
112 print " Failed to set Grbl check-mode. Aborting..."
113 quit()
114 elif grbl_out.find('ok') >= 0 :
115 if verbose: print 'REC:',grbl_out
116 break
117
118start_time = time.time();
119
120# Start status report periodic timer
121if ENABLE_STATUS_REPORTS :
122 timerThread = threading.Thread(target=periodic_timer)
123 timerThread.daemon = True
124 timerThread.start()
125
126# Stream g-code to grbl
127l_count = 0
128error_count = 0
129if settings_mode:
130 # Send settings file via simple call-response streaming method. Settings must be streamed
131 # in this manner since the EEPROM accessing cycles shut-off the serial interrupt.
132 print "SETTINGS MODE: Streaming", args.gcode_file.name, " to ", args.device_file
133 for line in f:
134 l_count += 1 # Iterate line counter
135 # l_block = re.sub('\s|\(.*?\)','',line).upper() # Strip comments/spaces/new line and capitalize
136 l_block = line.strip() # Strip all EOL characters for consistency
137 if verbose: print "SND>"+str(l_count)+": \"" + l_block + "\""
138 s.write(l_block + '\n') # Send g-code block to grbl
139 while 1:
140 grbl_out = s.readline().strip() # Wait for grbl response with carriage return
141 if grbl_out.find('ok') >= 0 :
142 if verbose: print " REC<"+str(l_count)+": \""+grbl_out+"\""
143 break
144 elif grbl_out.find('error') >= 0 :
145 if verbose: print " REC<"+str(l_count)+": \""+grbl_out+"\""
146 error_count += 1
147 break
148 else:
149 print " MSG: \""+grbl_out+"\""
150else:
151 # Send g-code program via a more agressive streaming protocol that forces characters into
152 # Grbl's serial read buffer to ensure Grbl has immediate access to the next g-code command
153 # rather than wait for the call-response serial protocol to finish. This is done by careful
154 # counting of the number of characters sent by the streamer to Grbl and tracking Grbl's
155 # responses, such that we never overflow Grbl's serial read buffer.
156 g_count = 0
157 c_line = []
158 for line in f:
159 l_count += 1 # Iterate line counter
160 l_block = re.sub('\s|\(.*?\)','',line).upper() # Strip comments/spaces/new line and capitalize
161 # l_block = line.strip()
162 c_line.append(len(l_block)+1) # Track number of characters in grbl serial read buffer
163 grbl_out = ''
164 while sum(c_line) >= RX_BUFFER_SIZE-1 | s.inWaiting() :
165 out_temp = s.readline().strip() # Wait for grbl response
166 if out_temp.find('ok') < 0 and out_temp.find('error') < 0 :
167 print " MSG: \""+out_temp+"\"" # Debug response
168 else :
169 if out_temp.find('error') >= 0 : error_count += 1
170 g_count += 1 # Iterate g-code counter
171 if verbose: print " REC<"+str(g_count)+": \""+out_temp+"\""
172 del c_line[0] # Delete the block character count corresponding to the last 'ok'
173 s.write(l_block + '\n') # Send g-code block to grbl
174 if verbose: print "SND>"+str(l_count)+": \"" + l_block + "\""
175 # Wait until all responses have been received.
176 while l_count > g_count :
177 out_temp = s.readline().strip() # Wait for grbl response
178 if out_temp.find('ok') < 0 and out_temp.find('error') < 0 :
179 print " MSG: \""+out_temp+"\"" # Debug response
180 else :
181 if out_temp.find('error') >= 0 : error_count += 1
182 g_count += 1 # Iterate g-code counter
183 del c_line[0] # Delete the block character count corresponding to the last 'ok'
184 if verbose: print " REC<"+str(g_count)+": \""+out_temp + "\""
185
186# Wait for user input after streaming is completed
187print "\nG-code streaming finished!"
188end_time = time.time();
189is_run = False;
190print " Time elapsed: ",end_time-start_time,"\n"
191if check_mode :
192 if error_count > 0 :
193 print "CHECK FAILED:",error_count,"errors found! See output for details.\n"
194 else :
195 print "CHECK PASSED: No errors found in g-code program.\n"
196else :
197 print "WARNING: Wait until Grbl completes buffered g-code blocks before exiting."
198 raw_input(" Press <Enter> to exit and disable Grbl.")
199
200# Close file and serial port
201f.close()
202s.close()