farmbot-serial 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,346 @@
1
+ ## HARDWARE INTERFACE
2
+ ## ******************
3
+
4
+ # Communicate with the arduino using a serial interface
5
+ # All information is exchanged using a variation of g-code
6
+ # Parameters are stored in the database
7
+ module Fb
8
+ class HardwareInterfaceArduino
9
+
10
+ attr_accessor :ramps_param, :ramps_main
11
+ #attr_accessor :test_serial_read, :test_serial_write
12
+ attr_accessor :serial_port
13
+ attr_accessor :external_info
14
+
15
+ # initialize the interface
16
+ #
17
+ def initialize(test_mode)
18
+ @status_debug_msg = $debug_msg
19
+
20
+ @test_mode = test_mode
21
+
22
+ # connect to arduino
23
+ connect_board()
24
+
25
+ @external_info = ""
26
+
27
+ end
28
+
29
+ ## ARDUINO HANLDING
30
+ ## ****************
31
+
32
+ # connect to the serial port and start communicating with the arduino/firmata protocol
33
+ #
34
+ def connect_board
35
+
36
+ parameters =
37
+ {
38
+ "baud" => 115200,
39
+ "data_bits" => 8,
40
+ "stop_bits" => 1,
41
+ "parity" => SerialPort::NONE,
42
+ "flow_control" => SerialPort::SOFT
43
+ }
44
+
45
+ comm_port = '/dev/ttyACM0'
46
+ @serial_port = SerialPort.new(comm_port, parameters) if @test_mode == false
47
+ @serial_port = SerialPortSim.new(comm_port, parameters) if @test_mode == true
48
+
49
+ end
50
+
51
+ # write a command to the robot
52
+ #
53
+ def execute_command(text, log, onscreen)
54
+ begin
55
+
56
+ write_status = create_write_status(text, log, onscreen)
57
+
58
+ #puts @serial_port.test_serial_read
59
+ prepare_serial_port(write_status)
60
+ #puts @serial_port.test_serial_read
61
+
62
+ # wait until the arduino responds
63
+ while(write_status.is_busy())
64
+
65
+ check_emergency_stop
66
+ process_feedback(write_status)
67
+
68
+ end
69
+
70
+ log_result_of_execution(write_status)
71
+
72
+ rescue => e
73
+ #puts 'exception received'
74
+ #puts e
75
+ handle_execution_exception(e)
76
+ end
77
+ end
78
+
79
+ def create_write_status(text, log, onscreen)
80
+ write_status = Fb::HardwareInterfaceArduinoWriteStatus.new
81
+ write_status.text = text
82
+ write_status.log = log
83
+
84
+ write_status.onscreen = onscreen
85
+ write_status
86
+ end
87
+
88
+ def handle_execution_exception(e)
89
+ puts("ST: serial error\n#{e.message}\n#{e.backtrace.inspect}")
90
+ @serial_port.rts = 1
91
+ connect_board
92
+ sleep 5 if @test_mode == false
93
+ end
94
+
95
+ def log_result_of_execution(write_status)
96
+
97
+ # log things if needed
98
+ if write_status.done == 1
99
+ puts 'ST: done' if write_status.onscreen
100
+ else
101
+ puts 'ST: timeout'
102
+ sleep 5 if @test_mode == false
103
+ end
104
+ end
105
+
106
+ # receive all characters coming from the serial port
107
+ #
108
+ def process_feedback(write_status)
109
+ i = @serial_port.read(1)
110
+
111
+ #print "-#{i}"
112
+ #print "#{i}"
113
+
114
+ if i != nil
115
+ i.each_char do |c|
116
+
117
+ add_and_process_characters(write_status, c)
118
+
119
+ end
120
+ else
121
+ sleep 0.001 if @test_mode == false
122
+ #write_status.done = 1 if @test_mode == true
123
+ end
124
+ end
125
+
126
+ # keep incoming characters in a buffer until it is a complete string
127
+ #
128
+ def add_and_process_characters(write_status, c)
129
+
130
+ if c == "\r" or c == "\n"
131
+ if write_status.received.length >= 3
132
+ log_incoming_text(write_status)
133
+ write_status.split_received
134
+ process_code_and_params(write_status)
135
+
136
+ end
137
+ else
138
+ write_status.received = write_status.received + c
139
+ end
140
+ end
141
+
142
+ # handle the incoming message depending on the first code number
143
+ #
144
+ def process_code_and_params(write_status)
145
+
146
+ # process the feedback
147
+ case write_status.code
148
+
149
+ # command received by arduino
150
+ when 'R01'
151
+ write_status.timeout = 90
152
+
153
+ # command is finished
154
+ when 'R02'
155
+ write_status.done = 1
156
+
157
+ # command is finished with errors
158
+ when 'R03'
159
+ write_status.done = 1
160
+
161
+ # command is still ongoing
162
+ when 'R04'
163
+ write_status.start = Time.now
164
+ write_status.timeout = 90
165
+
166
+ # specific feedback that is processes separately
167
+ else
168
+ process_value(write_status.code,write_status.params)
169
+ end
170
+
171
+ write_status.received = ''
172
+
173
+ end
174
+
175
+ # set the serial port ready to send
176
+ #
177
+ def prepare_serial_port(write_status)
178
+ puts "WR: #{write_status.text}" if write_status.onscreen
179
+ @serial_port.read_timeout = 2
180
+ clean_serial_buffer() if @test_mode == false
181
+ serial_port_write( "#{write_status.text}\n" )
182
+ end
183
+
184
+ # empty the input buffer so no old data is processed
185
+ #
186
+ def clean_serial_buffer
187
+ while (@serial_port.read(1) != nil)
188
+ end
189
+ end
190
+
191
+ # write something to the serial port
192
+ def serial_port_write(text)
193
+ @serial_port.write( text )
194
+ end
195
+
196
+ # if there is an emergency stop, immediately write it to the arduino
197
+ #
198
+ def check_emergency_stop
199
+ if (Fb::HardwareInterface.current.status.emergency_stop)
200
+ serial_port_write( "E\n" )
201
+ end
202
+ end
203
+
204
+ # write to log
205
+ #
206
+ def log_incoming_text(write_status)
207
+ puts "RD: #{write_status.received}" if write_status.onscreen
208
+ end
209
+
210
+ # process values received from arduino
211
+ #
212
+ def process_value(code,text)
213
+
214
+ params = Fb::HardwareInterfaceArduinoValuesReceived.new
215
+
216
+ process_value_split(code, params, text)
217
+
218
+ # depending on the report code, process the values
219
+ # this is done by reading parameter names and their values
220
+ # and respong on it as needed
221
+
222
+ process_value_process_param_list(params,code)
223
+ process_value_process_named_params(params,code)
224
+ process_value_process_text(code,text)
225
+
226
+ end
227
+
228
+ def process_value_split(code, params, text)
229
+
230
+ # get all separate parameters from the text
231
+ text.split(' ').each do |param|
232
+
233
+ if code == "R81"
234
+ # this is the only code that uses two letter parameters
235
+ par_code = param[0..1].to_s
236
+ par_value = param[2..-1].to_i
237
+ else
238
+ par_code = param[0..0].to_s
239
+ par_value = param[1..-1].to_i
240
+ end
241
+
242
+ params.load_parameter(par_code, par_value)
243
+
244
+ end
245
+
246
+ end
247
+
248
+ def process_value_process_param_list(params,code)
249
+ #if params.p != 0
250
+ process_value_R21(params,code)
251
+ process_value_R23(params,code)
252
+ process_value_R41(params,code)
253
+ #end
254
+ end
255
+
256
+
257
+ # Process report parameter value
258
+ #
259
+ def process_value_R21(params,code)
260
+ if code == 'R21'
261
+ param = @ramps_param.get_param_by_id(params.p)
262
+ if param != nil
263
+ param['value_ar'] = params.v
264
+ end
265
+ end
266
+ end
267
+
268
+ # Process report parameter value and save to database
269
+ #
270
+ def process_value_R23(params,code)
271
+ if code == 'R23'
272
+ param = @ramps_param.get_param_by_id(params.p)
273
+ if param != nil
274
+ @ramps_param.save_param_value(params.p, :by_id, :from_db, params.v)
275
+ end
276
+ end
277
+ end
278
+
279
+ # Process report pin values
280
+ #
281
+ def process_value_R41(params,code)
282
+ raise 'Not implemented.'
283
+ end
284
+
285
+ def process_value_process_named_params(params,code)
286
+ process_value_R81(params,code)
287
+ process_value_R82(params,code)
288
+ end
289
+
290
+ # Process report end stops
291
+ #
292
+ def process_value_R81(params,code)
293
+ if code == 'R81'
294
+ Fb::HardwareInterface.current.status.info_end_stop_x_a = (params.xa == 1)
295
+ Fb::HardwareInterface.current.status.info_end_stop_x_b = (params.xb == 1)
296
+ Fb::HardwareInterface.current.status.info_end_stop_y_a = (params.ya == 1)
297
+ Fb::HardwareInterface.current.status.info_end_stop_y_b = (params.yb == 1)
298
+ Fb::HardwareInterface.current.status.info_end_stop_z_a = (params.za == 1)
299
+ Fb::HardwareInterface.current.status.info_end_stop_z_b = (params.zb == 1)
300
+ end
301
+ end
302
+
303
+ # Process report position
304
+ def process_value_R82(params,code)
305
+ if code == 'R82'
306
+
307
+ Fb::HardwareInterface.current.status.info_current_x_steps = params.x
308
+ Fb::HardwareInterface.current.status.info_current_x = params.x / @ramps_param.axis_x_steps_per_unit
309
+
310
+ Fb::HardwareInterface.current.status.info_current_y_steps = params.y
311
+ Fb::HardwareInterface.current.status.info_current_y = params.y / @ramps_param.axis_y_steps_per_unit
312
+
313
+ Fb::HardwareInterface.current.status.info_current_z_steps = params.z
314
+ Fb::HardwareInterface.current.status.info_current_z = params.z / @ramps_param.axis_z_steps_per_unit
315
+
316
+ end
317
+ end
318
+
319
+ def process_value_process_text(code,text)
320
+ process_value_process_R83(code,text)
321
+ process_value_process_R99(code,text)
322
+ end
323
+
324
+ # Process report software version
325
+ #
326
+ def process_value_process_R83(code,text)
327
+ if code == 'R83'
328
+ Fb::HardwareInterface.current.status.device_version = text
329
+ end
330
+ end
331
+
332
+ # Process report of a debug comment
333
+ #
334
+ def process_value_process_R99(code,text)
335
+ if code == 'R99'
336
+ puts ">#{text}<"
337
+ end
338
+ end
339
+
340
+ def puts(*)
341
+ # Too many messages in the test buffer right now. Will delete this later.
342
+ # just disabling it for now.
343
+ STDOUT.print('╳')
344
+ end
345
+ end
346
+ end
@@ -0,0 +1,56 @@
1
+ module Fb
2
+ class HardwareInterfaceArduinoValuesReceived
3
+
4
+ attr_accessor :p , :v
5
+ attr_accessor :x , :y , :z
6
+ attr_accessor :xa, :xb
7
+ attr_accessor :ya, :yb
8
+ attr_accessor :za, :zb
9
+
10
+
11
+ def initialize
12
+
13
+ @p = -1
14
+ @v = 0
15
+ @x = 0
16
+ @y = 0
17
+ @z = 0
18
+ @xa = 0
19
+ @xb = 0
20
+ @ya = 0
21
+ @yb = 0
22
+ @za = 0
23
+ @zb = 0
24
+
25
+ end
26
+
27
+ def load_parameter(name, value)
28
+
29
+ case name
30
+ when 'P'
31
+ @p = value
32
+ when 'V'
33
+ @v = value
34
+ when 'XA'
35
+ @xa = value
36
+ when 'XB'
37
+ @xb = value
38
+ when 'YA'
39
+ @ya = value
40
+ when 'YB'
41
+ @yb = value
42
+ when 'ZA'
43
+ @za = value
44
+ when 'ZB'
45
+ @zb = value
46
+ when 'X'
47
+ @x = value
48
+ when 'Y'
49
+ @y = value
50
+ when 'Z'
51
+ @z = value
52
+ end
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,30 @@
1
+ module Fb
2
+ class HardwareInterfaceArduinoWriteStatus
3
+
4
+ attr_accessor :done, :code, :received, :start, :log, :onscreen, :text,
5
+ :params, :timeout
6
+
7
+ def initialize
8
+ @done = 0
9
+ @code = ''
10
+ @received = ''
11
+ @text = ''
12
+ @params = ''
13
+ @log = false
14
+ @onscreen = false
15
+ @start = Time.now
16
+ @timeout = 5
17
+ end
18
+
19
+ def is_busy
20
+ Time.now - @start < @timeout and @done == 0
21
+ end
22
+
23
+ def split_received
24
+ # get the parameter and data part
25
+ @code = received[0..2].upcase
26
+ @params = received[3..-1].to_s.upcase.strip
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,218 @@
1
+ ## HARDWARE INTERFACE
2
+ ## ******************
3
+
4
+ # Communicate with the arduino using a serial interface
5
+ # All information is exchanged using a variation of g-code
6
+ # Parameters are stored in the database
7
+ module Fb
8
+ class HardwareInterfaceParam
9
+
10
+ attr_accessor :params, :param_version_db, :param_version_ar, :params_in_sync,
11
+ :axis_x_steps_per_unit, :axis_y_steps_per_unit, :axis_z_steps_per_unit,
12
+ :ramps_arduino, :ramps_main
13
+
14
+
15
+ # initialize the interface
16
+ #
17
+ def initialize
18
+ @params = []
19
+ @external_info = ""
20
+ @param_version_db = 0
21
+ @param_version_ar = 0
22
+ @params_in_sync = false
23
+
24
+ @axis_x_steps_per_unit = 1
25
+ @axis_y_steps_per_unit = 1
26
+ @axis_z_steps_per_unit = 1
27
+
28
+ load_param_names()
29
+
30
+
31
+ load_param_values_non_arduino()
32
+
33
+ end
34
+
35
+ ## DATABASE AND SETTINGS HANDLING
36
+ ## ******************************
37
+
38
+ # load non-arduino parameters
39
+ #
40
+ def load_param_values_non_arduino
41
+
42
+ p = get_param_by_name('MOVEMENT_STEPS_PER_UNIT_X')
43
+ @axis_x_steps_per_unit = p['value_db']
44
+
45
+ p = get_param_by_name('MOVEMENT_STEPS_PER_UNIT_Y')
46
+ @axis_y_steps_per_unit = p['value_db']
47
+
48
+ p = get_param_by_name('MOVEMENT_STEPS_PER_UNIT_Z')
49
+ @axis_z_steps_per_unit = p['value_db']
50
+
51
+ end
52
+
53
+ # load the id's of the arduino parameters
54
+ #
55
+ def load_param_names
56
+ $arduino_default_params.each do |p|
57
+ param_name_add(p[:name], p[:id], p[:value])
58
+ end
59
+ end
60
+
61
+ # add a parameter to the param list
62
+ #
63
+ def param_name_add(name, id, default)
64
+ found = false
65
+ @params.each do |p|
66
+ if p['name'] == name
67
+ found = true
68
+ end
69
+ end
70
+
71
+ if found == false
72
+ param = Hash.new
73
+ param['name'] = name
74
+ param['id'] = id
75
+ param['value_db'] = 0
76
+ param['value_ar'] = 0
77
+ param['default'] = default
78
+ @params << param
79
+ end
80
+ end
81
+
82
+ # get the parameter object by name
83
+ #
84
+ def get_param_by_name(name)
85
+ param = nil
86
+ @params.each do |p|
87
+ if p['name'] == name
88
+ param = p
89
+ end
90
+ end
91
+ return param
92
+ end
93
+
94
+ # get the parameter object by id
95
+ #
96
+ def get_param_by_id(id)
97
+ param = nil
98
+ @params.each do |p|
99
+ if p['id'] == id
100
+ param = p
101
+ end
102
+ end
103
+ return param
104
+ end
105
+
106
+ # get parameter object by name or id
107
+ #
108
+ def get_param(name_or_id, by_name_or_id)
109
+ param = nil
110
+ @params.each do |p|
111
+ if (by_name_or_id == :by_id and p['id'] == name_or_id)
112
+ param = p
113
+ end
114
+ if (by_name_or_id == :by_name and p['name'] == name_or_id)
115
+ param = p
116
+ end
117
+ end
118
+ return param
119
+ end
120
+
121
+ # save parameter value to the database
122
+ #
123
+ def save_param_value(name_or_id, by_name_or_id, from_device_or_db, value)
124
+
125
+ param = get_param(name_or_id, by_name_or_id)
126
+
127
+ if param != nil and from_device_or_db == :from_device
128
+ param['value_ar'] = value
129
+ end
130
+ if param != nil and from_device_or_db == :from_db
131
+ param['value_db'] = value
132
+ end
133
+ end
134
+
135
+ # check to see of parameters in arduino are up to date
136
+ #
137
+ def check_parameters
138
+ update_param_version_ar()
139
+ compare_and_write_parameters()
140
+ end
141
+
142
+ def update_param_version_ar
143
+ # read the parameter version in the database and in the device
144
+ read_parameter_from_device(0)
145
+
146
+ params.each do |p|
147
+ if p['id'] == 0
148
+ @param_version_ar = p['value_ar']
149
+ end
150
+ end
151
+ end
152
+
153
+ def compare_and_write_parameters
154
+ # if the parameters in the device is different from the database parameter version
155
+ # read and compare each parameter and write to device is different
156
+
157
+ if @param_version_db != @param_version_ar
158
+
159
+ load_param_values_non_arduino()
160
+
161
+ if !parameters_different()
162
+ @params_in_sync = true
163
+ write_parameter_to_device(0, @param_version_db)
164
+ else
165
+ @params_in_sync = false
166
+ end
167
+ else
168
+ @params_in_sync = true
169
+ end
170
+ end
171
+
172
+ def parameters_different
173
+ differences_found_total = false
174
+ params.each do |p|
175
+ if p['id'] != 0
176
+ difference = check_and_write_parameter(p)
177
+ if difference then
178
+ @params_in_sync = false
179
+ differences_found_total = true
180
+ end
181
+ end
182
+ end
183
+ differences_found_total
184
+ end
185
+
186
+ # synchronise a parameter value
187
+ #
188
+ def check_and_write_parameter(param)
189
+
190
+ # read value from device and database
191
+ read_parameter_from_device(param['id'])
192
+
193
+ differences_found = false
194
+
195
+ # if the parameter value between device and database is different, write value to device
196
+ if param['value_db'] != param ['value_ar']
197
+ differences_found = true
198
+ write_parameter_to_device(param['id'],param['value_db'])
199
+ end
200
+
201
+ return differences_found
202
+
203
+ end
204
+
205
+ # read a parameter from arduino
206
+ #
207
+ def read_parameter_from_device(id)
208
+ @ramps_arduino.execute_command("F21 P#{id}", false, false)
209
+ end
210
+
211
+ # write a parameter value to arduino
212
+ #
213
+ def write_parameter_to_device(id, value)
214
+ @ramps_arduino.execute_command("F22 P#{id} V#{value}", false, false)
215
+ end
216
+
217
+ end
218
+ end