ruxbee 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|