ruby-xbee 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- M2YwMDViM2UwZDVhODRmOTY5NTk0MTk5ZjlmYzlhMGQ1ZGEyZGFmZg==
5
- data.tar.gz: !binary |-
6
- MTRhMGYwODc0MTVjN2ZiZTI3MjI0ODJmYTg0MjUwYWQwNjEwODdhNQ==
2
+ SHA1:
3
+ metadata.gz: 69899610a08432193f6c6aa024e12d7d1d1f1307
4
+ data.tar.gz: 4ad1f0cf2f2f80632767e79b28317d076032071c
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- YWRiNmE4NjFhNDQzNGJmNmY0ZGNhODJmY2ViYWE4ZjMxNTBmZjgxNmU0ODA1
10
- YWEwOGMyMjE1MTk4MDhmMWE3Mjc5ZGVmZGQ3OTNjNzNhNTkyZmFkNjMzYTYw
11
- NzYxZWY1MDlhZmQ1N2MzNGJjMWZiZDg4ZGFiZWUzMDQzYmJkYmE=
12
- data.tar.gz: !binary |-
13
- MTVmODU0MzllZjM4ZmNhYWI5ZGM5MDMyMGE1ZjJmYzJiNDJlMTc3YTYxYjhk
14
- ZmQ1MmU5Y2QyYjJiN2YxNzdlMzJlM2JjMGQ0NmRmNGVhNmE2MWJjYThiMWNl
15
- YmJkZTc3ZmNiYjRmOWMwYjEwYmUzOTY3MTQzZjBmNmFmM2M5Njc=
6
+ metadata.gz: be955c5442b964912f43af8aa2fa15a3a3ac123adc48d98095b8b929570fe1c78f85f4141b0e2f949ae5cda6ee8ae4bfadf402e64d05e48eca90efca4d969782
7
+ data.tar.gz: 8347bdc735975f245bcbc5feb0e80efa24a01a3b7e488eebe0ae063ea521846754d09d5878ed47030e768ac95d3b2926b15cc5c2f6dcefa3cede73f20f34ffc5
data/README.rdoc CHANGED
@@ -47,10 +47,11 @@ Example use of the core XBee ruby class can be found in the utilities themselves
47
47
  xbeeinfo.rb, xbeeconfigure.rb, xbeedio.rb, xbeelisten.rb, xbeesend.rb
48
48
 
49
49
  == Quick Start
50
- 0. install ruby 1.9.3 or 2.0.0 for your platform (see http://www.ruby-lang.org)
51
- 1. download the ruby-xbee ( git clone https://github.com/exsilium/ruby-xbee )
52
- 2. determine your /dev string and configure that in bin/ruby-xbee.rb
53
- 3. try ./bin/xbeeinfo.rb
50
+ 1. install ruby 1.9.3 or 2.0.0 for your platform (see http://www.ruby-lang.org)
51
+ 2. download the ruby-xbee ( git clone https://github.com/exsilium/ruby-xbee )
52
+ 3. install the dependencies `bundle install`
53
+ 4. determine your /dev string and configure that in bin/ruby-xbee.rb
54
+ 5. try ./bin/xbeeinfo.rb
54
55
 
55
56
  == Detailed Quick Start
56
57
  === clone the ruby-xbee repository.
data/bin/apilisten.rb CHANGED
@@ -6,9 +6,9 @@ require 'ruby-xbee'
6
6
  require 'pp'
7
7
 
8
8
  @uart_config = XBee::Config::XBeeUARTConfig.new()
9
- @xbee = XBee::BaseAPIModeInterface.new(@xbee_usbdev_str, @uart_config, 'API')
9
+ @xbee = XBee::BaseAPIModeInterface.new(@xbee_usbdev_str, @uart_config)
10
10
 
11
11
  # read XBee output forever
12
12
  while 1
13
13
  @xbee.getresponse true
14
- end
14
+ end
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Programmable XBee module Over the Air (OTA) Upgrade
4
+ # A sample using ruby-xbee and xmodem.
5
+ #
6
+ # Please note, this only works for PROGRAMMABLE XBee modules and for uploading
7
+ # the *application* firmware, not to be confused with the radio firmware.
8
+ #
9
+ # The coordinator must be running in API 1 mode because the programmable XBEE
10
+ # does not properly in the API mode 2 (with escaped control characters).
11
+ #
12
+ # This is specific to Series 2 radios and ZigBee
13
+ #
14
+ # This script is not interactive, please define the below CONSTANTS before use
15
+ #
16
+ # If you are using broadcast addresses, make sure only one device is there to
17
+ # answer, concurrent upgrades have not been implemented
18
+ #
19
+ # Prerequisites: Install the xmodem gem by: gem install xmodem it's purposely
20
+ # left out from runtime dependencies.
21
+ #
22
+ # Usage: 1) set the constants
23
+ # 2) make sure the ruby-xbee.rb has the correct device configured
24
+ # 3) ./ota_upgrade.rb
25
+ #
26
+ $: << File.dirname(__FILE__)
27
+
28
+ require 'date'
29
+ require 'ruby-xbee'
30
+ require 'pp'
31
+ require 'socket'
32
+
33
+ require 'rubygems'
34
+ gem 'xmodem'
35
+ require 'xmodem'
36
+
37
+ ### CONSTANTS - SET BEFORE USE
38
+
39
+ # Target Address of the device being upgraded 0x000000000000FFFF for broadcast
40
+ TARGET_ADDRESS = 0x000000000000FFFF
41
+ # Target network, 0xFFFE for broadcast
42
+ TARGET_NETWORK = 0xFFFE
43
+ # OTA password, nil, if no password used
44
+ PASSWORD = nil
45
+ # Firmware file being uploaded, can be full path
46
+ TRANSFER_FILE = "blink_led_0_0_1_OTA_blank_pass.bin"
47
+ # If not on unix, this is the port used for XMODEM IO
48
+ LOCAL_PORT = 2000
49
+
50
+ ### END OF CONSTANTS
51
+
52
+ @uart_config = XBee::Config::XBeeUARTConfig.new()
53
+ @xbee = XBee::Series2APIModeInterface.new(@xbee_usbdev_str, @uart_config)
54
+
55
+ # Create server/socket for XModem transfer
56
+ @server=nil
57
+ @unix_socket = "/tmp/xmodem-transfer.sock"
58
+
59
+ # State variables
60
+ @transfer_started = false
61
+ @transfer_eot = 0
62
+
63
+ if !FileTest.exists?(TRANSFER_FILE)
64
+ puts "Firmware file not found: #{TRANSFER_FILE}"
65
+ exit
66
+ end
67
+
68
+ if ENV['OS'] != "Windows_NT"
69
+ File.delete( @unix_socket ) if FileTest.exists?( @unix_socket )
70
+ @server = UNIXServer.new(@unix_socket)
71
+ else
72
+ @server = TCPServer.new(LOCAL_PORT)
73
+ end
74
+
75
+ puts "---------------------------------------------------"
76
+ puts " Over The Air Upgrade "
77
+ puts "---------------------------------------------------"
78
+ puts "Target address: 0x%x" % TARGET_ADDRESS
79
+ puts "Target network: 0x%x" % TARGET_NETWORK
80
+ puts "---------------------------------------------------"
81
+
82
+ #XMODEM::logger.outputters = Outputter.stdout
83
+ XMODEM::timeout_seconds = 10.0 # Let's prolong the timeout
84
+ XMODEM::block_size = 64 # XBEE uses non-standard block size
85
+
86
+ puts "XMODEM: Transfer blocksize: #{XMODEM::block_size}"
87
+ puts "XMODEM: Timeout in seconds: #{XMODEM::timeout_seconds}"
88
+
89
+ transfer_thread = Thread.new {
90
+ session = @server.accept
91
+ session.sync = true
92
+ file = File.new(TRANSFER_FILE, "rb")
93
+ puts "Starting file transfer of: #{TRANSFER_FILE}"
94
+ XMODEM::send(session, file)
95
+ session.close
96
+ puts "<CLOSED>"
97
+ file.close
98
+ }
99
+
100
+ ##
101
+ # If the target board does not have proper OTA enabled application running
102
+ # or if the previous upgrades have been unsuccessful to the point where the
103
+ # module is unable to run and goes to bootloader, the following method
104
+ # can be used instead of the init_ota_upgrade. If the application is running
105
+ # the following request does not get any response
106
+ #
107
+ # Please note that the following works only if the target is in bootloader and
108
+ # when explicit addressing is used (broadcast address doesn't work, altough
109
+ # the target network can be broadcast)!
110
+ frame_id = @xbee.next_frame_id
111
+ frame_to_send = XBee::Frame::ExplicitAddressingCommand.new(frame_id, TARGET_ADDRESS, TARGET_NETWORK, 0xE8, 0xE8, 0x0011, 0xC105, 0x00, 0x00, "F")
112
+ @xbee.xbee_serialport.write(frame_to_send._dump)
113
+
114
+ ##
115
+ # The following call is meant for Applications that have the OTA upgrade
116
+ # enabled. In case of wrong password a "bad password" message is sent in reply
117
+ #
118
+ # Let's send a initialization package for starting OTA upgrade
119
+ @xbee.init_ota_upgrade(PASSWORD, TARGET_ADDRESS, TARGET_NETWORK)
120
+
121
+ # main loop
122
+ while 1
123
+ begin
124
+
125
+ r = @xbee.getresponse true
126
+
127
+ if r.kind_of?(XBee::Frame::ExplicitRxIndicator) and r.cluster_id == 0x0011 and r.profile_id == 0xC105
128
+ if !@transfer_started and r.received_data == "C"
129
+ puts "XBEE: Got response with frame type: 0x%02x" % r.api_identifier
130
+ puts "XBEE: Source address of the one waiting for new firmware: %x" % r.source_address
131
+ # Connect to socket
132
+ if ENV['OS'] != "Windows_NT"
133
+ socket = UNIXSocket.open(@unix_socket)
134
+ else
135
+ socket = TCPSocket.new('localhost', LOCAL_PORT)
136
+ end
137
+ socket.sync=true
138
+ @transfer_started = true
139
+ puts "Connected to socket"
140
+ sleep(0.1)
141
+ end
142
+
143
+ if r.received_data == "C"
144
+ print "C"
145
+ elsif r.received_data.ord == XMODEM::ACK
146
+ print "O"
147
+ elsif r.received_data.ord == XMODEM::NAK
148
+ print "X"
149
+ elsif r.received_data == "F" and !@transfer_started
150
+ puts "F - Upgrade started from bootloader"
151
+ next
152
+ else
153
+ print "?"
154
+ end
155
+
156
+ unless socket.closed?
157
+ # As we have the socket open, we can presume the tranmission is ok to
158
+ # start.
159
+ if transfer_thread.alive? and @transfer_eot < 1
160
+ # Send the received payload to local XMODEM socket
161
+ socket.write(r.received_data)
162
+
163
+ # Try to get the block that the XMODEM wants to send
164
+ # 3 header bytes, data block, 2 bytes checksum
165
+ # We read the first byte separately in case it's the end of file
166
+ data_to_send = ""
167
+ b = XMODEM::receive_getbyte(socket)
168
+ data_to_send << b
169
+
170
+ if data_to_send.ord == XMODEM::SOH
171
+ (XMODEM::block_size+4).times do
172
+ b = XMODEM::receive_getbyte(socket)
173
+ data_to_send << b
174
+ end
175
+ elsif data_to_send.ord == XMODEM::EOT
176
+ print "<END-OF-TRANSMISSION>"
177
+ @transfer_eot += 1
178
+ end
179
+
180
+ #puts "Inspecting packet to encapsulate: #{data_to_send.inspect}"
181
+
182
+ # We have the XMODEM block, we should encapsulate and send it to the radio
183
+ frame_id = @xbee.next_frame_id
184
+ frame_to_send = XBee::Frame::ExplicitAddressingCommand.new(frame_id, r.source_address, r.source_network, 0xE8, 0xE8, 0x0011, 0xC105, 0x00, 0x00, data_to_send)
185
+ @xbee.xbee_serialport.write(frame_to_send._dump)
186
+ print "."
187
+ else
188
+ # Transfer completed
189
+ socket.close
190
+ puts "XBEE: Transfer completed!"
191
+ break
192
+ end
193
+ end
194
+ elsif r.kind_of?(XBee::Frame::ExplicitRxIndicator) and r.cluster_id == 0x1000 and r.profile_id == 0xC105 and r.received_data == "bad password"
195
+ puts "XBEE: Bad password sent for device: 0x%x. Giving up." % r.source_address
196
+ break
197
+ end
198
+
199
+ if r.kind_of?(XBee::Frame::TransmitStatus)
200
+ #puts "XBEE: Got response with frame type: 0x%02x" % r.api_identifier
201
+ #puts "XBEE: cmd_data: #{r.cmd_data.inspect}"
202
+ end
203
+
204
+ rescue RuntimeError => e
205
+ puts e
206
+ end
207
+ end
208
+
209
+ unless ENV['OS'] == "Windows_NT"
210
+ File.delete( @unix_socket ) if FileTest.exists?( @unix_socket )
211
+ end
212
+
213
+ puts "Exiting!"
@@ -1,7 +1,52 @@
1
1
  module XBee
2
2
  module Frame
3
+ ##
4
+ # Explicit Addressing ZigBee Command Frame (0x11)
5
+ #
6
+ # This frame allows ZigBee Application Layer fields to be specified for
7
+ # a data transmission. This frame is similar to Zigbee Transmit Request (0x10)
8
+ # but requires the ZigBee application layer addressing fields to be set.
9
+ #
10
+ # This frame is also used for triggering Programmable XBee (S2B) module
11
+ # Over-the-Air firmware upgrade process.
12
+ #
3
13
  class ExplicitAddressingCommand < Base
4
- def api_identifier ; 0x11 ; end
14
+ def api_identifier ; 0x11 ; end
15
+
16
+ attr_accessor :destination_address, :destination_network, :source_endpoint, :destination_endpoint, :cluster_id, :profile_id, :broadcast_radius, :transmit_options, :payload, :payload_pack_string
17
+
18
+ def initialize(frame_id = nil, destination_address = 0x000000000000ffff, destination_network = 0x0000fffe, source_endpoint = nil, destination_endpoint = nil, cluster_id = nil, profile_id = nil, broadcast_radius = 0x00, transmit_options = 0x00, payload = nil, payload_pack_string = "a*")
19
+ self.frame_id = frame_id
20
+ self.destination_address = destination_address
21
+ self.destination_network = destination_network
22
+ self.source_endpoint = source_endpoint
23
+ self.destination_endpoint = destination_endpoint
24
+ self.cluster_id = cluster_id
25
+ self.profile_id = profile_id
26
+ self.broadcast_radius = broadcast_radius
27
+ self.transmit_options = transmit_options
28
+ self.payload = payload
29
+ self.payload_pack_string = payload_pack_string
30
+ end
31
+
32
+ def cmd_data=(data_string)
33
+ # We need to read in the 64-bit destination_address in two 32-bit parts.
34
+ dest_high = dest_low = 0
35
+ self.frame_id, dest_high, dest_low, self.destination_network, self.source_endpoint, self.destination_endpoint, self.cluster_id, self.profile_id, self.broadcast_radius, self.transmit_options, self.payload = data_string.unpack("CNNnCCnnCC#{payload_pack_string}")
36
+ self.destination_address = dest_high << 32 | dest_low
37
+ end
38
+
39
+ def cmd_data
40
+ # We need to pack the 64-bit destination_address in two 32-bit parts.
41
+ dest_high = (self.destination_address >> 32) & 0xFFFFFFFF
42
+ dest_low = self.destination_address & 0xFFFFFFFF
43
+ if payload.nil?
44
+ # By default we send a null-byte payload \0
45
+ [self.frame_id, dest_high, dest_low, self.destination_network, self.source_endpoint, self.destination_endpoint, self.cluster_id, self.profile_id, self.broadcast_radius, self.transmit_options, self.payload].pack("CNNnCCnnCCx")
46
+ else
47
+ [self.frame_id, dest_high, dest_low, self.destination_network, self.source_endpoint, self.destination_endpoint, self.cluster_id, self.profile_id, self.broadcast_radius, self.transmit_options, self.payload].pack("CNNnCCnnCC#{payload_pack_string}")
48
+ end
49
+ end
5
50
  end
6
51
  end
7
52
  end
@@ -1,6 +1,23 @@
1
1
  module XBee
2
2
  module Frame
3
+ ##
4
+ # ZigBee Explicit Rx Indicator (0x91) (AO=1)
3
5
  class ExplicitRxIndicator < ReceivedFrame
6
+ def api_identifier ; 0x91 ; end
7
+ attr_accessor :source_address, :source_network, :source_endpoint, :destination_endpoint, :cluster_id, :profile_id, :receive_options, :received_data
8
+
9
+ def initialize(data = nil)
10
+ super(data) && (yield self if block_given?)
11
+ end
12
+
13
+ def cmd_data=(data_string)
14
+ # We need to read in the 64-bit source_address in two 32-bit parts.
15
+ src_high = src_low = 0
16
+ src_high, src_low, self.source_network, self.source_endpoint, self.destination_endpoint, self.cluster_id, self.profile_id, self.receive_options, self.received_data = data_string.unpack("NNnCCnnCa*")
17
+ self.source_address = src_high << 32 | src_low
18
+ @cmd_data = data_string
19
+ end
20
+
4
21
  end
5
22
  end
6
23
  end
@@ -15,21 +15,48 @@ module XBee
15
15
  0xFF - (data.unpack("C*").inject(0) { |sum, byte| (sum + byte) & 0xFF })
16
16
  end
17
17
 
18
- def Frame.new(source_io)
18
+ def Frame.new(source_io, api_mode = :API1)
19
19
  stray_bytes = []
20
+ unless api_mode == :API1 or api_mode == :API2
21
+ raise "XBee api_mode must be either :API1 (non-escaped) or :API2 (escaped, default)"
22
+ end
20
23
  until (start_delimiter = source_io.readchar.unpack('H*').join.to_i(16)) == 0x7e
21
24
  #puts "Stray byte 0x%x" % start_delimiter
22
25
  print "DEBUG: #{start_delimiter} | " if $DEBUG
23
26
  stray_bytes << start_delimiter
24
27
  end
25
- puts "Got some stray bytes for ya: #{stray_bytes.map {|b| "0x%x" % b} .join(", ")}" unless stray_bytes.empty?
26
- header = source_io.read(3).xb_unescape
28
+ if $VERBOSE
29
+ puts "Got some stray bytes for ya: #{stray_bytes.map {|b| "0x%x" % b} .join(", ")}" unless stray_bytes.empty?
30
+ end
31
+ if(api_mode == :API1)
32
+ header = source_io.read(3)
33
+ elsif(api_mode == :API2)
34
+ header = source_io.read(2).xb_unescape
35
+ end
27
36
  print "Reading ... header after start byte: #{header.unpack("C*").join(", ")} | " if $DEBUG
28
37
  frame_remaining = frame_length = api_identifier = cmd_data = ""
29
- if header.length == 3
30
- frame_length, api_identifier = header.unpack("nC")
38
+ if api_mode == :API2
39
+ if header.length == 2
40
+ frame_length = header.unpack("n").first
41
+ else
42
+ read_extra_byte = source_io.readchar
43
+ if(read_extra_byte == "\175")
44
+ # We stumbled upon another escaped character, read another byte and unescape
45
+ read_extra_byte += source_io.readchar
46
+ read_extra_byte = read_extra_byte.xb_unescape
47
+ else
48
+ header += read_extra_byte
49
+ end
50
+ frame_length = header.unpack("n")
51
+ end
52
+ api_identifier = source_io.readchar
53
+ if(api_identifier == "\175")
54
+ api_identifier += source_io.readchar
55
+ api_identifier = api_identifier.xb_unescape
56
+ end
57
+ api_identifier = api_identifier.unpack("C").first
31
58
  else
32
- frame_length, api_identifier = header.unpack("n").first, source_io.readchar
59
+ frame_length, api_identifier = header.unpack("nC")
33
60
  end
34
61
  #### DEBUG ####
35
62
  if $DEBUG then
@@ -38,10 +65,17 @@ module XBee
38
65
  end
39
66
  #### DEBUG ####
40
67
  cmd_data_intended_length = frame_length - 1
41
- while ((unescaped_length = cmd_data.xb_unescape.length) < cmd_data_intended_length)
42
- cmd_data += source_io.read(cmd_data_intended_length - unescaped_length)
68
+ if api_mode == :API2
69
+ while ((unescaped_length = cmd_data.xb_unescape.length) < cmd_data_intended_length)
70
+ cmd_data += source_io.read(cmd_data_intended_length - unescaped_length)
71
+ end
72
+ data = api_identifier.chr + cmd_data.xb_unescape
73
+ else
74
+ while (cmd_data.length < cmd_data_intended_length)
75
+ cmd_data += source_io.read(cmd_data_intended_length)
76
+ end
77
+ data = api_identifier.chr + cmd_data
43
78
  end
44
- data = api_identifier.chr + cmd_data.xb_unescape
45
79
  sent_checksum = source_io.getc.unpack('H*').join.to_i(16)
46
80
  #### DEBUG ####
47
81
  if $DEBUG then
@@ -56,15 +90,15 @@ module XBee
56
90
  case data[0].unpack('H*')[0].to_i(16)
57
91
  when 0x8A
58
92
  ModemStatus.new(data)
59
- when 0x88
93
+ when 0x88
60
94
  ATCommandResponse.new(data)
61
- when 0x97
95
+ when 0x97
62
96
  RemoteCommandResponse.new(data)
63
- when 0x8B
97
+ when 0x8B
64
98
  TransmitStatus.new(data)
65
- when 0x90
99
+ when 0x90
66
100
  ReceivePacket.new(data)
67
- when 0x91
101
+ when 0x91
68
102
  ExplicitRxIndicator.new(data)
69
103
  when 0x92
70
104
  IODataSampleRxIndicator.new(data)
@@ -88,22 +122,29 @@ module XBee
88
122
  Array(api_identifier).pack("C") + cmd_data
89
123
  end
90
124
 
91
- def _dump
125
+ def _dump(api_mode = :API1)
126
+ unless api_mode == :API1 or api_mode == :API2
127
+ raise "XBee api_mode must be either :API1 (non-escaped) or :API2 (escaped, default)"
128
+ end
92
129
  raise "Too much data (#{self.length} bytes) to fit into one frame!" if (self.length > 0xFFFF)
93
- "~" + [length].pack("n").xb_escape + data.xb_escape + [Frame.checksum(data)].pack("C")
130
+
131
+ if (api_mode == :API1)
132
+ "~" + [length].pack("n") + data + [Frame.checksum(data)].pack("C")
133
+ elsif (api_mode == :API2)
134
+ "~" + [length].pack("n").xb_escape + data.xb_escape + [Frame.checksum(data)].pack("C")
135
+ end
94
136
  end
95
137
  end
96
138
 
97
139
  class ReceivedFrame < Base
98
140
  def initialize(frame_data)
99
- # raise "Frame data must be an enumerable type" unless frame_data.kind_of?(Enumerable)
100
- self.api_identifier = frame_data[0]
141
+ self.api_identifier = frame_data[0].unpack('H*').join.to_i(16) unless frame_data.nil?
101
142
  if $DEBUG then
102
- print "Initializing a ReceivedFrame of type 0x%x | " % self.api_identifier.unpack('H*').join.to_i(16)
103
- else
104
- puts "Initializing a ReceivedFrame of type 0x%x" % self.api_identifier.unpack('H*').join.to_i(16)
143
+ print "Initializing a ReceivedFrame of type 0x%02x | " % self.api_identifier
144
+ elsif $VERBOSE
145
+ puts "Initializing a ReceivedFrame of type 0x%02x" % self.api_identifier
105
146
  end
106
- self.cmd_data = frame_data[1..-1]
147
+ self.cmd_data = frame_data[1..-1] unless frame_data.nil?
107
148
  end
108
149
  end
109
150
  end
@@ -120,4 +161,3 @@ require 'remote_command_request'
120
161
  require 'remote_command_response'
121
162
  require 'transmit_request'
122
163
  require 'transmit_status'
123
-
@@ -22,7 +22,7 @@ module XBee
22
22
  when 0..2
23
23
  modem_statuses.assoc(status_byte)
24
24
  else
25
- raise "ModemStatus frame appears to include an invalid status value: #{data_string}"
25
+ raise "ModemStatus frame appears to include an invalid status value: 0x%02x" % status_byte
26
26
  end
27
27
  #actually assign and move along
28
28
  @cmd_data = data_string
@@ -12,7 +12,7 @@ module XBee
12
12
  when 0..4
13
13
  command_statuses[status_byte]
14
14
  else
15
- raise "AT Command Response frame appears to include an invalid status: 0x%x" % status_byte
15
+ raise "AT Command Response frame appears to include an invalid status: 0x%02x" % status_byte
16
16
  end
17
17
  #actually assign and move along
18
18
  @cmd_data = data_string
@@ -3,11 +3,42 @@ require 'frame/frame'
3
3
  require File.dirname(File.dirname(__FILE__)) + '/ruby_xbee'
4
4
 
5
5
  module XBee
6
+ ##
7
+ # This is the main class for API mode for XBee radios.
6
8
  class BaseAPIModeInterface < RFModule
7
-
8
- VERSION = "1.1.0" # Version of this class
9
-
10
- def initialize(xbee_usbdev_str, uart_config = XBeeUARTConfig.new, operation_mode = :AT, transmission_mode = :SYNC)
9
+
10
+ VERSION = "1.2.0" # Version of this class
11
+
12
+ ##
13
+ # ==== Attributes
14
+ # * +xbee_usbdev_str+ - USB Device as a string
15
+ #
16
+ # ==== Options
17
+ # * +uart_config+ - XBeeUARTConfig
18
+ # * +operation_mode+ - Either :AT or :API for XBee operation mode
19
+ # * +transmission_mode+ - :SYNC for Synchronous communication or :ASYNC for Asynchonrous communication.
20
+ #
21
+ # A note on the asynchronous vs synchronous communication modes - A
22
+ # simplistic network of a few XBee nodes can pretty much work according to
23
+ # expected flows where requests and responses are always handled in
24
+ # synchronous ways. However, if bigger radio networks are being deployed
25
+ # (real scenarios) you cannot guarantee the synchronous nature of the network.
26
+ # You will have nodes joining/removing themselves, sleeping, sending data
27
+ # samples etc. Although the default behaviour is set as :SYNC, if you have a
28
+ # real network, then by design the network is Asynchronous, use :ASYNC instead.
29
+ # Otherwise you will most definitely run into "Invalid responses" issues.
30
+ #
31
+ # For handling the Asynchronous communication logic, use an external Queue
32
+ # and database to effectively handle the command/response and other frames
33
+ # that are concurrently being conversed on the PAN.
34
+ #
35
+ # ==== Example
36
+ # require 'ruby_xbee'
37
+ # @uart_config = XBee::Config::XBeeUARTConfig.new()
38
+ # @xbee_usbdev_str = '/dev/tty.usbserial-A101KYF6'
39
+ # @xbee = XBee::BaseAPIModeInterface.new(@xbee_usbdev_str, @uart_config, :API, :ASYNC)
40
+ #
41
+ def initialize(xbee_usbdev_str, uart_config = XBeeUARTConfig.new, operation_mode = :API, transmission_mode = :SYNC)
11
42
  super(xbee_usbdev_str, uart_config, operation_mode, transmission_mode)
12
43
  @frame_id = 1
13
44
  if self.operation_mode == :AT
@@ -23,11 +54,14 @@ module XBee
23
54
  # Switch to API mode - note that in Series 2 the Operation Mode is defined
24
55
  # by the firmware flashed to the device. Only Series 1 can switch from
25
56
  # AT (Transparent) to API Opearation and back seamlessly.
57
+ #
58
+ # API Mode 1 - API Enabled
59
+ # API Mode 2 - API Enabled, with escaped control characters
26
60
  def start_apimode_communication
27
61
  in_command_mode do
28
62
  puts "Entering api mode"
29
63
  # Set API Mode 2 (include escaped characters)
30
- self.xbee_serialport.write("ATAP2\r")
64
+ self.xbee_serialport.write("ATAP1\r")
31
65
  self.xbee_serialport.read(3)
32
66
  end
33
67
  end
@@ -35,10 +69,10 @@ module XBee
35
69
  def get_param(at_param_name, at_param_unpack_string = nil)
36
70
  frame_id = self.next_frame_id
37
71
  at_command_frame = XBee::Frame::ATCommand.new(at_param_name,frame_id,nil,at_param_unpack_string)
38
- puts "Sending ... [#{at_command_frame._dump.unpack("C*").join(", ")}]"
39
- self.xbee_serialport.write(at_command_frame._dump)
72
+ puts "Sending ... [#{at_command_frame._dump(self.api_mode.in_symbol).unpack("C*").join(", ")}]" if $VERBOSE
73
+ self.xbee_serialport.write(at_command_frame._dump(self.api_mode.in_symbol))
40
74
  if self.transmission_mode == :SYNC
41
- r = XBee::Frame.new(self.xbee_serialport)
75
+ r = XBee::Frame.new(self.xbee_serialport, self.api_mode.in_symbol)
42
76
  if r.kind_of?(XBee::Frame::ATCommandResponse) && r.status == :OK && r.frame_id == frame_id
43
77
  if block_given?
44
78
  yield r
@@ -60,10 +94,10 @@ module XBee
60
94
  def set_param(at_param_name, param_value, at_param_unpack_string = nil)
61
95
  frame_id = self.next_frame_id
62
96
  at_command_frame = XBee::Frame::ATCommand.new(at_param_name,frame_id,param_value,at_param_unpack_string)
63
- # puts "Sending ... [#{at_command_frame._dump.unpack("C*").join(", ")}]"
64
- self.xbee_serialport.write(at_command_frame._dump)
97
+ # puts "Sending ... [#{at_command_frame._dump(self.api_mode.in_symbol).unpack("C*").join(", ")}]"
98
+ self.xbee_serialport.write(at_command_frame._dump(self.api_mode.in_symbol))
65
99
  if self.transmission_mode == :SYNC
66
- r = XBee::Frame.new(self.xbee_serialport)
100
+ r = XBee::Frame.new(self.xbee_serialport, self.api_mode.in_symbol)
67
101
  if r.kind_of?(XBee::Frame::ATCommandResponse) && r.status == :OK && r.frame_id == frame_id
68
102
  if block_given?
69
103
  yield r
@@ -79,10 +113,10 @@ module XBee
79
113
  def get_remote_param(at_param_name, remote_address = 0x000000000000ffff, remote_network_address = 0xfffe, at_param_unpack_string = nil)
80
114
  frame_id = self.next_frame_id
81
115
  at_command_frame = XBee::Frame::RemoteCommandRequest.new(at_param_name, remote_address, remote_network_address, frame_id, nil, at_param_unpack_string)
82
- puts "Sending ... [#{at_command_frame._dump.unpack("C*").join(", ")}]"
83
- self.xbee_serialport.write(at_command_frame._dump)
116
+ puts "Sending ... [#{at_command_frame._dump(self.api_mode.in_symbol).unpack("C*").join(", ")}]"
117
+ self.xbee_serialport.write(at_command_frame._dump(self.api_mode.in_symbol))
84
118
  if self.transmission_mode == :SYNC
85
- r = XBee::Frame.new(self.xbee_serialport)
119
+ r = XBee::Frame.new(self.xbee_serialport, self.api_mode.in_symbol)
86
120
  if r.kind_of?(XBee::Frame::RemoteCommandResponse) && r.status == :OK && r.frame_id == frame_id
87
121
  if block_given?
88
122
  yield r
@@ -98,10 +132,10 @@ module XBee
98
132
  def set_remote_param(at_param_name, param_value, remote_address = 0x000000000000ffff, remote_network_address = 0xfffe, at_param_unpack_string = nil)
99
133
  frame_id = self.next_frame_id
100
134
  at_command_frame = XBee::Frame::RemoteCommandRequest.new(at_param_name, remote_address, remote_network_address, frame_id, param_value, at_param_unpack_string)
101
- puts "Sending ... [#{at_command_frame._dump.unpack("C*").join(", ")}]"
102
- self.xbee_serialport.write(at_command_frame._dump)
135
+ puts "Sending ... [#{at_command_frame._dump(self.api_mode.in_symbol).unpack("C*").join(", ")}]"
136
+ self.xbee_serialport.write(at_command_frame._dump(self.api_mode.in_symbol))
103
137
  if self.transmission_mode == :SYNC
104
- r = XBee::Frame.new(self.xbee_serialport)
138
+ r = XBee::Frame.new(self.xbee_serialport, self.api_mode.in_symbol)
105
139
  if r.kind_of?(XBee::Frame::RemoteCommandResponse) && r.status == :OK && r.frame_id == frame_id
106
140
  if block_given?
107
141
  yield r
@@ -154,15 +188,15 @@ module XBee
154
188
  frame_id = self.next_frame_id
155
189
  # neighbors often takes more than 1000ms to return data
156
190
  node_discover_cmd = XBee::Frame::ATCommand.new("ND",frame_id,nil)
157
- #puts "Node discover command dump: #{node_discover_cmd._dump.unpack("C*").join(", ")}"
191
+ #puts "Node discover command dump: #{node_discover_cmd._dump(self.api_mode.in_symbol).unpack("C*").join(", ")}"
158
192
  tmp = @xbee_serialport.read_timeout
159
193
  @xbee_serialport.read_timeout = Integer(self.node_discover_timeout.in_seconds * 1050)
160
- @xbee_serialport.write(node_discover_cmd._dump)
194
+ @xbee_serialport.write(node_discover_cmd._dump(self.api_mode.in_symbol))
161
195
  responses = []
162
196
  #read_thread = Thread.new do
163
197
  begin
164
198
  loop do
165
- r = XBee::Frame.new(self.xbee_serialport)
199
+ r = XBee::Frame.new(self.xbee_serialport, self.api_mode.in_symbol)
166
200
  # puts "Got a response! Frame ID: #{r.frame_id}, Command: #{r.at_command}, Status: #{r.status}, Value: #{r.retrieved_value}"
167
201
  if r.kind_of?(XBee::Frame::ATCommandResponse) && r.status == :OK && r.frame_id == frame_id
168
202
  if r.retrieved_value.empty?
@@ -529,7 +563,7 @@ module XBee
529
563
  def reset!
530
564
  @xbee_serialport.write("ATFR\r")
531
565
  end
532
-
566
+
533
567
  ##
534
568
  # Performs a network reset on one or more modules within a PAN. The module responds
535
569
  # immediately with an "OK" and then restarts the network. All network configuration
@@ -586,7 +620,7 @@ module XBee
586
620
  # echo is disabled by default
587
621
  def getresponse( echo = false )
588
622
  if echo == true
589
- r = XBee::Frame.new(self.xbee_serialport)
623
+ r = XBee::Frame.new(self.xbee_serialport, self.api_mode.in_symbol)
590
624
  else
591
625
  getresults( @xbee_serialport, echo )
592
626
  end
@@ -603,6 +637,12 @@ module XBee
603
637
  end # class Series1APIModeInterface
604
638
 
605
639
  class Series2APIModeInterface < BaseAPIModeInterface
606
-
640
+ ##
641
+ # Initiating the application firmware OTA upgrade for Programmable XBee modules
642
+ def init_ota_upgrade(password = nil, remote_address = 0x000000000000ffff, remote_network_address = 0xfffe)
643
+ frame_id = self.next_frame_id
644
+ command_frame = XBee::Frame::ExplicitAddressingCommand.new(frame_id, remote_address, remote_network_address, 0xE8, 0xE8, 0x1000, 0xC105, 0x00, 0x00, password)
645
+ self.xbee_serialport.write(command_frame._dump(self.api_mode.in_symbol))
646
+ end
607
647
  end # class Series2APIModeInterface
608
648
  end