xbee 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.editorconfig +13 -0
- data/.gitignore +25 -0
- data/.ruby-version +1 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +49 -0
- data/examples/check_and_set_destination_address.rb +161 -0
- data/examples/read_frames.rb +45 -0
- data/examples/response_parser_throughput.rb +35 -0
- data/lib/xbee.rb +5 -0
- data/lib/xbee/address.rb +23 -0
- data/lib/xbee/address_16.rb +39 -0
- data/lib/xbee/address_64.rb +40 -0
- data/lib/xbee/bytes.rb +21 -0
- data/lib/xbee/exceptions/exception.rb +9 -0
- data/lib/xbee/exceptions/frame_format_error.rb +9 -0
- data/lib/xbee/exceptions/unknown_frame_type.rb +9 -0
- data/lib/xbee/frames/addressed_frame.rb +27 -0
- data/lib/xbee/frames/at_command.rb +35 -0
- data/lib/xbee/frames/at_command_queue_parameter_value.rb +21 -0
- data/lib/xbee/frames/at_command_response.rb +31 -0
- data/lib/xbee/frames/create_source_route.rb +15 -0
- data/lib/xbee/frames/data/sample.rb +72 -0
- data/lib/xbee/frames/data_sample_rx_indicator.rb +51 -0
- data/lib/xbee/frames/explicit_addressing_command.rb +68 -0
- data/lib/xbee/frames/explicit_rx_indicator.rb +60 -0
- data/lib/xbee/frames/frame.rb +65 -0
- data/lib/xbee/frames/identified_frame.rb +24 -0
- data/lib/xbee/frames/many_to_one_route_request_indicator.rb +11 -0
- data/lib/xbee/frames/modem_status.rb +47 -0
- data/lib/xbee/frames/node_identification_indicator.rb +38 -0
- data/lib/xbee/frames/over_the_air_firmware_update_status.rb +28 -0
- data/lib/xbee/frames/receive_packet.rb +44 -0
- data/lib/xbee/frames/remote_at_command_request.rb +53 -0
- data/lib/xbee/frames/remote_at_command_response.rb +45 -0
- data/lib/xbee/frames/route_record_indicator.rb +28 -0
- data/lib/xbee/frames/transmit_request.rb +70 -0
- data/lib/xbee/frames/transmit_status.rb +63 -0
- data/lib/xbee/frames/unidentified_addressed_frame.rb +26 -0
- data/lib/xbee/frames/xbee_sensor_read_indicator.rb +50 -0
- data/lib/xbee/packet.rb +175 -0
- data/lib/xbee/version.rb +5 -0
- data/lib/xbee/xbee.rb +109 -0
- data/xbee.gemspec +40 -0
- metadata +245 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'semantic_logger'
|
3
|
+
|
4
|
+
require_relative '../address_16'
|
5
|
+
require_relative '../address_64'
|
6
|
+
require_relative '../exceptions/frame_format_error'
|
7
|
+
require_relative '../exceptions/unknown_frame_type'
|
8
|
+
|
9
|
+
module XBee
|
10
|
+
module Frames
|
11
|
+
class Frame
|
12
|
+
include SemanticLogger::Loggable
|
13
|
+
|
14
|
+
class << self
|
15
|
+
# [<Integer, Frame>] Map of frame type IDs to implementation classes
|
16
|
+
@@frame_types = {}
|
17
|
+
|
18
|
+
# Registers the frame type
|
19
|
+
def api_id(byte)
|
20
|
+
@@frame_types ||= {}
|
21
|
+
raise "Attempted to redefine API ID #{byte.inspect}" if @@frame_types.has_key? byte
|
22
|
+
@@frame_types[byte] = self
|
23
|
+
|
24
|
+
define_singleton_method(:frame_type) do
|
25
|
+
byte
|
26
|
+
end
|
27
|
+
|
28
|
+
define_method(:frame_type) do
|
29
|
+
byte
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def from_packet(packet)
|
35
|
+
raise Exceptions::UnknownFrameType, packet unless @@frame_types.has_key? packet.data[0]
|
36
|
+
@@frame_types[packet.data[0]].new packet: packet
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
# [XBee::Packet] if this frame was received, it'll belong to a Packet. (Frames prepped for transmit have no Packet association.)
|
42
|
+
attr_reader :packet
|
43
|
+
|
44
|
+
# Subclasses should shift +@parse_bytes+ as necessary to get their data fields.
|
45
|
+
def initialize(packet: nil)
|
46
|
+
logger.trace 'Initializing...', packet: packet
|
47
|
+
@packet = packet
|
48
|
+
if packet
|
49
|
+
@parse_bytes = packet.data.dup
|
50
|
+
@frame_type = @parse_bytes.shift
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
def bytes
|
56
|
+
[frame_type]
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
def to_packet
|
61
|
+
@packet = Packet.new bytes
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'frame'
|
3
|
+
|
4
|
+
module XBee
|
5
|
+
module Frames
|
6
|
+
# The base class for frames that have an ID for tracking the radio's processing.
|
7
|
+
class IdentifiedFrame < Frame
|
8
|
+
attr_accessor :id
|
9
|
+
|
10
|
+
def initialize(packet: nil)
|
11
|
+
super
|
12
|
+
|
13
|
+
if @parse_bytes
|
14
|
+
@id = @parse_bytes.shift
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def bytes
|
20
|
+
super + [(id || 0x00)]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'frame'
|
3
|
+
|
4
|
+
module XBee
|
5
|
+
module Frames
|
6
|
+
class ModemStatus < Frame
|
7
|
+
api_id 0x8A
|
8
|
+
|
9
|
+
|
10
|
+
STATUSES = {
|
11
|
+
0x00 => 'Hardware reset',
|
12
|
+
0x01 => 'Watchdog timer reset',
|
13
|
+
0x02 => 'Joined network',
|
14
|
+
0x03 => 'Disassociated',
|
15
|
+
0x06 => 'Coordinator started',
|
16
|
+
0x07 => 'Network security key was updated',
|
17
|
+
0x0D => 'Voltage supply limit exceeded',
|
18
|
+
0x11 => 'Modem configuration changed while join in progress',
|
19
|
+
}
|
20
|
+
|
21
|
+
attr_accessor :status
|
22
|
+
|
23
|
+
|
24
|
+
def initialize(packet: nil)
|
25
|
+
super
|
26
|
+
|
27
|
+
if @parse_bytes
|
28
|
+
@status = @parse_bytes.shift
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
def status_string
|
34
|
+
if status < 0x80
|
35
|
+
STATUSES[status]
|
36
|
+
else
|
37
|
+
'Ember ZigBee stack error'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
def bytes
|
43
|
+
super + [status || 0x00]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'unidentified_addressed_frame'
|
3
|
+
require_relative '../bytes'
|
4
|
+
|
5
|
+
module XBee
|
6
|
+
module Frames
|
7
|
+
class NodeIdentificationIndicator < UnidentifiedAddressedFrame
|
8
|
+
api_id 0x95
|
9
|
+
|
10
|
+
attr_accessor :options
|
11
|
+
attr_accessor :source_address16
|
12
|
+
attr_accessor :source_address64
|
13
|
+
attr_accessor :parent_address16
|
14
|
+
attr_accessor :identifier
|
15
|
+
attr_accessor :device_type
|
16
|
+
attr_accessor :source_event
|
17
|
+
attr_accessor :profile_id
|
18
|
+
attr_accessor :manufacturer_id
|
19
|
+
|
20
|
+
|
21
|
+
def initialize(packet: nil)
|
22
|
+
super
|
23
|
+
|
24
|
+
if @parse_bytes
|
25
|
+
@options = @parse_bytes.shift
|
26
|
+
@source_address16 = Address16.new *@parse_bytes.shift(2)
|
27
|
+
@source_address64 = Address64.new *@parse_bytes.shift(8)
|
28
|
+
@identifier = @parse_bytes.shift 2
|
29
|
+
@parent_address16 = Address16.new *@parse_bytes.shift(2)
|
30
|
+
@device_type = @parse_bytes.shift
|
31
|
+
@source_event = @parse_bytes.shift
|
32
|
+
@profile_id = Bytes.unsigned_int_from_array @parse_bytes.shift(2)
|
33
|
+
@manufacturer_id = Bytes.unsigned_int_from_array @parse_bytes.shift(2)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'unidentified_addressed_frame'
|
3
|
+
|
4
|
+
module XBee
|
5
|
+
module Frames
|
6
|
+
class OverTheAirFirmwareUpdateStatus < UnidentifiedAddressedFrame
|
7
|
+
api_id 0xA0
|
8
|
+
|
9
|
+
attr_accessor :options
|
10
|
+
attr_accessor :bootloader_message
|
11
|
+
attr_accessor :block_number
|
12
|
+
|
13
|
+
attr_accessor :target_address64
|
14
|
+
|
15
|
+
|
16
|
+
def initialize(packet: nil)
|
17
|
+
super
|
18
|
+
|
19
|
+
if @parse_bytes
|
20
|
+
@options = @parse_bytes.shift
|
21
|
+
@bootloader_message = @parse_bytes.shift
|
22
|
+
@block_number = @parse_bytes.shift
|
23
|
+
@target_address64 = Address64.new *@parse_bytes.shift(8)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'frame'
|
3
|
+
|
4
|
+
module XBee
|
5
|
+
module Frames
|
6
|
+
# When a device configured with a standard API Rx Indicator (AO = 0) receives an RF data packet, it
|
7
|
+
# sends it out the serial interface using this message type.
|
8
|
+
class ReceivePacket < Frame
|
9
|
+
api_id 0x90
|
10
|
+
|
11
|
+
|
12
|
+
OPTION_BITS = {
|
13
|
+
0x01 => 'Acknowledged',
|
14
|
+
0x02 => 'Broadcast packet',
|
15
|
+
0x20 => 'Encrypted with APS encryption',
|
16
|
+
0x40 => 'End device',
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
|
20
|
+
attr_accessor :address64
|
21
|
+
attr_accessor :address16
|
22
|
+
attr_accessor :options
|
23
|
+
attr_accessor :data
|
24
|
+
|
25
|
+
|
26
|
+
def initialize(packet: nil)
|
27
|
+
super
|
28
|
+
|
29
|
+
if @parse_bytes
|
30
|
+
@address64 = Address64.new *@parse_bytes.shift(8)
|
31
|
+
@address16 = Address16.new *@parse_bytes.shift(2)
|
32
|
+
@options = @parse_bytes.shift
|
33
|
+
@data = @parse_bytes
|
34
|
+
@parse_bytes = []
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def bytes
|
40
|
+
super + (address64 || Address64.from_array([0] * 8)).to_a + (address16 || Address16.new(0, 0)).to_a + [options || 0x00] + (data || [])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'addressed_frame'
|
3
|
+
|
4
|
+
module XBee
|
5
|
+
module Frames
|
6
|
+
# Use this frame to query or set device parameters on the local device. This API command applies
|
7
|
+
# changes after running the command. You can query parameter values by sending the 0x08 AT
|
8
|
+
# Command frame with no parameter value field (the two-byte AT command is immediately followed by
|
9
|
+
# the frame checksum).
|
10
|
+
class RemoteATCommandRequest < AddressedFrame
|
11
|
+
api_id 0x17
|
12
|
+
|
13
|
+
OPTIONS = {
|
14
|
+
0x01 => :disable_ack,
|
15
|
+
0x40 => :extended_transmission_timeout,
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
attr_accessor :at_command
|
19
|
+
attr_accessor :command_parameter
|
20
|
+
attr_accessor :remote_command_options
|
21
|
+
|
22
|
+
|
23
|
+
def initialize(packet: nil)
|
24
|
+
@address16 = Address16::BROADCAST
|
25
|
+
|
26
|
+
super
|
27
|
+
|
28
|
+
if @parse_bytes
|
29
|
+
@remote_command_options = @parse_bytes.shift
|
30
|
+
@at_command = @parse_bytes.shift 2
|
31
|
+
@command_parameter = @parse_bytes
|
32
|
+
@parse_bytes = []
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
# @param value [Array/String] The AT command. Must be two bytes, either an array or a string.
|
38
|
+
def at_command=(value)
|
39
|
+
raise ArgumentError, "AT command must be exactly two bytes (got #{value.inspect})" unless value.length == 2
|
40
|
+
if value.respond_to?(:force_encoding)
|
41
|
+
@at_command = value.dup.force_encoding('ASCII').unpack 'C*'
|
42
|
+
else
|
43
|
+
@at_command = value
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
def bytes
|
49
|
+
super + [remote_command_options || 0x00] + (at_command || [0] * 2) + (command_parameter || [])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'addressed_frame'
|
3
|
+
|
4
|
+
module XBee
|
5
|
+
module Frames
|
6
|
+
# If a device receives this frame in response to a Remote Command Request (0x17) frame, the device
|
7
|
+
# sends an AT Command Response (0x97) frame out the serial interface.
|
8
|
+
# Some commands, such as the ND command, may send back multiple frames.
|
9
|
+
class RemoteATCommandResponse < AddressedFrame
|
10
|
+
api_id 0x97
|
11
|
+
|
12
|
+
attr_accessor :at_command
|
13
|
+
attr_accessor :status
|
14
|
+
attr_accessor :data
|
15
|
+
|
16
|
+
|
17
|
+
# Possible values for +status+
|
18
|
+
STATUS = {
|
19
|
+
0 => :ok,
|
20
|
+
1 => :error,
|
21
|
+
2 => :invalid_command,
|
22
|
+
3 => :invalid_parameter,
|
23
|
+
4 => :transmission_failed,
|
24
|
+
}.freeze
|
25
|
+
|
26
|
+
|
27
|
+
def initialize(packet: nil)
|
28
|
+
super
|
29
|
+
|
30
|
+
if @parse_bytes
|
31
|
+
@at_command = @parse_bytes.shift 2
|
32
|
+
@status = @parse_bytes.shift
|
33
|
+
@data = @parse_bytes
|
34
|
+
@parse_bytes = []
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def bytes
|
40
|
+
super + (command || [0x00] * 2) + [status || 0x00] + (data || [])
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'unidentified_addressed_frame'
|
3
|
+
|
4
|
+
module XBee
|
5
|
+
module Frames
|
6
|
+
class RouteRecordIndicator < UnidentifiedAddressedFrame
|
7
|
+
api_id 0xA1
|
8
|
+
|
9
|
+
attr_accessor :options
|
10
|
+
attr_accessor :addresses
|
11
|
+
|
12
|
+
|
13
|
+
def initialize(packet: nil)
|
14
|
+
@addresses = []
|
15
|
+
|
16
|
+
super
|
17
|
+
|
18
|
+
if @parse_bytes
|
19
|
+
@options = @parse_bytes.shift
|
20
|
+
number_of_addresses = @parse_bytes.shift
|
21
|
+
number_of_addresses.times do
|
22
|
+
@addresses.push Address16.new(*@parse_bytes.shift(2))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'addressed_frame'
|
3
|
+
|
4
|
+
module XBee
|
5
|
+
module Frames
|
6
|
+
# This frame causes the device to send payload data as an RF packet to a specific destination.
|
7
|
+
# * For broadcast transmissions, set the 64-bit destination address to 0x000000000000FFFF.
|
8
|
+
# Address the coordinator by either setting the 64-bit address to all 0x00s and the 16-bit address
|
9
|
+
# to 0xFFFE, or setting the 64-bit address to the coordinator's 64-bit address and the 16-bit
|
10
|
+
# address to 0x0000.
|
11
|
+
# * For all other transmissions, setting the 16-bit address to the correct 16-bit address helps
|
12
|
+
# improve performance when transmitting to multiple destinations. If you do not know a 16-bit
|
13
|
+
# address, set this field to 0xFFFE (unknown). If successful, the Transmit Status frame (0x8B)
|
14
|
+
# indicates the discovered 16-bit address.
|
15
|
+
#
|
16
|
+
# You can set the broadcast radius from 0 up to NH. If set to 0, the value of NH specifies the broadcast
|
17
|
+
# radius (recommended). This parameter is only used for broadcast transmissions.
|
18
|
+
#
|
19
|
+
# You can read the maximum number of payload bytes with the NP command.
|
20
|
+
class TransmitRequest < AddressedFrame
|
21
|
+
api_id 0x10
|
22
|
+
|
23
|
+
attr_accessor :broadcast_radius
|
24
|
+
attr_accessor :options
|
25
|
+
attr_accessor :data
|
26
|
+
|
27
|
+
|
28
|
+
OPTIONS = {
|
29
|
+
0x01 => :disable_retries,
|
30
|
+
0x20 => :enable_aps_encryption,
|
31
|
+
0x40 => :extended_transmission_timeout,
|
32
|
+
}.freeze
|
33
|
+
|
34
|
+
class << self
|
35
|
+
# Allocates and returns an instance pre-addressed as a broadcast.
|
36
|
+
def broadcast
|
37
|
+
new.tap do |frame|
|
38
|
+
frame.address64 = Address64::BROADCAST
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
# Allocates and returns an instance pre-addressed for the coordinator node.
|
44
|
+
def coordinator
|
45
|
+
new.tap do |frame|
|
46
|
+
frame.address64 = Address64::COORDINATOR
|
47
|
+
frame.address16 = Address16::COORDINATOR
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def initialize(packet: nil)
|
54
|
+
super
|
55
|
+
|
56
|
+
if @parse_bytes
|
57
|
+
@broadcast_radius = @parse_bytes.shift
|
58
|
+
@options = @parse_bytes.shift
|
59
|
+
@data = @parse_bytes
|
60
|
+
@parse_bytes = []
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def bytes
|
66
|
+
super + [broadcast_radius || 0x00] + [options || 0x00] + (data || [])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|