domotics-arduino 0.0.1 → 0.0.2

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,294 @@
1
+ module Domotics
2
+ module Arduino
3
+
4
+ class ArduinoSerialError < StandardError; end
5
+
6
+ module ArduinoSerial
7
+ # Constants from Arduino
8
+ LOW = 0
9
+ HIGH = 1
10
+ STATES = [LOW, HIGH]
11
+ INPUT = 0
12
+ OUTPUT = 1
13
+ INPUTPULLUP = 2
14
+ MODES = [INPUT, OUTPUT, INPUTPULLUP]
15
+ # Replies from board
16
+ SUCCESSREPRLY = 2
17
+ FAILREPRLY = 3
18
+ # Board events
19
+ EVENTS = {'0' => :pin_state_change}
20
+ # Commands for board
21
+ SETPINMODE = 0
22
+ GETDIGITAL = 1
23
+ SETDIGITAL = 2
24
+ GETADC = 3
25
+ SETPWM = 4
26
+ GETWATCH = 5
27
+ SETWATCH = 6
28
+ ECHOREPLY = 7
29
+ DEFAULTS = 8
30
+ SETPWMFREQ = 9
31
+ # Watch states
32
+ WATCHOFF = 0
33
+ WATCHON = 1
34
+ W_STATES = [WATCHOFF, WATCHON]
35
+ # Timers
36
+ TIMER_0 = 0
37
+ TIMER_1 = 1
38
+ TIMER_2 = 2
39
+ TIMER_3 = 3
40
+ TIMER_4 = 4
41
+ TIMER_5 = 5
42
+ def initialize(args_hash = {})
43
+ # grab args from hash
44
+ case @type = args_hash[:type] || :mega
45
+ when :nano
46
+ @port_str = args_hash[:port] || "/dev/ttyUSB0"
47
+ @number_of_pins = 22
48
+ # todo: address of last 2 pins on nano???
49
+ @adc_pins = Array.new(8) { |index| 14+index }
50
+ @pwm_pins = [3,5,6,9,10,11]
51
+ when :mega
52
+ @port_str = args_hash[:port] || "/dev/ttyACM0"
53
+ @number_of_pins = 70
54
+ @adc_pins = Array.new(16) { |index| 54+index }
55
+ @pwm_pins = Array.new(12) { |index| 2+index } + [44,45,46]
56
+ else
57
+ raise ArduinoSerialError, 'Invalid board type.'
58
+ end
59
+ # Not allow multiple command sends
60
+ @command_lock = Mutex.new
61
+ @reply = Queue.new
62
+ # Open connection
63
+ @board = nil
64
+ @board_listener = nil
65
+ connect
66
+ super
67
+ end
68
+
69
+ # ---0--- SETPINMODE
70
+ def set_mode(pin, mode)
71
+ 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
73
+ @pin_mode[pin] = mode
74
+ send_command(SETPINMODE, pin, mode)
75
+ end
76
+ def set_input_pullup(pin)
77
+ set_mode(pin, INPUTPULLUP)
78
+ end
79
+
80
+ # ---1--- GETDIGITAL
81
+ def get_digital(pin)
82
+ check_pin(pin)
83
+ send_command(GETDIGITAL, pin)
84
+ end
85
+
86
+ # ---2--- SETDIGITAL
87
+ def set_digital(pin, state)
88
+ check_pin(pin); check_pin_watch(pin); raise ArgumentError, 'Error! Invalid state.' unless STATES.include? state
89
+ set_mode(pin, OUTPUT) unless @pin_mode[pin] == OUTPUT
90
+ send_command(SETDIGITAL, pin, state)
91
+ end
92
+ def set_high(pin)
93
+ set_digital(pin, HIGH)
94
+ end
95
+ def set_low(pin)
96
+ set_digital(pin, LOW)
97
+ end
98
+
99
+ # ---3--- GETADC
100
+ def get_adc(pin)
101
+ check_pin(pin); raise ArgumentError, 'Not ADC pin.' unless @adc_pins.include? pin
102
+ send_command(GETADC, pin)
103
+ end
104
+
105
+ # ---4--- SETPWM
106
+ def set_pwm(pin, value)
107
+ check_pin(pin); check_pin_watch(pin); raise ArgumentError, 'Not PWM or DAC pin.' unless @pwm_pins.include? pin
108
+ raise ArgumentError, 'Invalid PWM value' unless value.is_a?(Integer) and value>=0 and value<=255
109
+ send_command(SETPWM, pin, value)
110
+ end
111
+
112
+ # ---9--- SETPWMFREQ
113
+ def set_pwm_frequency(pin, divisor)
114
+ case @type
115
+ when :nano
116
+ case pin
117
+ when 9,10
118
+ timer = TIMER_1
119
+ when 3,11
120
+ timer = TIMER_2
121
+ else
122
+ timer = nil
123
+ end
124
+ when :mega
125
+ case pin
126
+ when 2,3,5
127
+ timer = TIMER_3
128
+ when 6,7,8
129
+ timer = TIMER_4
130
+ when 9,10
131
+ timer = TIMER_2
132
+ when 11,12
133
+ timer = TIMER_1
134
+ when 44,45,46
135
+ timer = TIMER_5
136
+ else
137
+ timer = nil
138
+ end
139
+ end
140
+ raise ArgumentError, 'Invalid PWM pin for change frequency.' unless timer
141
+ if timer == TIMER_2
142
+ raise ArgumentError, 'Invalid timer divisor.' unless (1..7).include? divisor
143
+ else
144
+ raise ArgumentError, 'Invalid timer divisor.' unless (1..5).include? divisor
145
+ end
146
+ send_command(SETPWMFREQ, timer, divisor)
147
+ end
148
+
149
+ # ---5--- GETWATCH
150
+ def get_watch(pin)
151
+ check_pin(pin)
152
+ send_command(GETWATCH, pin)
153
+ end
154
+
155
+ # ---6--- SETWATCH
156
+ def set_watch(pin, watch)
157
+ 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
159
+ set_mode(pin, INPUT) if @pin_mode[pin] == OUTPUT
160
+ @watch_list[pin] = watch
161
+ send_command(SETWATCH, pin, watch)
162
+ end
163
+
164
+ def destroy
165
+ $logger.info { "Destroy board connection..." }
166
+ @command_lock.synchronize do
167
+ @board_listener.exit if @board_listener
168
+ @board.close
169
+ end
170
+ $logger.info { "done." }
171
+ super
172
+ end
173
+
174
+ private
175
+
176
+ # Default event handler simple prints event.
177
+ def event_handler(hash)
178
+ puts hash.inspect
179
+ end
180
+
181
+ # Send command directly to board
182
+ def send_command(command, pin = 0, value = 0)
183
+ @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
199
+ end
200
+ end
201
+ end
202
+ # Listen for board replies and alarms
203
+ def listen
204
+ @board_listener.exit if @board_listener
205
+ @board_listener = Thread.new do
206
+ begin
207
+ loop do
208
+ message = @board.gets
209
+ raise ArduinoSerialError, 'Board i/o error.' unless message # message nil - board disconected
210
+ message = message.force_encoding("ISO-8859-1").split
211
+ case message.length
212
+ when 1
213
+ @reply.push(message[0].to_i)
214
+ when 3
215
+ event_handler :event => EVENTS[message[0]], :pin => message[1].to_i, :state => message[2].to_i
216
+ when 2
217
+ @reply.push(message.collect{ |m| m.to_i })
218
+ else
219
+ raise ArduinoSerialError, 'Invalid reply from board.'
220
+ end
221
+ end
222
+ rescue ArduinoSerialError => e
223
+ # Continue to operate in new thread
224
+ Thread.new do
225
+ $logger.error e.message
226
+ # Release command lock
227
+ @reply.push(FAILREPRLY) if @command_lock.locked?
228
+ # Close board connection
229
+ @board.close
230
+ $logger.info 'Try to restart board in 5 seconds...'
231
+ sleep 5
232
+ connect
233
+ end
234
+ # Exit errored thread
235
+ @board_listener.exit
236
+ end
237
+ end
238
+ end
239
+ # Connect to board
240
+ def connect
241
+ $logger.info { "Open serial connection to board..." }
242
+ baudrate = 115200; databits = 8; stopbits = 1; parity = SerialPort::NONE
243
+ @board = SerialPort.new(@port_str, baudrate, databits, stopbits, parity)
244
+ @board.read_timeout = 0
245
+ @board.sync = true
246
+ $logger.info { "done." }
247
+ $logger.info { "Initializing arduino board..." }
248
+ # Pin states and mods
249
+ @pin_mode = Array.new(@number_of_pins, INPUT)
250
+ @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..." }
256
+ 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..." }
261
+ random = Random.new
262
+ a, b = 2.times.map { random.rand(0..9) }
263
+ if send_command(ECHOREPLY, a, b) == [b, a]
264
+ $logger.info { "done." }
265
+ else
266
+ $logger.error { "Bad reply from board (wrong firmware?)." }
267
+ raise ArduinoSerialError
268
+ end
269
+ rescue Exception => e
270
+ $logger.error { e.message }
271
+ tries = tries || 0
272
+ tries += 1
273
+ if tries <= 3
274
+ $logger.info { "Attempt #{tries}: try to reconnect in #{5**tries} seconds." }
275
+ sleep 5**tries
276
+ retry
277
+ end
278
+ $logger.error { "Board malfunction. Automatic restart failed." }
279
+ event_handler :event => :malfunction
280
+ end
281
+ # Checks
282
+ def check_pin(pin)
283
+ raise ArgumentError, 'Invalid pin number.' unless pin.is_a?(Integer) and pin>=0 and pin<@number_of_pins
284
+ end
285
+ def check_pin_watch(pin)
286
+ raise ArgumentError, 'Cant access watched pin.' if @watch_list[pin] == WATCHON
287
+ end
288
+
289
+ rescue ArgumentError => e
290
+ $logger.error e.message
291
+ nil
292
+ end
293
+ end
294
+ end
@@ -0,0 +1,28 @@
1
+ # Digital pin
2
+ module Domotics
3
+ module Arduino
4
+ module DigitalPin
5
+ def initialize(args_hash = {})
6
+ @board = Domotics::Device[args_hash[:device]]
7
+ @pin = args_hash[:pin]
8
+ @board.register_pin self, @pin
9
+ super
10
+ end
11
+ def state!
12
+ to_hls @board.get_digital(@pin)
13
+ end
14
+ def set_state(value)
15
+ @board.set_digital @pin, to_lls(value)
16
+ super
17
+ end
18
+ # Convert to High Level State
19
+ def to_hls(value)
20
+ value == ArduinoSerial::HIGH ? :on : :off
21
+ end
22
+ # Convert to Low Level State
23
+ def to_lls(value)
24
+ value == :on ? ArduinoSerial::HIGH : ArduinoSerial::LOW
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/ruby -w
2
+ # coding: utf-8
3
+ module Domotics
4
+ module Arduino
5
+ # Normal_close sensor
6
+ module NCSensor
7
+ include DigitalPin
8
+ def initialize(args_hash = {})
9
+ super
10
+ @board.set_input_pullup @pin
11
+ @board.set_watch @pin, ArduinoSerial::WATCHON
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ module Domotics
2
+ module Arduino
3
+ # Normal_open sensor
4
+ module NOSensor
5
+ include DigitalPin
6
+ def initialize(args_hash = {})
7
+ super
8
+ @board.set_watch @pin, ArduinoSerial::WATCHON
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,149 @@
1
+ /*
2
+ * Serial communication for gHome.
3
+ *
4
+ * Input(space separeted):
5
+ * 1. command
6
+ * 2. pin
7
+ * 3. option
8
+ *
9
+ * created 03 Sep 2012
10
+ * by goredar
11
+ */
12
+
13
+
14
+
15
+ // Cons
16
+ // Replys definitions
17
+ const int SuccessReprly = 2;
18
+ const int FailReprly = 3;
19
+ // Input commands definitions
20
+ const int SetPinMode = 0;
21
+ const int GetDigital = 1;
22
+ const int SetDigital = 2;
23
+ const int GetADC = 3;
24
+ const int SetPWM = 4;
25
+ const int GetWatch = 5;
26
+ const int SetWatch = 6;
27
+ const int EchoReply = 7;
28
+ const int Defaults = 8;
29
+ const int SetPWMFreq = 9;
30
+ // Output message definitions
31
+ const int WatchAlarm = 0;
32
+ // Number of pins for board:
33
+ // nano - 22, mega - 70
34
+ const int NumberOfPins = 70;
35
+ // For watch function
36
+ const int WatchOFF = 0;
37
+ const int WatchON = 1;
38
+ // Vars
39
+ int command = 0;
40
+ int pin = 0;
41
+ int option = 0;
42
+ int state = 0;
43
+ boolean watchlist[NumberOfPins];
44
+ int statelist[NumberOfPins];
45
+
46
+ void setup() {
47
+ default_state();
48
+ // Begin serial
49
+ Serial.begin(115200);
50
+ }
51
+ // Main stream
52
+ void loop() {
53
+ if (Serial.available() > 0) {
54
+ // Parse command
55
+ command = Serial.parseInt();
56
+ pin = Serial.parseInt();
57
+ option = Serial.parseInt();
58
+ if (Serial.read() == '\n') {
59
+ switch (command) {
60
+ case SetPinMode:
61
+ pinMode(pin, option);
62
+ Serial.println(SuccessReprly, DEC);
63
+ break;
64
+ case GetDigital:
65
+ Serial.println(digitalRead(pin), DEC);
66
+ break;
67
+ case SetDigital:
68
+ digitalWrite(pin, option);
69
+ Serial.println(SuccessReprly, DEC);
70
+ break;
71
+ case GetADC:
72
+ Serial.println(analogRead(pin), DEC);
73
+ break;
74
+ case SetPWM:
75
+ analogWrite(pin, option);
76
+ Serial.println(SuccessReprly, DEC);
77
+ break;
78
+ case SetPWMFreq:
79
+ switch (pin) {
80
+ case 1: TCCR1B = (TCCR1B & 0xF8) | option; break;
81
+ case 2: TCCR2B = (TCCR2B & 0xF8) | option; break;
82
+ //#ifdef __AVR_ATmega2560__
83
+ case 3: TCCR3B = (TCCR3B & 0xF8) | option; break;
84
+ case 4: TCCR4B = (TCCR4B & 0xF8) | option; break;
85
+ case 5: TCCR5B = (TCCR5B & 0xF8) | option; break;
86
+ //#endif
87
+ }
88
+ Serial.println(SuccessReprly, DEC);
89
+ break;
90
+ case GetWatch:
91
+ if (watchlist[pin]) {
92
+ Serial.println(WatchON, DEC);
93
+ }
94
+ else {
95
+ Serial.println(WatchOFF, DEC);
96
+ }
97
+ break;
98
+ case SetWatch:
99
+ if (option == WatchOFF) {
100
+ watchlist[pin] = false;
101
+ }
102
+ else {
103
+ statelist[pin] = digitalRead(pin);
104
+ watchlist[pin] = true;
105
+ }
106
+ Serial.println(SuccessReprly, DEC);
107
+ break;
108
+ case EchoReply:
109
+ Serial.print(option, DEC);
110
+ Serial.print(' ');
111
+ Serial.println(pin, DEC);
112
+ break;
113
+ case Defaults:
114
+ default_state();
115
+ Serial.println(SuccessReprly, DEC);
116
+ break;
117
+ default:
118
+ // Invalid command
119
+ Serial.println(FailReprly, DEC);
120
+ break;
121
+ }
122
+ }
123
+ else {
124
+ // Invalid input
125
+ Serial.println(FailReprly, DEC);
126
+ }
127
+ }
128
+ for (int i=2; i < NumberOfPins; i++){
129
+ // Watch for changes on digital pins
130
+ if (watchlist[i]) {
131
+ state = digitalRead(i);
132
+ if (state != statelist[i]) {
133
+ statelist[i] = state;
134
+ Serial.print(WatchAlarm, DEC);
135
+ Serial.print(' ');
136
+ Serial.print(i, DEC);
137
+ Serial.print(' ');
138
+ Serial.println(state, DEC);
139
+ }
140
+ }
141
+ }
142
+ }
143
+
144
+ void default_state() {
145
+ for (int i=0; i < NumberOfPins; i++) {
146
+ pinMode(i, INPUT);
147
+ watchlist[i] = false;
148
+ }
149
+ }
@@ -0,0 +1,16 @@
1
+
2
+ int command = 0;
3
+ int pin = 0;
4
+ int option = 0;
5
+ int state = 0;
6
+
7
+ void setup() {
8
+ Serial.begin(115200);
9
+ }
10
+
11
+ void loop() {
12
+ PORTD maps to Arduino digital pins 0 to 7
13
+ PORTB maps to Arduino digital pins 8 to 13
14
+ PORTC maps to Arduino analog pins 0 to 5.
15
+ PORTB |= B1100;
16
+ }
@@ -0,0 +1,26 @@
1
+ module Domotics
2
+ module Arduino
3
+ module PWMPin
4
+ def initialize(args_hash = {})
5
+ @board = Domotics::Device[args_hash[:device]]
6
+ @pin = args_hash[:pin]
7
+ @board.set_pwm_frequency @pin, 1
8
+ super
9
+ end
10
+ def set_state(value = 0)
11
+ value = case value
12
+ when 0, :off
13
+ @board.set_low @pin
14
+ 0
15
+ when 1..254
16
+ @board.set_pwm @pin, value
17
+ value
18
+ when 255, :on
19
+ @board.set_high @pin
20
+ 255
21
+ end
22
+ super value
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,5 +1,5 @@
1
1
  module Domotics
2
2
  module Arduino
3
- VERSION = "0.0.1"
3
+ VERSION = "0.0.2"
4
4
  end
5
5
  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.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -57,6 +57,13 @@ files:
57
57
  - Rakefile
58
58
  - domotics-arduino.gemspec
59
59
  - lib/domotics/arduino.rb
60
+ - lib/domotics/arduino/arduino_serial.rb
61
+ - lib/domotics/arduino/digital_pin.rb
62
+ - lib/domotics/arduino/digital_pin/nc_sensor.rb
63
+ - lib/domotics/arduino/digital_pin/no_sensor.rb
64
+ - lib/domotics/arduino/firmware/duino.pde
65
+ - lib/domotics/arduino/firmware/stepper.c
66
+ - lib/domotics/arduino/pwm_pin.rb
60
67
  - lib/domotics/arduino/version.rb
61
68
  homepage: ''
62
69
  licenses: