farmbot-serial 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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