ruby-xbee 1.1.0 → 1.2.0

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.
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