ruxbee 0.1.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 +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE +11 -0
- data/README.md +36 -0
- data/README.rdoc +233 -0
- data/Rakefile +6 -0
- data/agpl.txt +661 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/examples/listen_api.rb +35 -0
- data/icon.jpg +0 -0
- data/lib/ruxbee/at_commands.rb +28 -0
- data/lib/ruxbee/config.rb +96 -0
- data/lib/ruxbee/frame/at_command.rb +35 -0
- data/lib/ruxbee/frame/at_command_response.rb +33 -0
- data/lib/ruxbee/frame/base_frame.rb +32 -0
- data/lib/ruxbee/frame/explicit_addressing_command.rb +52 -0
- data/lib/ruxbee/frame/explicit_rx_indicator.rb +23 -0
- data/lib/ruxbee/frame/frame.rb +128 -0
- data/lib/ruxbee/frame/io_data_sample_rx_indicator.rb +10 -0
- data/lib/ruxbee/frame/modem_status.rb +32 -0
- data/lib/ruxbee/frame/receive_packet.rb +6 -0
- data/lib/ruxbee/frame/receive_packet_16.rb +29 -0
- data/lib/ruxbee/frame/received_frame.rb +16 -0
- data/lib/ruxbee/frame/remote_command_request.rb +34 -0
- data/lib/ruxbee/frame/remote_command_response.rb +22 -0
- data/lib/ruxbee/frame/transmit_request.rb +7 -0
- data/lib/ruxbee/frame/transmit_status.rb +6 -0
- data/lib/ruxbee/rfmodule.rb +85 -0
- data/lib/ruxbee/version.rb +3 -0
- data/lib/ruxbee/xbee_api.rb +642 -0
- data/lib/ruxbee/xbee_cmd.rb +554 -0
- data/lib/ruxbee.rb +37 -0
- data/ruxbee.gemspec +34 -0
- metadata +140 -0
data/bin/setup
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "ruxbee"
|
5
|
+
require 'pp'
|
6
|
+
require 'getoptlong'
|
7
|
+
|
8
|
+
|
9
|
+
opts = GetoptLong.new(
|
10
|
+
[ '--device', '-d', GetoptLong::REQUIRED_ARGUMENT ],
|
11
|
+
[ '--speed', '-s', GetoptLong::REQUIRED_ARGUMENT ]
|
12
|
+
)
|
13
|
+
|
14
|
+
@dev = nil
|
15
|
+
@speed = nil
|
16
|
+
|
17
|
+
opts.each do |opt, arg|
|
18
|
+
case opt
|
19
|
+
when '--device'
|
20
|
+
@dev = arg
|
21
|
+
when '--speed'
|
22
|
+
@speed = arg.to_i
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
@xbee = XBee.new @dev, @speed, :API_S1
|
27
|
+
|
28
|
+
loop do
|
29
|
+
res = @xbee.getresponse
|
30
|
+
if not res.nil? and res.api_identifier == '81'
|
31
|
+
res = {rssi: res.rssi, address: res.address_16_bits, api_frame_id: res.api_identifier, data: res.cmd_data}
|
32
|
+
pp res
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
data/icon.jpg
ADDED
Binary file
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module XBee
|
2
|
+
module ATCommands
|
3
|
+
class ParameterDescriptor
|
4
|
+
def initialize
|
5
|
+
yield self if block_given?
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class CommandDescriptor
|
10
|
+
attr_reader :command, :command_name, :command_description, :parameter
|
11
|
+
|
12
|
+
def initialize (command, command_name, command_description = nil, parameter = nil)
|
13
|
+
@command = command
|
14
|
+
@command_name = command_name
|
15
|
+
@command_description = command_description
|
16
|
+
@parameter = parameter
|
17
|
+
yield self if block_given?
|
18
|
+
end
|
19
|
+
|
20
|
+
def has_parameter?
|
21
|
+
parameter.nil?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
AP_PARAM_DESCRIPTOR = ParameterDescriptor.new
|
26
|
+
AP = CommandDescriptor.new("AP","API Mode","0 = off; 1 = on, unescaped; 2 = on, escaped", AP_PARAM_DESCRIPTOR)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
|
2
|
+
module XBee
|
3
|
+
module Config
|
4
|
+
##
|
5
|
+
# A class for encapsulating UART communication parameters
|
6
|
+
|
7
|
+
class XBeeUARTConfig
|
8
|
+
attr_accessor :baud, :data_bits, :parity, :stop_bits
|
9
|
+
|
10
|
+
##
|
11
|
+
# Defaults to standard 9600 baud 8N1 communications
|
12
|
+
def initialize(baud = 9600, data_bits = 8, parity = 0, stop_bits = 1)
|
13
|
+
self.baud = Integer(baud)
|
14
|
+
self.data_bits = Integer(data_bits)
|
15
|
+
self.parity = Integer(parity)
|
16
|
+
self.stop_bits = Integer(stop_bits)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# A class for encapsulating XBee programmable parameters
|
23
|
+
class RFModuleParameter
|
24
|
+
attr_accessor :at_name, :value, :default_value, :retrieved, :operation_mode
|
25
|
+
|
26
|
+
def initialize(at_name, default_value)
|
27
|
+
self.at_name= at_name
|
28
|
+
self.default_value = default_value
|
29
|
+
self.value = default_value
|
30
|
+
self.retrieved = false
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
class GuardTime < RFModuleParameter
|
36
|
+
def initialize(default = 0x3E8)
|
37
|
+
super("GT", default)
|
38
|
+
end
|
39
|
+
|
40
|
+
def in_seconds
|
41
|
+
self.value / 1000.0
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class CommandModeTimeout < RFModuleParameter
|
46
|
+
def initialize(default = 0x64)
|
47
|
+
super("CT", default)
|
48
|
+
end
|
49
|
+
|
50
|
+
def in_seconds
|
51
|
+
self.value / 1000.0
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class CommandCharacter < RFModuleParameter
|
56
|
+
def initialize(default = '+')
|
57
|
+
super("CC", default)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
class NodeDiscoverTimeout < RFModuleParameter
|
63
|
+
def initialize(default = 0x82)
|
64
|
+
super("NT", default)
|
65
|
+
end
|
66
|
+
|
67
|
+
def in_seconds
|
68
|
+
self.value / 10.0
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class NodeIdentifier < RFModuleParameter
|
73
|
+
def initialize(default = " ")
|
74
|
+
super("NI", default)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class ApiEnableMode < RFModuleParameter
|
79
|
+
def initialize(default = 0x01)
|
80
|
+
unless default == 0x01 or default == 0x02
|
81
|
+
raise "XBee AP parameter range can be 1-2; 1 = API-enabled; 2 = API-enabled (with escaped control characters)"
|
82
|
+
end
|
83
|
+
super("AP", default)
|
84
|
+
end
|
85
|
+
|
86
|
+
def in_symbol
|
87
|
+
unless self.value == 0x01 or self.value == 0x02
|
88
|
+
raise "XBee AP parameter invalid range! Valid range 1-2; Set to: #{self.value}"
|
89
|
+
end
|
90
|
+
return :API1 if self.value == 0x01
|
91
|
+
return :API2 if self.value == 0x02
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
module XBee
|
3
|
+
module Frame
|
4
|
+
class ATCommand < XBee::Frame::Base
|
5
|
+
def api_identifier ; 0x08 ; end
|
6
|
+
|
7
|
+
attr_accessor :at_command, :parameter_value, :parameter_pack_string
|
8
|
+
|
9
|
+
def initialize(at_command, frame_id = nil, parameter_value = nil, parameter_pack_string = "a*")
|
10
|
+
self.frame_id = frame_id
|
11
|
+
self.at_command = at_command # TODO: Check for valid AT command codes here
|
12
|
+
self.parameter_value = parameter_value
|
13
|
+
self.parameter_pack_string = parameter_pack_string
|
14
|
+
yield self if block_given?
|
15
|
+
end
|
16
|
+
|
17
|
+
def cmd_data=(data_string)
|
18
|
+
self.frame_id, self.at_command, self.parameter_value = data_string.unpack("ca2#{parameter_pack_string}")
|
19
|
+
end
|
20
|
+
|
21
|
+
def cmd_data
|
22
|
+
if parameter_value.nil?
|
23
|
+
[frame_id, at_command].pack("ca2")
|
24
|
+
else
|
25
|
+
[frame_id, at_command, parameter_value].pack("ca2#{parameter_pack_string}")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class ATCommandQueueParameterValue < ATCommand
|
31
|
+
def api_identifier ; 0x09 ; end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module XBee
|
2
|
+
module Frame
|
3
|
+
class ATCommandResponse < ReceivedFrame
|
4
|
+
attr_accessor :frame_id, :at_command, :status, :retrieved_value
|
5
|
+
|
6
|
+
def initialize(data = nil)
|
7
|
+
super(data) && (yield self if block_given?)
|
8
|
+
end
|
9
|
+
|
10
|
+
def command_statuses
|
11
|
+
[:OK, :ERROR, :Invalid_Command, :Invalid_Parameter]
|
12
|
+
end
|
13
|
+
|
14
|
+
def cmd_data=(data_string)
|
15
|
+
self.frame_id, self.at_command, status_byte, self.retrieved_value = data_string.unpack("Ca2Ca*")
|
16
|
+
self.status = case status_byte
|
17
|
+
when 0..3
|
18
|
+
command_statuses[status_byte]
|
19
|
+
else
|
20
|
+
raise "AT Command Response frame appears to include an invalid status: 0x%x" % status_byte
|
21
|
+
end
|
22
|
+
#actually assign and move along
|
23
|
+
@cmd_data = data_string
|
24
|
+
#### DEBUG ####
|
25
|
+
if $DEBUG then
|
26
|
+
print "Retrieved Value: #{self.retrieved_value.unpack('C*').join(', ')} | "
|
27
|
+
print "Retrieved Value: #{self.retrieved_value.unpack('a*')} | "
|
28
|
+
end
|
29
|
+
#### DEBUG ####
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module XBee
|
2
|
+
module Frame
|
3
|
+
|
4
|
+
class Base
|
5
|
+
attr_accessor :api_identifier, :cmd_data, :frame_id
|
6
|
+
|
7
|
+
def api_identifier ; @api_identifier ||= 0x00 ; end
|
8
|
+
|
9
|
+
def cmd_data ; @cmd_data ||= "" ; end
|
10
|
+
|
11
|
+
def length ; data.length ; end
|
12
|
+
|
13
|
+
def data
|
14
|
+
Array(api_identifier).pack("C") + cmd_data
|
15
|
+
end
|
16
|
+
|
17
|
+
def _dump(api_mode = :API1)
|
18
|
+
unless api_mode == :API1 or api_mode == :API2
|
19
|
+
raise "XBee api_mode must be either :API1 (non-escaped) or :API2 (escaped, default)"
|
20
|
+
end
|
21
|
+
raise "Too much data (#{self.length} bytes) to fit into one frame!" if (self.length > 0xFFFF)
|
22
|
+
|
23
|
+
if (api_mode == :API1)
|
24
|
+
"~" + [length].pack("n") + data + [Frame.checksum(data)].pack("C")
|
25
|
+
elsif (api_mode == :API2)
|
26
|
+
"~" + [length].pack("n").xb_escape + data.xb_escape + [Frame.checksum(data)].pack("C")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module XBee
|
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
|
+
#
|
13
|
+
class ExplicitAddressingCommand < Base
|
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
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module XBee
|
2
|
+
module Frame
|
3
|
+
##
|
4
|
+
# ZigBee Explicit Rx Indicator (0x91) (AO=1)
|
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
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'ruxbee/frame/base_frame'
|
2
|
+
require 'ruxbee/frame/received_frame'
|
3
|
+
require 'ruxbee/frame/at_command'
|
4
|
+
require 'ruxbee/frame/at_command_response'
|
5
|
+
require 'ruxbee/frame/explicit_addressing_command'
|
6
|
+
require 'ruxbee/frame/explicit_rx_indicator'
|
7
|
+
require 'ruxbee/frame/io_data_sample_rx_indicator'
|
8
|
+
require 'ruxbee/frame/modem_status'
|
9
|
+
require 'ruxbee/frame/receive_packet'
|
10
|
+
require 'ruxbee/frame/remote_command_request'
|
11
|
+
require 'ruxbee/frame/remote_command_response'
|
12
|
+
require 'ruxbee/frame/transmit_request'
|
13
|
+
require 'ruxbee/frame/transmit_status'
|
14
|
+
require 'ruxbee/frame/receive_packet_16'
|
15
|
+
|
16
|
+
class String
|
17
|
+
def xb_escape
|
18
|
+
self.gsub(/[\176\175\021\023]/) { |c| [0x7D, c[0].ord ^ 0x20].pack("CC")}
|
19
|
+
end
|
20
|
+
def xb_unescape
|
21
|
+
self.gsub(/\175./) { |ec| [ec.unpack("CC").last ^ 0x20].pack("C")}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module XBee
|
26
|
+
module Frame
|
27
|
+
def Frame.checksum(data)
|
28
|
+
0xFF - (data.unpack("C*").inject(0) { |sum, byte| (sum + byte) & 0xFF })
|
29
|
+
end
|
30
|
+
|
31
|
+
def Frame.new(source_io, api_mode = :API1)
|
32
|
+
stray_bytes = []
|
33
|
+
unless api_mode == :API1 or api_mode == :API2
|
34
|
+
raise "XBee api_mode must be either :API1 (non-escaped) or :API2 (escaped, default)"
|
35
|
+
end
|
36
|
+
until (start_delimiter = source_io.readchar.unpack('H*').join.to_i(16)) == 0x7e
|
37
|
+
#puts "Stray byte 0x%x" % start_delimiter
|
38
|
+
print "DEBUG: #{start_delimiter} | " if $DEBUG
|
39
|
+
stray_bytes << start_delimiter
|
40
|
+
end
|
41
|
+
if $VERBOSE
|
42
|
+
puts "Got some stray bytes for ya: #{stray_bytes.map {|b| "0x%x" % b} .join(", ")}" unless stray_bytes.empty?
|
43
|
+
end
|
44
|
+
if(api_mode == :API1)
|
45
|
+
header = source_io.read(3)
|
46
|
+
elsif(api_mode == :API2)
|
47
|
+
header = source_io.read(2).xb_unescape
|
48
|
+
end
|
49
|
+
print "Reading ... header after start byte: #{header.unpack("C*").join(", ")} | " if $DEBUG
|
50
|
+
frame_remaining = frame_length = api_identifier = cmd_data = ""
|
51
|
+
if api_mode == :API2
|
52
|
+
if header.length == 2
|
53
|
+
frame_length = header.unpack("n").first
|
54
|
+
else
|
55
|
+
read_extra_byte = source_io.readchar
|
56
|
+
if(read_extra_byte == "\175")
|
57
|
+
# We stumbled upon another escaped character, read another byte and unescape
|
58
|
+
read_extra_byte += source_io.readchar
|
59
|
+
read_extra_byte = read_extra_byte.xb_unescape
|
60
|
+
else
|
61
|
+
header += read_extra_byte
|
62
|
+
end
|
63
|
+
frame_length = header.unpack("n")
|
64
|
+
end
|
65
|
+
api_identifier = source_io.readchar
|
66
|
+
if(api_identifier == "\175")
|
67
|
+
api_identifier += source_io.readchar
|
68
|
+
api_identifier = api_identifier.xb_unescape
|
69
|
+
end
|
70
|
+
api_identifier = api_identifier.unpack("C").first
|
71
|
+
else
|
72
|
+
frame_length, api_identifier = header.unpack("nC")
|
73
|
+
end
|
74
|
+
#### DEBUG ####
|
75
|
+
if $DEBUG then
|
76
|
+
print "Frame length: #{frame_length} | "
|
77
|
+
print "Api Identifier: #{api_identifier} | "
|
78
|
+
end
|
79
|
+
#### DEBUG ####
|
80
|
+
cmd_data_intended_length = frame_length - 1
|
81
|
+
if api_mode == :API2
|
82
|
+
while ((unescaped_length = cmd_data.xb_unescape.length) < cmd_data_intended_length)
|
83
|
+
cmd_data += source_io.read(cmd_data_intended_length - unescaped_length)
|
84
|
+
end
|
85
|
+
data = api_identifier.chr + cmd_data.xb_unescape
|
86
|
+
else
|
87
|
+
while (cmd_data.length < cmd_data_intended_length)
|
88
|
+
cmd_data += source_io.read(cmd_data_intended_length)
|
89
|
+
end
|
90
|
+
data = api_identifier.chr + cmd_data
|
91
|
+
end
|
92
|
+
sent_checksum = source_io.getc.unpack('H*').join.to_i(16)
|
93
|
+
#### DEBUG ####
|
94
|
+
if $DEBUG then
|
95
|
+
print "Sent checksum: #{sent_checksum} | "
|
96
|
+
print "Received checksum: #{Frame.checksum(data)} | "
|
97
|
+
print "Payload: #{cmd_data.unpack("C*").join(", ")} | "
|
98
|
+
end
|
99
|
+
#### DEBUG ####
|
100
|
+
unless sent_checksum == Frame.checksum(data)
|
101
|
+
raise "Bad checksum - data discarded"
|
102
|
+
end
|
103
|
+
case data[0].unpack('H*')[0].to_i(16)
|
104
|
+
when 0x8A
|
105
|
+
ModemStatus.new(data)
|
106
|
+
when 0x81
|
107
|
+
ReceivePacket16.new(data)
|
108
|
+
when 0x88
|
109
|
+
ATCommandResponse.new(data)
|
110
|
+
when 0x97
|
111
|
+
RemoteCommandResponse.new(data)
|
112
|
+
when 0x8B
|
113
|
+
TransmitStatus.new(data)
|
114
|
+
when 0x90
|
115
|
+
ReceivePacket.new(data)
|
116
|
+
when 0x91
|
117
|
+
ExplicitRxIndicator.new(data)
|
118
|
+
when 0x92
|
119
|
+
IODataSampleRxIndicator.new(data)
|
120
|
+
else
|
121
|
+
ReceivedFrame.new(data)
|
122
|
+
end
|
123
|
+
rescue EOFError
|
124
|
+
# No operation as we expect eventually something
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module XBee
|
2
|
+
module Frame
|
3
|
+
class ModemStatus < ReceivedFrame
|
4
|
+
attr_accessor :status
|
5
|
+
|
6
|
+
def initialize(data = nil)
|
7
|
+
super(data) && (yield self if block_given?)
|
8
|
+
end
|
9
|
+
|
10
|
+
def modem_statuses
|
11
|
+
[
|
12
|
+
[0, :Hardware_Reset],
|
13
|
+
[1, :Watchdog_Timer_Reset],
|
14
|
+
[2, :Associated],
|
15
|
+
]
|
16
|
+
end
|
17
|
+
|
18
|
+
def cmd_data=(data_string)
|
19
|
+
status_byte = data_string.unpack("c")[0]
|
20
|
+
# update status ivar for later use
|
21
|
+
self.status = case status_byte
|
22
|
+
when 0..2
|
23
|
+
modem_statuses.assoc(status_byte)
|
24
|
+
else
|
25
|
+
raise "ModemStatus frame appears to include an invalid status value: 0x%02x" % status_byte
|
26
|
+
end
|
27
|
+
#actually assign and move along
|
28
|
+
@cmd_data = data_string
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module XBee
|
2
|
+
module Frame
|
3
|
+
|
4
|
+
class ReceivePacket16 < Base
|
5
|
+
attr_reader :rssi, :address_16_bits, :options
|
6
|
+
@rssi = 0x00
|
7
|
+
@address_16_bits = 0x0000
|
8
|
+
@options = 0x00
|
9
|
+
|
10
|
+
def initialize(frame_data)
|
11
|
+
self.api_identifier = frame_data[0].unpack('H*')[0] #.join.to_i(16) unless frame_data.nil?
|
12
|
+
if $DEBUG then
|
13
|
+
print "Initializing a ReceivedFrame of type 0x%02x | " % self.api_identifier
|
14
|
+
elsif $VERBOSE
|
15
|
+
puts "Initializing a ReceivedFrame of type 0x%02x" % self.api_identifier
|
16
|
+
end
|
17
|
+
@address_16_bits = frame_data[1..2] unless frame_data.nil?
|
18
|
+
@rssi = frame_data[3] unless frame_data.nil?
|
19
|
+
@options = frame_data[4] unless frame_data.nil?
|
20
|
+
self.cmd_data = frame_data[5..-1] unless frame_data.nil?
|
21
|
+
|
22
|
+
@rssi = @rssi.unpack('C')[0]
|
23
|
+
@address_16_bits = @address_16_bits.unpack('B8B8').join()
|
24
|
+
@options = @options.unpack('B8')[0]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module XBee
|
2
|
+
module Frame
|
3
|
+
class ReceivedFrame < Base
|
4
|
+
def initialize(frame_data)
|
5
|
+
self.api_identifier = frame_data[0].unpack('H*').join.to_i(16) unless frame_data.nil?
|
6
|
+
if $DEBUG then
|
7
|
+
print "Initializing a ReceivedFrame of type 0x%02x | " % self.api_identifier
|
8
|
+
elsif $VERBOSE
|
9
|
+
puts "Initializing a ReceivedFrame of type 0x%02x" % self.api_identifier
|
10
|
+
end
|
11
|
+
self.cmd_data = frame_data[1..-1] unless frame_data.nil?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'ruxbee/frame/at_command'
|
2
|
+
|
3
|
+
module XBee
|
4
|
+
module Frame
|
5
|
+
class RemoteCommandRequest < XBee::Frame::ATCommand
|
6
|
+
def api_identifier ; 0x17 ; end
|
7
|
+
|
8
|
+
attr_accessor :destination_address, :destination_network
|
9
|
+
|
10
|
+
def initialize(at_command, destination_address = 0x000000000000ffff, destination_network = 0x0000fffe, frame_id = nil, parameter_value = nil, parameter_pack_string = "a*")
|
11
|
+
self.destination_address = destination_address
|
12
|
+
self.destination_network = destination_network
|
13
|
+
super(at_command, frame_id, parameter_value, parameter_pack_string)
|
14
|
+
yield self if block_given?
|
15
|
+
end
|
16
|
+
|
17
|
+
def cmd_data=(data_string)
|
18
|
+
dest_high = dest_low = 0
|
19
|
+
self.frame_id, dest_high, dest_low, self.destination_network, self.at_command, self.parameter_value = data_string.unpack("CNNnxa2#{parameter_pack_string}")
|
20
|
+
self.destination_address = dest_high << 32 | dest_low
|
21
|
+
end
|
22
|
+
|
23
|
+
def cmd_data
|
24
|
+
dest_high = (self.destination_address >> 32) & 0xFFFFFFFF
|
25
|
+
dest_low = self.destination_address & 0xFFFFFFFF
|
26
|
+
if parameter_value.nil?
|
27
|
+
[self.frame_id, dest_high, dest_low, self.destination_network, 0x00, self.at_command].pack("CNNnCa2")
|
28
|
+
else
|
29
|
+
[self.frame_id, dest_high, dest_low, self.destination_network, 0x02, self.at_command, self.parameter_value].pack("CNNnCa2#{parameter_pack_string}")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'ruxbee/frame/at_command_response'
|
2
|
+
|
3
|
+
module XBee
|
4
|
+
module Frame
|
5
|
+
class RemoteCommandResponse < XBee::Frame::ATCommandResponse
|
6
|
+
attr_accessor :destination_address, :destination_network
|
7
|
+
def cmd_data=(data_string)
|
8
|
+
dest_high = dest_low = 0
|
9
|
+
self.frame_id, dest_high, dest_low, self.destination_network, self.at_command, status_byte, self.retrieved_value = data_string.unpack("CNNna2Ca*")
|
10
|
+
self.destination_address = dest_high << 32 | dest_low
|
11
|
+
self.status = case status_byte
|
12
|
+
when 0..4
|
13
|
+
command_statuses[status_byte]
|
14
|
+
else
|
15
|
+
raise "AT Command Response frame appears to include an invalid status: 0x%02x" % status_byte
|
16
|
+
end
|
17
|
+
#actually assign and move along
|
18
|
+
@cmd_data = data_string
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|