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 CHANGED
@@ -1 +1,14 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ end
7
+
8
+ desc "Run tests"
9
+ task :default => :test
10
+
11
+ desc "Publish gem to rubygems.org"
12
+ task :publish do
13
+
14
+ end
@@ -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
@@ -1,9 +1 @@
1
- require "serialport"
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 ArduinoSerialError < StandardError; end
9
+ class ArduinoError < StandardError; end
5
10
 
6
- module ArduinoSerial
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(args_hash = {})
47
+ def initialize(args = {})
43
48
  # grab args from hash
44
- case @type = args_hash[:type] || :mega
49
+ case @type = args[:type] || :mega
45
50
  when :nano
46
- @port_str = args_hash[:port] || "/dev/ttyUSB0"
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 = args_hash[:port] || "/dev/ttyACM0"
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 ArduinoSerialError, 'Invalid board type.'
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
- $logger.warn { "Already set mode for pin: #{pin}." } if @pin_mode[pin] == mode
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
- $logger.warn { "Warning! already set watch mode for pin: #{pin}." } if @watch_list[pin] == watch
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
- $logger.info { "Destroy board connection..." }
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
- $logger.info { "done." }
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
- puts hash.inspect
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
- @board.puts("#{command} #{pin} #{value}")
185
- # Get reply
186
- case reply = @reply.pop
187
- when 0
188
- 0
189
- when 1
190
- 1
191
- when SUCCESSREPRLY
192
- true
193
- when FAILREPRLY
194
- false
195
- when Array
196
- reply
197
- else
198
- nil
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 ArduinoSerialError, 'Board i/o error.' unless message # message nil - board disconected
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 ArduinoSerialError, 'Invalid reply from board.'
227
+ raise ArduinoError, "Invalid reply from board[#{@port_str}]."
220
228
  end
221
229
  end
222
- rescue ArduinoSerialError => e
230
+ rescue ArduinoError => e
223
231
  # Continue to operate in new thread
224
232
  Thread.new do
225
- $logger.error e.message
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
- $logger.info 'Try to restart board in 5 seconds...'
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
- $logger.info { "Open serial connection to board..." }
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
- $logger.info { "done." }
247
- $logger.info { "Initializing arduino board..." }
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
- @board.dtr = 0
253
- sleep(2)
254
- $logger.info { "done." }
255
- $logger.info { "Starting board listener..." }
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
- $logger.info { "done." }
258
- $logger.info { "Reset board to defaults..." }
259
- $logger.info { "done." } if send_command(DEFAULTS)
260
- $logger.info { "Checking connection with board..." }
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
- $logger.info { "done." }
274
+ @logger.info { "done." }
265
275
  else
266
- $logger.error { "Bad reply from board (wrong firmware?)." }
267
- raise ArduinoSerialError
276
+ @logger.error { "Bad reply from board[#{@port_str}] (wrong firmware?)." }
277
+ raise ArduinoError
268
278
  end
269
279
  rescue Exception => e
270
- $logger.error { e.message }
280
+ @logger.error { e.message }
271
281
  tries = tries || 0
272
282
  tries += 1
273
283
  if tries <= 3
274
- $logger.info { "Attempt #{tries}: try to reconnect in #{5**tries} seconds." }
284
+ @logger.info { "Attempt #{tries}: try to reconnect in #{5**tries} seconds." }
275
285
  sleep 5**tries
276
286
  retry
277
287
  end
278
- $logger.error { "Board malfunction. Automatic restart failed." }
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
- $logger.error e.message
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
@@ -1,5 +1,5 @@
1
1
  module Domotics
2
2
  module Arduino
3
- VERSION = "0.0.2"
3
+ VERSION = "0.0.3"
4
4
  end
5
5
  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.2
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