domotics-arduino 0.0.2 → 0.0.3
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.
- data/Rakefile +13 -0
- data/domotics-arduino.gemspec +3 -1
- data/lib/domotics/arduino.rb +1 -9
- data/lib/domotics/arduino/arduino_serial.rb +67 -57
- data/lib/domotics/arduino/digital_pin.rb +5 -0
- data/lib/domotics/arduino/pwm_pin.rb +3 -0
- data/lib/domotics/arduino/version.rb +1 -1
- data/test/board_emulator.rb +26 -0
- data/test/test_arduino.rb +21 -0
- metadata +22 -2
data/Rakefile
CHANGED
data/domotics-arduino.gemspec
CHANGED
@@ -17,7 +17,9 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency "serialport"
|
20
22
|
|
21
23
|
spec.add_development_dependency "bundler", "~> 1.3"
|
22
24
|
spec.add_development_dependency "rake"
|
23
|
-
end
|
25
|
+
end
|
data/lib/domotics/arduino.rb
CHANGED
@@ -1,9 +1 @@
|
|
1
|
-
require
|
2
|
-
require "thread"
|
3
|
-
Dir[File.dirname(__FILE__) + '/arduino/**/*.rb'].sort.each {|file| require file}
|
4
|
-
|
5
|
-
#module Domotics
|
6
|
-
# module Arduino
|
7
|
-
# # Your code goes here...
|
8
|
-
# end
|
9
|
-
#end
|
1
|
+
Dir[File.dirname(__FILE__) + '/arduino/**/*.rb'].sort.each {|file| require file}
|
@@ -1,9 +1,14 @@
|
|
1
|
+
require 'serialport'
|
2
|
+
require 'thread'
|
3
|
+
require 'logger'
|
4
|
+
require 'timeout'
|
5
|
+
|
1
6
|
module Domotics
|
2
7
|
module Arduino
|
3
8
|
|
4
|
-
class
|
9
|
+
class ArduinoError < StandardError; end
|
5
10
|
|
6
|
-
module
|
11
|
+
module ArduinoBase
|
7
12
|
# Constants from Arduino
|
8
13
|
LOW = 0
|
9
14
|
HIGH = 1
|
@@ -39,23 +44,24 @@ module Domotics
|
|
39
44
|
TIMER_3 = 3
|
40
45
|
TIMER_4 = 4
|
41
46
|
TIMER_5 = 5
|
42
|
-
def initialize(
|
47
|
+
def initialize(args = {})
|
43
48
|
# grab args from hash
|
44
|
-
case @type =
|
49
|
+
case @type = args[:type] || :mega
|
45
50
|
when :nano
|
46
|
-
@port_str =
|
51
|
+
@port_str = args[:port] || "/dev/ttyUSB0"
|
47
52
|
@number_of_pins = 22
|
48
53
|
# todo: address of last 2 pins on nano???
|
49
54
|
@adc_pins = Array.new(8) { |index| 14+index }
|
50
55
|
@pwm_pins = [3,5,6,9,10,11]
|
51
56
|
when :mega
|
52
|
-
@port_str =
|
57
|
+
@port_str = args[:port] || "/dev/ttyACM0"
|
53
58
|
@number_of_pins = 70
|
54
59
|
@adc_pins = Array.new(16) { |index| 54+index }
|
55
60
|
@pwm_pins = Array.new(12) { |index| 2+index } + [44,45,46]
|
56
61
|
else
|
57
|
-
raise
|
62
|
+
raise ArduinoError, 'Invalid board type.'
|
58
63
|
end
|
64
|
+
@logger = args[:logger] || Logger.new(STDERR)
|
59
65
|
# Not allow multiple command sends
|
60
66
|
@command_lock = Mutex.new
|
61
67
|
@reply = Queue.new
|
@@ -63,13 +69,13 @@ module Domotics
|
|
63
69
|
@board = nil
|
64
70
|
@board_listener = nil
|
65
71
|
connect
|
66
|
-
super
|
72
|
+
super if self.class.superclass != Object
|
67
73
|
end
|
68
74
|
|
69
75
|
# ---0--- SETPINMODE
|
70
76
|
def set_mode(pin, mode)
|
71
77
|
check_pin(pin); check_pin_watch(pin); raise ArgumentError, 'Error! Invalid mode.' unless MODES.include? mode
|
72
|
-
|
78
|
+
@logger.warn { "Already set mode for pin: #{pin}." } if @pin_mode[pin] == mode
|
73
79
|
@pin_mode[pin] = mode
|
74
80
|
send_command(SETPINMODE, pin, mode)
|
75
81
|
end
|
@@ -110,7 +116,7 @@ module Domotics
|
|
110
116
|
end
|
111
117
|
|
112
118
|
# ---9--- SETPWMFREQ
|
113
|
-
def set_pwm_frequency(pin, divisor)
|
119
|
+
def set_pwm_frequency(pin, divisor = 5)
|
114
120
|
case @type
|
115
121
|
when :nano
|
116
122
|
case pin
|
@@ -145,7 +151,7 @@ module Domotics
|
|
145
151
|
end
|
146
152
|
send_command(SETPWMFREQ, timer, divisor)
|
147
153
|
end
|
148
|
-
|
154
|
+
|
149
155
|
# ---5--- GETWATCH
|
150
156
|
def get_watch(pin)
|
151
157
|
check_pin(pin)
|
@@ -155,47 +161,49 @@ module Domotics
|
|
155
161
|
# ---6--- SETWATCH
|
156
162
|
def set_watch(pin, watch)
|
157
163
|
check_pin(pin); raise ArgumentError, 'Invalid watch mode.' unless W_STATES.include? watch
|
158
|
-
|
164
|
+
@logger.warn { "Warning! already set watch mode for pin: #{pin}." } if @watch_list[pin] == watch
|
159
165
|
set_mode(pin, INPUT) if @pin_mode[pin] == OUTPUT
|
160
166
|
@watch_list[pin] = watch
|
161
167
|
send_command(SETWATCH, pin, watch)
|
162
168
|
end
|
163
169
|
|
164
170
|
def destroy
|
165
|
-
|
171
|
+
@logger.info { "Destroy board connection..." }
|
166
172
|
@command_lock.synchronize do
|
167
|
-
@board_listener.exit if @board_listener
|
173
|
+
@board_listener.exit if @board_listener and @board_listener.alive?
|
168
174
|
@board.close
|
169
175
|
end
|
170
|
-
|
171
|
-
super
|
176
|
+
@logger.info { "done." }
|
177
|
+
super if self.class.superclass != Object
|
172
178
|
end
|
173
179
|
|
174
180
|
private
|
175
181
|
|
176
182
|
# Default event handler simple prints event.
|
177
183
|
def event_handler(hash)
|
178
|
-
|
184
|
+
raise ArduinoError, hash[:event].inspect
|
179
185
|
end
|
180
186
|
|
181
187
|
# Send command directly to board
|
182
188
|
def send_command(command, pin = 0, value = 0)
|
183
189
|
@command_lock.synchronize do
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
0
|
189
|
-
|
190
|
-
1
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
190
|
+
Timeout.timeout(1) do
|
191
|
+
@board.puts("#{command} #{pin} #{value}")
|
192
|
+
# Get reply
|
193
|
+
case reply = @reply.pop
|
194
|
+
when 0
|
195
|
+
0
|
196
|
+
when 1
|
197
|
+
1
|
198
|
+
when SUCCESSREPRLY
|
199
|
+
true
|
200
|
+
when FAILREPRLY
|
201
|
+
false
|
202
|
+
when Array
|
203
|
+
reply
|
204
|
+
else
|
205
|
+
nil
|
206
|
+
end
|
199
207
|
end
|
200
208
|
end
|
201
209
|
end
|
@@ -206,7 +214,7 @@ module Domotics
|
|
206
214
|
begin
|
207
215
|
loop do
|
208
216
|
message = @board.gets
|
209
|
-
raise
|
217
|
+
raise ArduinoError, "Board[#{@port_str}] i/o error." unless message # message nil - board disconected
|
210
218
|
message = message.force_encoding("ISO-8859-1").split
|
211
219
|
case message.length
|
212
220
|
when 1
|
@@ -216,18 +224,18 @@ module Domotics
|
|
216
224
|
when 2
|
217
225
|
@reply.push(message.collect{ |m| m.to_i })
|
218
226
|
else
|
219
|
-
raise
|
227
|
+
raise ArduinoError, "Invalid reply from board[#{@port_str}]."
|
220
228
|
end
|
221
229
|
end
|
222
|
-
rescue
|
230
|
+
rescue ArduinoError => e
|
223
231
|
# Continue to operate in new thread
|
224
232
|
Thread.new do
|
225
|
-
|
233
|
+
@logger.error e.message
|
226
234
|
# Release command lock
|
227
235
|
@reply.push(FAILREPRLY) if @command_lock.locked?
|
228
236
|
# Close board connection
|
229
237
|
@board.close
|
230
|
-
|
238
|
+
@logger.info "Try to restart board[#{@port_str}] in 5 seconds..."
|
231
239
|
sleep 5
|
232
240
|
connect
|
233
241
|
end
|
@@ -238,44 +246,46 @@ module Domotics
|
|
238
246
|
end
|
239
247
|
# Connect to board
|
240
248
|
def connect
|
241
|
-
|
249
|
+
@logger.info { "Open serial connection to board[#{@port_str}]..." }
|
242
250
|
baudrate = 115200; databits = 8; stopbits = 1; parity = SerialPort::NONE
|
243
251
|
@board = SerialPort.new(@port_str, baudrate, databits, stopbits, parity)
|
244
252
|
@board.read_timeout = 0
|
245
253
|
@board.sync = true
|
246
|
-
|
247
|
-
|
254
|
+
@logger.info { "done." }
|
255
|
+
@logger.info { "Initializing board[#{@port_str}]..." }
|
248
256
|
# Pin states and mods
|
249
257
|
@pin_mode = Array.new(@number_of_pins, INPUT)
|
250
258
|
@watch_list = Array.new(@number_of_pins, WATCHOFF)
|
251
|
-
# Hard reset board
|
252
|
-
@
|
253
|
-
|
254
|
-
|
255
|
-
|
259
|
+
# Hard reset board (unless it's not a fake board from emulator)
|
260
|
+
unless @port_str =~ /\A\/dev\/pts\/.*\Z/
|
261
|
+
@board.dtr = 0
|
262
|
+
sleep(2)
|
263
|
+
end
|
264
|
+
@logger.info { "done." }
|
265
|
+
@logger.info { "Starting board[#{@port_str}] listener..." }
|
256
266
|
listen
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
267
|
+
@logger.info { "done." }
|
268
|
+
@logger.info { "Reset board[#{@port_str}] to defaults..." }
|
269
|
+
@logger.info { "done." } if send_command(DEFAULTS)
|
270
|
+
@logger.info { "Checking connection with board[#{@port_str}]..." }
|
261
271
|
random = Random.new
|
262
272
|
a, b = 2.times.map { random.rand(0..9) }
|
263
273
|
if send_command(ECHOREPLY, a, b) == [b, a]
|
264
|
-
|
274
|
+
@logger.info { "done." }
|
265
275
|
else
|
266
|
-
|
267
|
-
raise
|
276
|
+
@logger.error { "Bad reply from board[#{@port_str}] (wrong firmware?)." }
|
277
|
+
raise ArduinoError
|
268
278
|
end
|
269
279
|
rescue Exception => e
|
270
|
-
|
280
|
+
@logger.error { e.message }
|
271
281
|
tries = tries || 0
|
272
282
|
tries += 1
|
273
283
|
if tries <= 3
|
274
|
-
|
284
|
+
@logger.info { "Attempt #{tries}: try to reconnect in #{5**tries} seconds." }
|
275
285
|
sleep 5**tries
|
276
286
|
retry
|
277
287
|
end
|
278
|
-
|
288
|
+
@logger.error { "Board[#{@port_str}] malfunction. Automatic restart failed." }
|
279
289
|
event_handler :event => :malfunction
|
280
290
|
end
|
281
291
|
# Checks
|
@@ -285,9 +295,9 @@ module Domotics
|
|
285
295
|
def check_pin_watch(pin)
|
286
296
|
raise ArgumentError, 'Cant access watched pin.' if @watch_list[pin] == WATCHON
|
287
297
|
end
|
288
|
-
|
298
|
+
|
289
299
|
rescue ArgumentError => e
|
290
|
-
|
300
|
+
@logger.error e.message
|
291
301
|
nil
|
292
302
|
end
|
293
303
|
end
|
@@ -2,23 +2,28 @@
|
|
2
2
|
module Domotics
|
3
3
|
module Arduino
|
4
4
|
module DigitalPin
|
5
|
+
|
5
6
|
def initialize(args_hash = {})
|
6
7
|
@board = Domotics::Device[args_hash[:device]]
|
7
8
|
@pin = args_hash[:pin]
|
8
9
|
@board.register_pin self, @pin
|
9
10
|
super
|
10
11
|
end
|
12
|
+
|
11
13
|
def state!
|
12
14
|
to_hls @board.get_digital(@pin)
|
13
15
|
end
|
16
|
+
|
14
17
|
def set_state(value)
|
15
18
|
@board.set_digital @pin, to_lls(value)
|
16
19
|
super
|
17
20
|
end
|
21
|
+
|
18
22
|
# Convert to High Level State
|
19
23
|
def to_hls(value)
|
20
24
|
value == ArduinoSerial::HIGH ? :on : :off
|
21
25
|
end
|
26
|
+
|
22
27
|
# Convert to Low Level State
|
23
28
|
def to_lls(value)
|
24
29
|
value == :on ? ArduinoSerial::HIGH : ArduinoSerial::LOW
|
@@ -1,12 +1,14 @@
|
|
1
1
|
module Domotics
|
2
2
|
module Arduino
|
3
3
|
module PWMPin
|
4
|
+
|
4
5
|
def initialize(args_hash = {})
|
5
6
|
@board = Domotics::Device[args_hash[:device]]
|
6
7
|
@pin = args_hash[:pin]
|
7
8
|
@board.set_pwm_frequency @pin, 1
|
8
9
|
super
|
9
10
|
end
|
11
|
+
|
10
12
|
def set_state(value = 0)
|
11
13
|
value = case value
|
12
14
|
when 0, :off
|
@@ -21,6 +23,7 @@ module Domotics
|
|
21
23
|
end
|
22
24
|
super value
|
23
25
|
end
|
26
|
+
|
24
27
|
end
|
25
28
|
end
|
26
29
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'pty'
|
2
|
+
|
3
|
+
class BoardEmulator
|
4
|
+
def initialize(args = {})
|
5
|
+
success = Domotics::Arduino::ArduinoBase::SUCCESSREPRLY
|
6
|
+
@master, @slave = PTY.open
|
7
|
+
type = args[:type] || :normal
|
8
|
+
unless type == :dead
|
9
|
+
Thread.new do
|
10
|
+
loop do
|
11
|
+
raise unless message = @master.gets
|
12
|
+
command, pin, value = message.chomp.split(" ").map{ |m| m.to_i }
|
13
|
+
case command
|
14
|
+
when Domotics::Arduino::ArduinoBase::ECHOREPLY
|
15
|
+
@master.puts "#{value} #{pin}\n"
|
16
|
+
else
|
17
|
+
@master.puts success
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
def port
|
24
|
+
@slave.path
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'domotics/arduino'
|
3
|
+
require 'board_emulator'
|
4
|
+
|
5
|
+
class ArduinoTestBoard
|
6
|
+
include Domotics::Arduino::ArduinoBase
|
7
|
+
end
|
8
|
+
|
9
|
+
class ArduinoTest < Test::Unit::TestCase
|
10
|
+
def setup
|
11
|
+
end
|
12
|
+
def test_open_connection
|
13
|
+
assert brd = ArduinoTestBoard.new(port: BoardEmulator.new.port)
|
14
|
+
assert !brd.destroy
|
15
|
+
end
|
16
|
+
def test_dead_board
|
17
|
+
assert_raise Domotics::Arduino::ArduinoError do
|
18
|
+
ArduinoTestBoard.new(port: BoardEmulator.new(type: :dead).port)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: domotics-arduino
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,6 +11,22 @@ bindir: bin
|
|
11
11
|
cert_chain: []
|
12
12
|
date: 2013-12-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: serialport
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
14
30
|
- !ruby/object:Gem::Dependency
|
15
31
|
name: bundler
|
16
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -65,6 +81,8 @@ files:
|
|
65
81
|
- lib/domotics/arduino/firmware/stepper.c
|
66
82
|
- lib/domotics/arduino/pwm_pin.rb
|
67
83
|
- lib/domotics/arduino/version.rb
|
84
|
+
- test/board_emulator.rb
|
85
|
+
- test/test_arduino.rb
|
68
86
|
homepage: ''
|
69
87
|
licenses:
|
70
88
|
- MIT
|
@@ -90,4 +108,6 @@ rubygems_version: 1.8.25
|
|
90
108
|
signing_key:
|
91
109
|
specification_version: 3
|
92
110
|
summary: Arduino part of Domotics
|
93
|
-
test_files:
|
111
|
+
test_files:
|
112
|
+
- test/board_emulator.rb
|
113
|
+
- test/test_arduino.rb
|