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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +13 -0
  3. data/.gitignore +25 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +49 -0
  8. data/examples/check_and_set_destination_address.rb +161 -0
  9. data/examples/read_frames.rb +45 -0
  10. data/examples/response_parser_throughput.rb +35 -0
  11. data/lib/xbee.rb +5 -0
  12. data/lib/xbee/address.rb +23 -0
  13. data/lib/xbee/address_16.rb +39 -0
  14. data/lib/xbee/address_64.rb +40 -0
  15. data/lib/xbee/bytes.rb +21 -0
  16. data/lib/xbee/exceptions/exception.rb +9 -0
  17. data/lib/xbee/exceptions/frame_format_error.rb +9 -0
  18. data/lib/xbee/exceptions/unknown_frame_type.rb +9 -0
  19. data/lib/xbee/frames/addressed_frame.rb +27 -0
  20. data/lib/xbee/frames/at_command.rb +35 -0
  21. data/lib/xbee/frames/at_command_queue_parameter_value.rb +21 -0
  22. data/lib/xbee/frames/at_command_response.rb +31 -0
  23. data/lib/xbee/frames/create_source_route.rb +15 -0
  24. data/lib/xbee/frames/data/sample.rb +72 -0
  25. data/lib/xbee/frames/data_sample_rx_indicator.rb +51 -0
  26. data/lib/xbee/frames/explicit_addressing_command.rb +68 -0
  27. data/lib/xbee/frames/explicit_rx_indicator.rb +60 -0
  28. data/lib/xbee/frames/frame.rb +65 -0
  29. data/lib/xbee/frames/identified_frame.rb +24 -0
  30. data/lib/xbee/frames/many_to_one_route_request_indicator.rb +11 -0
  31. data/lib/xbee/frames/modem_status.rb +47 -0
  32. data/lib/xbee/frames/node_identification_indicator.rb +38 -0
  33. data/lib/xbee/frames/over_the_air_firmware_update_status.rb +28 -0
  34. data/lib/xbee/frames/receive_packet.rb +44 -0
  35. data/lib/xbee/frames/remote_at_command_request.rb +53 -0
  36. data/lib/xbee/frames/remote_at_command_response.rb +45 -0
  37. data/lib/xbee/frames/route_record_indicator.rb +28 -0
  38. data/lib/xbee/frames/transmit_request.rb +70 -0
  39. data/lib/xbee/frames/transmit_status.rb +63 -0
  40. data/lib/xbee/frames/unidentified_addressed_frame.rb +26 -0
  41. data/lib/xbee/frames/xbee_sensor_read_indicator.rb +50 -0
  42. data/lib/xbee/packet.rb +175 -0
  43. data/lib/xbee/version.rb +5 -0
  44. data/lib/xbee/xbee.rb +109 -0
  45. data/xbee.gemspec +40 -0
  46. 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,11 @@
1
+ # frozen_string_literal: true
2
+ require_relative 'unidentified_addressed_frame'
3
+
4
+ module XBee
5
+ module Frames
6
+ class ManyToOneRouteRequestIndicator < UnidentifiedAddressedFrame
7
+ api_id 0xA3
8
+
9
+ end
10
+ end
11
+ 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