001/** 002 * Copyright (c) 2014-2015 Digi International Inc., 003 * All rights not expressly granted are reserved. 004 * 005 * This Source Code Form is subject to the terms of the Mozilla Public 006 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 007 * You can obtain one at http://mozilla.org/MPL/2.0/. 008 * 009 * Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343 010 * ======================================================================= 011 */ 012package com.digi.xbee.api; 013 014import java.io.IOException; 015import java.util.ArrayList; 016import java.util.Arrays; 017import java.util.Set; 018import java.util.TreeSet; 019 020import org.slf4j.Logger; 021import org.slf4j.LoggerFactory; 022 023import com.digi.xbee.api.connection.IConnectionInterface; 024import com.digi.xbee.api.connection.DataReader; 025import com.digi.xbee.api.connection.serial.SerialPortParameters; 026import com.digi.xbee.api.exceptions.ATCommandException; 027import com.digi.xbee.api.exceptions.InterfaceNotOpenException; 028import com.digi.xbee.api.exceptions.InvalidOperatingModeException; 029import com.digi.xbee.api.exceptions.OperationNotSupportedException; 030import com.digi.xbee.api.exceptions.TimeoutException; 031import com.digi.xbee.api.exceptions.TransmitException; 032import com.digi.xbee.api.exceptions.XBeeException; 033import com.digi.xbee.api.io.IOLine; 034import com.digi.xbee.api.io.IOMode; 035import com.digi.xbee.api.io.IOSample; 036import com.digi.xbee.api.io.IOValue; 037import com.digi.xbee.api.listeners.IExplicitDataReceiveListener; 038import com.digi.xbee.api.listeners.IIOSampleReceiveListener; 039import com.digi.xbee.api.listeners.IModemStatusReceiveListener; 040import com.digi.xbee.api.listeners.IPacketReceiveListener; 041import com.digi.xbee.api.listeners.IDataReceiveListener; 042import com.digi.xbee.api.models.ATCommand; 043import com.digi.xbee.api.models.ATCommandResponse; 044import com.digi.xbee.api.models.ATCommandStatus; 045import com.digi.xbee.api.models.AssociationIndicationStatus; 046import com.digi.xbee.api.models.HardwareVersion; 047import com.digi.xbee.api.models.PowerLevel; 048import com.digi.xbee.api.models.RemoteATCommandOptions; 049import com.digi.xbee.api.models.XBee16BitAddress; 050import com.digi.xbee.api.models.XBee64BitAddress; 051import com.digi.xbee.api.models.OperatingMode; 052import com.digi.xbee.api.models.XBeeProtocol; 053import com.digi.xbee.api.models.XBeeTransmitStatus; 054import com.digi.xbee.api.packet.XBeeAPIPacket; 055import com.digi.xbee.api.packet.APIFrameType; 056import com.digi.xbee.api.packet.XBeePacket; 057import com.digi.xbee.api.packet.common.ATCommandPacket; 058import com.digi.xbee.api.packet.common.ATCommandQueuePacket; 059import com.digi.xbee.api.packet.common.ATCommandResponsePacket; 060import com.digi.xbee.api.packet.common.IODataSampleRxIndicatorPacket; 061import com.digi.xbee.api.packet.common.RemoteATCommandPacket; 062import com.digi.xbee.api.packet.common.RemoteATCommandResponsePacket; 063import com.digi.xbee.api.packet.common.TransmitStatusPacket; 064import com.digi.xbee.api.packet.raw.RX16IOPacket; 065import com.digi.xbee.api.packet.raw.RX64IOPacket; 066import com.digi.xbee.api.packet.raw.TXStatusPacket; 067import com.digi.xbee.api.utils.ByteUtils; 068import com.digi.xbee.api.utils.HexUtils; 069 070/** 071 * This class provides common functionality for all XBee devices. 072 * 073 * @see XBeeDevice 074 * @see RemoteXBeeDevice 075 */ 076public abstract class AbstractXBeeDevice { 077 078 // Constants. 079 080 /** 081 * Default receive timeout used to wait for a response in synchronous 082 * operations: {@value} ms. 083 * 084 * @see XBeeDevice#getReceiveTimeout() 085 * @see XBeeDevice#setReceiveTimeout(int) 086 */ 087 protected final static int DEFAULT_RECEIVE_TIMETOUT = 2000; // 2.0 seconds of timeout to receive packet and command responses. 088 089 /** 090 * Timeout to wait before entering in command mode: {@value} ms. 091 * 092 * <p>It is used to determine the operating mode of the module (this 093 * library only supports API modes, not transparent mode).</p> 094 * 095 * <p>This value depends on the {@code GT}, {@code AT} and/or {@code BT} 096 * parameters.</p> 097 * 098 * @see XBeeDevice#determineOperatingMode() 099 */ 100 protected final static int TIMEOUT_BEFORE_COMMAND_MODE = 1200; 101 102 /** 103 * Timeout to wait after entering in command mode: {@value} ms. 104 * 105 * <p>It is used to determine the operating mode of the module (this 106 * library only supports API modes, not transparent mode).</p> 107 * 108 * <p>This value depends on the {@code GT}, {@code AT} and/or {@code BT} 109 * parameters.</p> 110 * 111 * @see XBeeDevice#determineOperatingMode() 112 */ 113 protected final static int TIMEOUT_ENTER_COMMAND_MODE = 1500; 114 115 // Variables. 116 protected IConnectionInterface connectionInterface; 117 118 protected DataReader dataReader = null; 119 120 protected XBeeProtocol xbeeProtocol = XBeeProtocol.UNKNOWN; 121 122 protected OperatingMode operatingMode = OperatingMode.UNKNOWN; 123 124 protected XBee16BitAddress xbee16BitAddress = XBee16BitAddress.UNKNOWN_ADDRESS; 125 protected XBee64BitAddress xbee64BitAddress = XBee64BitAddress.UNKNOWN_ADDRESS; 126 127 protected int currentFrameID = 0xFF; 128 protected int receiveTimeout = DEFAULT_RECEIVE_TIMETOUT; 129 130 protected AbstractXBeeDevice localXBeeDevice; 131 132 protected Logger logger; 133 134 private String nodeID; 135 private String firmwareVersion; 136 137 private HardwareVersion hardwareVersion; 138 139 private Object ioLock = new Object(); 140 141 private boolean ioPacketReceived = false; 142 private boolean applyConfigurationChanges = true; 143 144 private byte[] ioPacketPayload; 145 146 /** 147 * Class constructor. Instantiates a new {@code XBeeDevice} object in the 148 * given port name and baud rate. 149 * 150 * @param port Serial port name where XBee device is attached to. 151 * @param baudRate Serial port baud rate to communicate with the device. 152 * Other connection parameters will be set as default (8 153 * data bits, 1 stop bit, no parity, no flow control). 154 * 155 * @throws IllegalArgumentException if {@code baudRate < 0}. 156 * @throws NullPointerException if {@code port == null}. 157 * 158 * @see #AbstractXBeeDevice(IConnectionInterface) 159 * @see #AbstractXBeeDevice(String, SerialPortParameters) 160 * @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress) 161 * @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress, XBee16BitAddress, String) 162 * @see #AbstractXBeeDevice(String, int, int, int, int, int) 163 */ 164 public AbstractXBeeDevice(String port, int baudRate) { 165 this(XBee.createConnectiontionInterface(port, baudRate)); 166 } 167 168 /** 169 * Class constructor. Instantiates a new {@code XBeeDevice} object in the 170 * given serial port name and settings. 171 * 172 * @param port Serial port name where XBee device is attached to. 173 * @param baudRate Serial port baud rate to communicate with the device. 174 * @param dataBits Serial port data bits. 175 * @param stopBits Serial port data bits. 176 * @param parity Serial port data bits. 177 * @param flowControl Serial port data bits. 178 * 179 * @throws IllegalArgumentException if {@code baudRate < 0} or 180 * if {@code dataBits < 0} or 181 * if {@code stopBits < 0} or 182 * if {@code parity < 0} or 183 * if {@code flowControl < 0}. 184 * @throws NullPointerException if {@code port == null}. 185 * 186 * @see #AbstractXBeeDevice(IConnectionInterface) 187 * @see #AbstractXBeeDevice(String, int) 188 * @see #AbstractXBeeDevice(String, SerialPortParameters) 189 * @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress) 190 * @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress, XBee16BitAddress, String) 191 */ 192 public AbstractXBeeDevice(String port, int baudRate, int dataBits, int stopBits, int parity, int flowControl) { 193 this(port, new SerialPortParameters(baudRate, dataBits, stopBits, parity, flowControl)); 194 } 195 196 /** 197 * Class constructor. Instantiates a new {@code XBeeDevice} object in the 198 * given serial port name and parameters. 199 * 200 * @param port Serial port name where XBee device is attached to. 201 * @param serialPortParameters Object containing the serial port parameters. 202 * 203 * @throws NullPointerException if {@code port == null} or 204 * if {@code serialPortParameters == null}. 205 * 206 * @see #AbstractXBeeDevice(IConnectionInterface) 207 * @see #AbstractXBeeDevice(String, int) 208 * @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress) 209 * @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress, XBee16BitAddress, String) 210 * @see #AbstractXBeeDevice(String, int, int, int, int, int) 211 * @see com.digi.xbee.api.connection.serial.SerialPortParameters 212 */ 213 public AbstractXBeeDevice(String port, SerialPortParameters serialPortParameters) { 214 this(XBee.createConnectiontionInterface(port, serialPortParameters)); 215 } 216 217 /** 218 * Class constructor. Instantiates a new {@code XBeeDevice} object with the 219 * given connection interface. 220 * 221 * @param connectionInterface The connection interface with the physical 222 * XBee device. 223 * 224 * @throws NullPointerException if {@code connectionInterface == null}. 225 * 226 * @see #AbstractXBeeDevice(String, int) 227 * @see #AbstractXBeeDevice(String, SerialPortParameters) 228 * @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress) 229 * @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress, XBee16BitAddress, String) 230 * @see #AbstractXBeeDevice(String, int, int, int, int, int) 231 * @see com.digi.xbee.api.connection.IConnectionInterface 232 */ 233 public AbstractXBeeDevice(IConnectionInterface connectionInterface) { 234 if (connectionInterface == null) 235 throw new NullPointerException("ConnectionInterface cannot be null."); 236 237 this.connectionInterface = connectionInterface; 238 this.logger = LoggerFactory.getLogger(this.getClass()); 239 logger.debug(toString() + "Using the connection interface {}.", 240 connectionInterface.getClass().getSimpleName()); 241 } 242 243 /** 244 * Class constructor. Instantiates a new {@code RemoteXBeeDevice} object 245 * with the given local {@code XBeeDevice} which contains the connection 246 * interface to be used. 247 * 248 * @param localXBeeDevice The local XBee device that will behave as 249 * connection interface to communicate with this 250 * remote XBee device. 251 * @param addr64 The 64-bit address to identify this XBee device. 252 * 253 * @throws IllegalArgumentException If {@code localXBeeDevice.isRemote() == true}. 254 * @throws NullPointerException if {@code localXBeeDevice == null} or 255 * if {@code addr64 == null}. 256 * 257 * @see #AbstractXBeeDevice(IConnectionInterface) 258 * @see #AbstractXBeeDevice(String, int) 259 * @see #AbstractXBeeDevice(String, SerialPortParameters) 260 * @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress, XBee16BitAddress, String) 261 * @see #AbstractXBeeDevice(String, int, int, int, int, int) 262 * @see com.digi.xbee.api.models.XBee16BitAddress 263 */ 264 public AbstractXBeeDevice(XBeeDevice localXBeeDevice, XBee64BitAddress addr64) { 265 this(localXBeeDevice, addr64, null, null); 266 } 267 268 /** 269 * Class constructor. Instantiates a new {@code RemoteXBeeDevice} object 270 * with the given local {@code XBeeDevice} which contains the connection 271 * interface to be used. 272 * 273 * @param localXBeeDevice The local XBee device that will behave as 274 * connection interface to communicate with this 275 * remote XBee device. 276 * @param addr64 The 64-bit address to identify this XBee device. 277 * @param addr16 The 16-bit address to identify this XBee device. It might 278 * be {@code null}. 279 * @param id The node identifier of this XBee device. It might be 280 * {@code null}. 281 * 282 * @throws IllegalArgumentException If {@code localXBeeDevice.isRemote() == true}. 283 * @throws NullPointerException If {@code localXBeeDevice == null} or 284 * if {@code addr64 == null}. 285 * 286 * @see #AbstractXBeeDevice(IConnectionInterface) 287 * @see #AbstractXBeeDevice(String, int) 288 * @see #AbstractXBeeDevice(String, SerialPortParameters) 289 * @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress) 290 * @see #AbstractXBeeDevice(String, int, int, int, int, int) 291 * @see com.digi.xbee.api.models.XBee16BitAddress 292 * @see com.digi.xbee.api.models.XBee64BitAddress 293 */ 294 public AbstractXBeeDevice(XBeeDevice localXBeeDevice, XBee64BitAddress addr64, 295 XBee16BitAddress addr16, String id) { 296 if (localXBeeDevice == null) 297 throw new NullPointerException("Local XBee device cannot be null."); 298 if (addr64 == null) 299 throw new NullPointerException("XBee 64-bit address of the device cannot be null."); 300 if (localXBeeDevice.isRemote()) 301 throw new IllegalArgumentException("The given local XBee device is remote."); 302 303 this.localXBeeDevice = localXBeeDevice; 304 this.connectionInterface = localXBeeDevice.getConnectionInterface(); 305 this.xbee64BitAddress = addr64; 306 this.xbee16BitAddress = addr16; 307 if (addr16 == null) 308 xbee16BitAddress = XBee16BitAddress.UNKNOWN_ADDRESS; 309 this.nodeID = id; 310 this.logger = LoggerFactory.getLogger(this.getClass()); 311 logger.debug(toString() + "Using the connection interface {}.", 312 connectionInterface.getClass().getSimpleName()); 313 } 314 315 /** 316 * Returns the connection interface associated to this XBee device. 317 * 318 * @return XBee device's connection interface. 319 * 320 * @see com.digi.xbee.api.connection.IConnectionInterface 321 */ 322 public IConnectionInterface getConnectionInterface() { 323 return connectionInterface; 324 } 325 326 /** 327 * Returns whether this XBee device is a remote device. 328 * 329 * @return {@code true} if this XBee device is a remote device, 330 * {@code false} otherwise. 331 */ 332 abstract public boolean isRemote(); 333 334 /** 335 * Reads some parameters from this device and obtains its protocol. 336 * 337 * <p>This method refresh the values of:</p> 338 * <ul> 339 * <li>64-bit address only if it is not initialized.</li> 340 * <li>Node Identifier.</li> 341 * <li>Hardware version if it is not initialized.</li> 342 * <li>Firmware version.</li> 343 * <li>XBee device protocol.</li> 344 * <li>16-bit address (not for DigiMesh/DigiPoint modules).</li> 345 * </ul> 346 * 347 * @throws InterfaceNotOpenException if this device connection is not open. 348 * @throws TimeoutException if there is a timeout reading the parameters. 349 * @throws XBeeException if there is any error trying to read the device information or 350 * if there is any other XBee related exception. 351 * 352 * @see #get16BitAddress() 353 * @see #get64BitAddress() 354 * @see #getHardwareVersion() 355 * @see #getNodeID() 356 * @see #getFirmwareVersion() 357 * @see #getXBeeProtocol() 358 * @see #setNodeID(String) 359 */ 360 public void readDeviceInfo() throws TimeoutException, XBeeException { 361 byte[] response = null; 362 // Get the 64-bit address. 363 if (xbee64BitAddress == null || xbee64BitAddress == XBee64BitAddress.UNKNOWN_ADDRESS) { 364 String addressHigh; 365 String addressLow; 366 367 response = getParameter("SH"); 368 addressHigh = HexUtils.byteArrayToHexString(response); 369 370 response = getParameter("SL"); 371 addressLow = HexUtils.byteArrayToHexString(response); 372 373 while(addressLow.length() < 8) 374 addressLow = "0" + addressLow; 375 376 xbee64BitAddress = new XBee64BitAddress(addressHigh + addressLow); 377 } 378 // Get the Node ID. 379 response = getParameter("NI"); 380 nodeID = new String(response); 381 382 // Get the hardware version. 383 if (hardwareVersion == null) { 384 response = getParameter("HV"); 385 hardwareVersion = HardwareVersion.get(response[0]); 386 } 387 // Get the firmware version. 388 response = getParameter("VR"); 389 firmwareVersion = HexUtils.byteArrayToHexString(response); 390 391 // Original value of the protocol. 392 XBeeProtocol origProtocol = getXBeeProtocol(); 393 394 // Obtain the device protocol. 395 xbeeProtocol = XBeeProtocol.determineProtocol(hardwareVersion, firmwareVersion); 396 397 if (origProtocol != XBeeProtocol.UNKNOWN 398 && origProtocol != xbeeProtocol) { 399 throw new XBeeException("Error reading device information: " 400 + "Your module seems to be " + xbeeProtocol 401 + " and NOT " + origProtocol + ". Check if you are using" 402 + " the appropriate device class."); 403 } 404 405 // Get the 16-bit address. This must be done after obtaining the protocol because 406 // DigiMesh and Point-to-Multipoint protocols don't have 16-bit addresses. 407 XBeeProtocol protocol = getXBeeProtocol(); 408 if (protocol != XBeeProtocol.DIGI_MESH 409 && protocol != XBeeProtocol.DIGI_POINT 410 && protocol != XBeeProtocol.UNKNOWN) { 411 response = getParameter("MY"); 412 xbee16BitAddress = new XBee16BitAddress(response); 413 } 414 } 415 416 /** 417 * Returns the 16-bit address of this XBee device. 418 * 419 * <p>To refresh this value use the {@link #readDeviceInfo()} method.</p> 420 * 421 * @return The 16-bit address of this XBee device. 422 * 423 * @see com.digi.xbee.api.models.XBee16BitAddress 424 */ 425 public XBee16BitAddress get16BitAddress() { 426 return xbee16BitAddress; 427 } 428 429 /** 430 * Returns the 64-bit address of this XBee device. 431 * 432 * <p>If this value is {@code null} or 433 * {@code XBee64BitAddress.UNKNOWN_ADDRESS}, use the 434 * {@link #readDeviceInfo()} method to get its value.</p> 435 * 436 * @return The 64-bit address of this XBee device. 437 * 438 * @see com.digi.xbee.api.models.XBee64BitAddress 439 */ 440 public XBee64BitAddress get64BitAddress() { 441 return xbee64BitAddress; 442 } 443 444 /** 445 * Returns the Operating mode (AT, API or API escaped) of this XBee device 446 * for a local device, and the operating mode of the local device used as 447 * communication interface for a remote device. 448 * 449 * @return The operating mode of the local XBee device. 450 * 451 * @see #isRemote() 452 * @see com.digi.xbee.api.models.OperatingMode 453 */ 454 protected OperatingMode getOperatingMode() { 455 if (isRemote()) 456 return localXBeeDevice.getOperatingMode(); 457 return operatingMode; 458 } 459 460 /** 461 * Returns the XBee Protocol of this XBee device. 462 * 463 * <p>To refresh this value use the {@link #readDeviceInfo()} method.</p> 464 * 465 * @return The XBee device protocol. 466 * 467 * @see com.digi.xbee.api.models.XBeeProtocol 468 */ 469 public XBeeProtocol getXBeeProtocol() { 470 return xbeeProtocol; 471 } 472 473 /** 474 * Returns the node identifier of this XBee device. 475 * 476 * <p>To refresh this value use the {@link #readDeviceInfo()} method.</p> 477 * 478 * @return The node identifier of this device. 479 * 480 * @see #setNodeID(String) 481 */ 482 public String getNodeID() { 483 return nodeID; 484 } 485 486 /** 487 * Sets the node identifier of this XBee device. 488 * 489 * @param nodeID The new node id of the device. 490 * 491 * @throws IllegalArgumentException if {@code nodeID.length > 20}. 492 * @throws InterfaceNotOpenException if this device connection is not open. 493 * @throws NullPointerException if {@code nodeID == null}. 494 * @throws TimeoutException if there is a timeout setting the node ID value. 495 * @throws XBeeException if there is any other XBee related exception. 496 * 497 * @see #getNodeID() 498 */ 499 public void setNodeID(String nodeID) throws TimeoutException, XBeeException { 500 if (nodeID == null) 501 throw new NullPointerException("Node ID cannot be null."); 502 if (nodeID.length() > 20) 503 throw new IllegalArgumentException("Node ID length must be less than 21."); 504 505 setParameter("NI", nodeID.getBytes()); 506 507 this.nodeID = nodeID; 508 } 509 510 /** 511 * Returns the firmware version (hexadecimal string value) of this XBee 512 * device. 513 * 514 * <p>To refresh this value use the {@link #readDeviceInfo()} method.</p> 515 * 516 * @return The firmware version of the XBee device. 517 */ 518 public String getFirmwareVersion() { 519 return firmwareVersion; 520 } 521 522 /** 523 * Returns the hardware version of this XBee device. 524 * 525 * <p>If this value is {@code null}, use the {@link #readDeviceInfo()} 526 * method to get its value.</p> 527 * 528 * @return The hardware version of the XBee device. 529 * 530 * @see com.digi.xbee.api.models.HardwareVersion 531 * @see com.digi.xbee.api.models.HardwareVersionEnum 532 */ 533 public HardwareVersion getHardwareVersion() { 534 return hardwareVersion; 535 } 536 537 /** 538 * Updates the current device reference with the data provided for the 539 * given device. 540 * 541 * <p><b>This is only for internal use.</b></p> 542 * 543 * @param device The XBee Device to get the data from. 544 */ 545 public void updateDeviceDataFrom(AbstractXBeeDevice device) { 546 // TODO Should the devices have the same protocol?? 547 // TODO Should be allow to update a local from a remote or viceversa?? Maybe 548 // this must be in the Local/Remote device class(es) and not here... 549 550 // Only update the Node Identifier if the provided is not null. 551 if (device.getNodeID() != null) 552 this.nodeID = device.getNodeID(); 553 554 // Only update the 64-bit address if the original is null or unknown. 555 XBee64BitAddress addr64 = device.get64BitAddress(); 556 if (addr64 != null && addr64 != XBee64BitAddress.UNKNOWN_ADDRESS 557 && !addr64.equals(xbee64BitAddress) 558 && (xbee64BitAddress == null 559 || xbee64BitAddress.equals(XBee64BitAddress.UNKNOWN_ADDRESS))) { 560 xbee64BitAddress = addr64; 561 } 562 563 // TODO Change here the 16-bit address or maybe in ZigBee and 802.15.4? 564 // TODO Should the 16-bit address be always updated? Or following the same rule as the 64-bit address. 565 XBee16BitAddress addr16 = device.get16BitAddress(); 566 if (addr16 != null && !addr16.equals(xbee16BitAddress)) { 567 xbee16BitAddress = addr16; 568 } 569 570 //this.deviceType = device.deviceType; // This is not yet done. 571 572 // The operating mode: only API/API2. Do we need this for a remote device? 573 // The protocol of the device should be the same. 574 // The hardware version should be the same. 575 // The firmware version can change... 576 } 577 578 /** 579 * Adds the provided listener to the list of listeners to be notified 580 * when new packets are received. 581 * 582 * <p>If the listener has been already included, this method does nothing. 583 * </p> 584 * 585 * @param listener Listener to be notified when new packets are received. 586 * 587 * @throws NullPointerException if {@code listener == null} 588 * 589 * @see #removePacketListener(IPacketReceiveListener) 590 * @see com.digi.xbee.api.listeners.IPacketReceiveListener 591 */ 592 protected void addPacketListener(IPacketReceiveListener listener) { 593 if (listener == null) 594 throw new NullPointerException("Listener cannot be null."); 595 596 if (dataReader == null) 597 return; 598 dataReader.addPacketReceiveListener(listener); 599 } 600 601 /** 602 * Removes the provided listener from the list of packets listeners. 603 * 604 * <p>If the listener was not in the list this method does nothing.</p> 605 * 606 * @param listener Listener to be removed from the list of listeners. 607 * 608 * @throws NullPointerException if {@code listener == null} 609 * 610 * @see #addPacketListener(IPacketReceiveListener) 611 * @see com.digi.xbee.api.listeners.IPacketReceiveListener 612 */ 613 protected void removePacketListener(IPacketReceiveListener listener) { 614 if (listener == null) 615 throw new NullPointerException("Listener cannot be null."); 616 617 if (dataReader == null) 618 return; 619 dataReader.removePacketReceiveListener(listener); 620 } 621 622 /** 623 * Adds the provided listener to the list of listeners to be notified 624 * when new data is received. 625 * 626 * <p>If the listener has been already included this method does nothing. 627 * </p> 628 * 629 * @param listener Listener to be notified when new data is received. 630 * 631 * @throws NullPointerException if {@code listener == null} 632 * 633 * @see #removeDataListener(IDataReceiveListener) 634 * @see com.digi.xbee.api.listeners.IDataReceiveListener 635 */ 636 protected void addDataListener(IDataReceiveListener listener) { 637 if (listener == null) 638 throw new NullPointerException("Listener cannot be null."); 639 640 if (dataReader == null) 641 return; 642 dataReader.addDataReceiveListener(listener); 643 } 644 645 /** 646 * Removes the provided listener from the list of data listeners. 647 * 648 * <p>If the listener was not in the list this method does nothing.</p> 649 * 650 * @param listener Listener to be removed from the list of listeners. 651 * 652 * @throws NullPointerException if {@code listener == null} 653 * 654 * @see #addDataListener(IDataReceiveListener) 655 * @see com.digi.xbee.api.listeners.IDataReceiveListener 656 */ 657 protected void removeDataListener(IDataReceiveListener listener) { 658 if (listener == null) 659 throw new NullPointerException("Listener cannot be null."); 660 661 if (dataReader == null) 662 return; 663 dataReader.removeDataReceiveListener(listener); 664 } 665 666 /** 667 * Adds the provided listener to the list of listeners to be notified 668 * when new IO samples are received. 669 * 670 * <p>If the listener has been already included this method does nothing. 671 * </p> 672 * 673 * @param listener Listener to be notified when new IO samples are received. 674 * 675 * @throws NullPointerException if {@code listener == null} 676 * 677 * @see #removeIOSampleListener(IIOSampleReceiveListener) 678 * @see com.digi.xbee.api.listeners.IIOSampleReceiveListener 679 */ 680 protected void addIOSampleListener(IIOSampleReceiveListener listener) { 681 if (listener == null) 682 throw new NullPointerException("Listener cannot be null."); 683 684 if (dataReader == null) 685 return; 686 dataReader.addIOSampleReceiveListener(listener); 687 } 688 689 /** 690 * Removes the provided listener from the list of IO samples listeners. 691 * 692 * <p>If the listener was not in the list this method does nothing.</p> 693 * 694 * @param listener Listener to be removed from the list of listeners. 695 * 696 * @throws NullPointerException if {@code listener == null} 697 * 698 * @see #addIOSampleListener(IIOSampleReceiveListener) 699 * @see com.digi.xbee.api.listeners.IIOSampleReceiveListener 700 */ 701 protected void removeIOSampleListener(IIOSampleReceiveListener listener) { 702 if (listener == null) 703 throw new NullPointerException("Listener cannot be null."); 704 705 if (dataReader == null) 706 return; 707 dataReader.removeIOSampleReceiveListener(listener); 708 } 709 710 /** 711 * Adds the provided listener to the list of listeners to be notified 712 * when new Modem Status events are received. 713 * 714 * <p>If the listener has been already included this method does nothing. 715 * </p> 716 * 717 * @param listener Listener to be notified when new Modem Status events are 718 * received. 719 * 720 * @throws NullPointerException if {@code listener == null} 721 * 722 * @see #removeModemStatusListener(IModemStatusReceiveListener) 723 * @see com.digi.xbee.api.listeners.IModemStatusReceiveListener 724 */ 725 protected void addModemStatusListener(IModemStatusReceiveListener listener) { 726 if (listener == null) 727 throw new NullPointerException("Listener cannot be null."); 728 729 if (dataReader == null) 730 return; 731 dataReader.addModemStatusReceiveListener(listener); 732 } 733 734 /** 735 * Removes the provided listener from the list of Modem Status listeners. 736 * 737 * <p>If the listener was not in the list this method does nothing.</p> 738 * 739 * @param listener Listener to be removed from the list of listeners. 740 * 741 * @throws NullPointerException if {@code listener == null} 742 * 743 * @see #addModemStatusListener(IModemStatusReceiveListener) 744 * @see com.digi.xbee.api.listeners.IModemStatusReceiveListener 745 */ 746 protected void removeModemStatusListener(IModemStatusReceiveListener listener) { 747 if (listener == null) 748 throw new NullPointerException("Listener cannot be null."); 749 if (dataReader == null) 750 return; 751 dataReader.removeModemStatusReceiveListener(listener); 752 } 753 754 /** 755 * Adds the provided listener to the list of listeners to be notified 756 * when new explicit data packets are received. 757 * 758 * <p>If the listener has been already included this method does nothing. 759 * </p> 760 * 761 * @param listener Listener to be notified when new explicit data packets 762 * are received. 763 * 764 * @throws NullPointerException if {@code listener == null} 765 * 766 * @see #removeExplicitDataListener(IExplicitDataReceiveListener) 767 * @see com.digi.xbee.api.listeners.IExplicitDataReceiveListener 768 */ 769 protected void addExplicitDataListener(IExplicitDataReceiveListener listener) { 770 if (listener == null) 771 throw new NullPointerException("Listener cannot be null."); 772 773 if (dataReader == null) 774 return; 775 dataReader.addExplicitDataReceiveListener(listener); 776 } 777 778 /** 779 * Removes the provided listener from the list of explicit data receive 780 * listeners. 781 * 782 * <p>If the listener was not in the list this method does nothing.</p> 783 * 784 * @param listener Listener to be removed from the list of listeners. 785 * 786 * @throws NullPointerException if {@code listener == null} 787 * 788 * @see #addExplicitDataListener(IExplicitDataReceiveListener) 789 * @see com.digi.xbee.api.listeners.IExplicitDataReceiveListener 790 */ 791 protected void removeExplicitDataListener(IExplicitDataReceiveListener listener) { 792 if (listener == null) 793 throw new NullPointerException("Listener cannot be null."); 794 if (dataReader == null) 795 return; 796 dataReader.removeExplicitDataReceiveListener(listener); 797 } 798 799 /** 800 * Sends the given AT command and waits for answer or until the configured 801 * receive timeout expires. 802 * 803 * <p>The receive timeout is configured using the {@code setReceiveTimeout} 804 * method and can be consulted with {@code getReceiveTimeout} method.</p> 805 * 806 * @param command AT command to be sent. 807 * @return An {@code ATCommandResponse} object containing the response of 808 * the command or {@code null} if there is no response. 809 * 810 * @throws InterfaceNotOpenException if this device connection is not open. 811 * @throws InvalidOperatingModeException if the operating mode is different 812 * than {@link OperatingMode#API} and 813 * {@link OperatingMode#API_ESCAPE}. 814 * @throws IOException if an I/O error occurs while sending the AT command. 815 * @throws NullPointerException if {@code command == null}. 816 * @throws TimeoutException if the configured time expires while waiting 817 * for the command reply. 818 * 819 * @see XBeeDevice#getReceiveTimeout() 820 * @see XBeeDevice#setReceiveTimeout(int) 821 * @see com.digi.xbee.api.models.ATCommand 822 * @see com.digi.xbee.api.models.ATCommandResponse 823 */ 824 protected ATCommandResponse sendATCommand(ATCommand command) 825 throws InvalidOperatingModeException, TimeoutException, IOException { 826 // Check if command is null. 827 if (command == null) 828 throw new NullPointerException("AT command cannot be null."); 829 // Check connection. 830 if (!connectionInterface.isOpen()) 831 throw new InterfaceNotOpenException(); 832 833 ATCommandResponse response = null; 834 OperatingMode operatingMode = getOperatingMode(); 835 switch (operatingMode) { 836 case AT: 837 case UNKNOWN: 838 default: 839 throw new InvalidOperatingModeException(operatingMode); 840 case API: 841 case API_ESCAPE: 842 // Create the corresponding AT command packet depending on if the device is local or remote. 843 XBeePacket packet; 844 if (isRemote()) { 845 XBee16BitAddress remote16BitAddress = get16BitAddress(); 846 if (remote16BitAddress == null) 847 remote16BitAddress = XBee16BitAddress.UNKNOWN_ADDRESS; 848 int remoteATCommandOptions = RemoteATCommandOptions.OPTION_NONE; 849 if (isApplyConfigurationChangesEnabled()) 850 remoteATCommandOptions |= RemoteATCommandOptions.OPTION_APPLY_CHANGES; 851 packet = new RemoteATCommandPacket(getNextFrameID(), get64BitAddress(), remote16BitAddress, remoteATCommandOptions, command.getCommand(), command.getParameter()); 852 } else { 853 if (isApplyConfigurationChangesEnabled()) 854 packet = new ATCommandPacket(getNextFrameID(), command.getCommand(), command.getParameter()); 855 else 856 packet = new ATCommandQueuePacket(getNextFrameID(), command.getCommand(), command.getParameter()); 857 } 858 if (command.getParameter() == null) 859 logger.debug(toString() + "Sending AT command '{}'.", command.getCommand()); 860 else 861 logger.debug(toString() + "Sending AT command '{} {}'.", command.getCommand(), HexUtils.prettyHexString(command.getParameter())); 862 try { 863 // Send the packet and build the corresponding response depending on if the device is local or remote. 864 XBeePacket answerPacket; 865 if (isRemote()) 866 answerPacket = localXBeeDevice.sendXBeePacket(packet); 867 else 868 answerPacket = sendXBeePacket(packet); 869 if (answerPacket instanceof ATCommandResponsePacket) 870 response = new ATCommandResponse(command, ((ATCommandResponsePacket)answerPacket).getCommandValue(), ((ATCommandResponsePacket)answerPacket).getStatus()); 871 else if (answerPacket instanceof RemoteATCommandResponsePacket) 872 response = new ATCommandResponse(command, ((RemoteATCommandResponsePacket)answerPacket).getCommandValue(), ((RemoteATCommandResponsePacket)answerPacket).getStatus()); 873 874 if (response != null && response.getResponse() != null) 875 logger.debug(toString() + "AT command response: {}.", HexUtils.prettyHexString(response.getResponse())); 876 else 877 logger.debug(toString() + "AT command response: null."); 878 } catch (ClassCastException e) { 879 logger.error("Received an invalid packet type after sending an AT command packet." + e); 880 } 881 } 882 return response; 883 } 884 885 /** 886 * Sends the given XBee packet asynchronously. 887 * 888 * <p>The method will not wait for an answer for the packet.</p> 889 * 890 * <p>To be notified when the answer is received, use 891 * {@link #sendXBeePacket(XBeePacket, IPacketReceiveListener)}.</p> 892 * 893 * @param packet XBee packet to be sent asynchronously. 894 * 895 * @throws InterfaceNotOpenException if this device connection is not open. 896 * @throws InvalidOperatingModeException if the operating mode is different 897 * than {@link OperatingMode#API} and 898 * {@link OperatingMode#API_ESCAPE}. 899 * @throws IOException if an I/O error occurs while sending the XBee packet. 900 * @throws NullPointerException if {@code packet == null}. 901 * 902 * @see #sendXBeePacket(XBeePacket) 903 * @see #sendXBeePacket(XBeePacket, IPacketReceiveListener) 904 * @see #sendXBeePacketAsync(XBeePacket) 905 * @see com.digi.xbee.api.packet.XBeePacket 906 */ 907 protected void sendXBeePacketAsync(XBeePacket packet) 908 throws InvalidOperatingModeException, IOException { 909 sendXBeePacket(packet, null); 910 } 911 912 /** 913 * Sends the given XBee packet asynchronously and registers the given 914 * packet listener (if not {@code null}) to wait for an answer. 915 * 916 * <p>The method will not wait for an answer for the packet, but the given 917 * listener will be notified when the answer arrives.</p> 918 * 919 * @param packet XBee packet to be sent. 920 * @param packetReceiveListener Listener for the operation, {@code null} 921 * not to be notified when the answer arrives. 922 * 923 * @throws InterfaceNotOpenException if this device connection is not open. 924 * @throws InvalidOperatingModeException if the operating mode is different 925 * than {@link OperatingMode#API} and 926 * {@link OperatingMode#API_ESCAPE}. 927 * @throws IOException if an I/O error occurs while sending the XBee packet. 928 * @throws NullPointerException if {@code packet == null}. 929 * 930 * @see #sendXBeePacket(XBeePacket) 931 * @see #sendXBeePacket(XBeePacket, IPacketReceiveListener) 932 * @see #sendXBeePacketAsync(XBeePacket) 933 * @see com.digi.xbee.api.listeners.IPacketReceiveListener 934 * @see com.digi.xbee.api.packet.XBeePacket 935 */ 936 protected void sendXBeePacket(XBeePacket packet, IPacketReceiveListener packetReceiveListener) 937 throws InvalidOperatingModeException, IOException { 938 // Check if the packet to send is null. 939 if (packet == null) 940 throw new NullPointerException("XBee packet cannot be null."); 941 // Check connection. 942 if (!connectionInterface.isOpen()) 943 throw new InterfaceNotOpenException(); 944 945 OperatingMode operatingMode = getOperatingMode(); 946 switch (operatingMode) { 947 case AT: 948 case UNKNOWN: 949 default: 950 throw new InvalidOperatingModeException(operatingMode); 951 case API: 952 case API_ESCAPE: 953 // Add the required frame ID and subscribe listener if given. 954 if (packet instanceof XBeeAPIPacket) { 955 if (((XBeeAPIPacket)packet).needsAPIFrameID()) { 956 if (((XBeeAPIPacket)packet).getFrameID() == XBeeAPIPacket.NO_FRAME_ID) 957 ((XBeeAPIPacket)packet).setFrameID(getNextFrameID()); 958 if (packetReceiveListener != null) 959 dataReader.addPacketReceiveListener(packetReceiveListener, ((XBeeAPIPacket)packet).getFrameID()); 960 } else if (packetReceiveListener != null) 961 dataReader.addPacketReceiveListener(packetReceiveListener); 962 } 963 964 // Write packet data. 965 writePacket(packet); 966 break; 967 } 968 } 969 970 /** 971 * Sends the given XBee packet synchronously and blocks until response is 972 * received or receive timeout is reached. 973 * 974 * <p>The receive timeout is configured using the {@code setReceiveTimeout} 975 * method and can be consulted with {@code getReceiveTimeout} method.</p> 976 * 977 * <p>Use {@link #sendXBeePacketAsync(XBeePacket)} for non-blocking 978 * operations.</p> 979 * 980 * @param packet XBee packet to be sent. 981 * @return An {@code XBeePacket} containing the response of the sent packet 982 * or {@code null} if there is no response. 983 * 984 * @throws InterfaceNotOpenException if this device connection is not open. 985 * @throws InvalidOperatingModeException if the operating mode is different 986 * than {@link OperatingMode#API} and 987 * {@link OperatingMode#API_ESCAPE}. 988 * @throws IOException if an I/O error occurs while sending the XBee packet. 989 * @throws NullPointerException if {@code packet == null}. 990 * @throws TimeoutException if the configured time expires while waiting for 991 * the packet reply. 992 * 993 * @see #sendXBeePacket(XBeePacket) 994 * @see #sendXBeePacket(XBeePacket, IPacketReceiveListener) 995 * @see #sendXBeePacketAsync(XBeePacket) 996 * @see XBeeDevice#setReceiveTimeout(int) 997 * @see XBeeDevice#getReceiveTimeout() 998 * @see com.digi.xbee.api.packet.XBeePacket 999 */ 1000 protected XBeePacket sendXBeePacket(final XBeePacket packet) 1001 throws InvalidOperatingModeException, TimeoutException, IOException { 1002 // Check if the packet to send is null. 1003 if (packet == null) 1004 throw new NullPointerException("XBee packet cannot be null."); 1005 // Check connection. 1006 if (!connectionInterface.isOpen()) 1007 throw new InterfaceNotOpenException(); 1008 1009 OperatingMode operatingMode = getOperatingMode(); 1010 switch (operatingMode) { 1011 case AT: 1012 case UNKNOWN: 1013 default: 1014 throw new InvalidOperatingModeException(operatingMode); 1015 case API: 1016 case API_ESCAPE: 1017 // Build response container. 1018 ArrayList<XBeePacket> responseList = new ArrayList<XBeePacket>(); 1019 1020 // If the packet does not need frame ID, send it async. and return null. 1021 if (packet instanceof XBeeAPIPacket) { 1022 if (!((XBeeAPIPacket)packet).needsAPIFrameID()) { 1023 sendXBeePacketAsync(packet); 1024 return null; 1025 } 1026 } else { 1027 sendXBeePacketAsync(packet); 1028 return null; 1029 } 1030 1031 // Add the required frame ID to the packet if necessary. 1032 insertFrameID(packet); 1033 1034 // Generate a packet received listener for the packet to be sent. 1035 IPacketReceiveListener packetReceiveListener = createPacketReceivedListener(packet, responseList); 1036 1037 // Add the packet listener to the data reader. 1038 addPacketListener(packetReceiveListener); 1039 1040 // Write the packet data. 1041 writePacket(packet); 1042 try { 1043 // Wait for response or timeout. 1044 synchronized (responseList) { 1045 try { 1046 responseList.wait(receiveTimeout); 1047 } catch (InterruptedException e) {} 1048 } 1049 // After the wait check if we received any response, if not throw timeout exception. 1050 if (responseList.size() < 1) 1051 throw new TimeoutException(); 1052 // Return the received packet. 1053 return responseList.get(0); 1054 } finally { 1055 // Always remove the packet listener from the list. 1056 removePacketListener(packetReceiveListener); 1057 } 1058 } 1059 } 1060 1061 /** 1062 * Insert (if possible) the next frame ID stored in the device to the 1063 * provided packet. 1064 * 1065 * @param xbeePacket The packet to add the frame ID. 1066 * 1067 * @see com.digi.xbee.api.packet.XBeePacket 1068 */ 1069 private void insertFrameID(XBeePacket xbeePacket) { 1070 if (xbeePacket instanceof XBeeAPIPacket) 1071 return; 1072 1073 if (((XBeeAPIPacket)xbeePacket).needsAPIFrameID() && ((XBeeAPIPacket)xbeePacket).getFrameID() == XBeeAPIPacket.NO_FRAME_ID) 1074 ((XBeeAPIPacket)xbeePacket).setFrameID(getNextFrameID()); 1075 } 1076 1077 /** 1078 * Returns the packet listener corresponding to the provided sent packet. 1079 * 1080 * <p>The listener will filter those packets matching with the Frame ID of 1081 * the sent packet storing them in the provided responseList array.</p> 1082 * 1083 * @param sentPacket The packet sent. 1084 * @param responseList List of packets received that correspond to the 1085 * frame ID of the packet sent. 1086 * 1087 * @return A packet receive listener that will filter the packets received 1088 * corresponding to the sent one. 1089 * 1090 * @see com.digi.xbee.api.listeners.IPacketReceiveListener 1091 * @see com.digi.xbee.api.packet.XBeePacket 1092 */ 1093 private IPacketReceiveListener createPacketReceivedListener(final XBeePacket sentPacket, final ArrayList<XBeePacket> responseList) { 1094 IPacketReceiveListener packetReceiveListener = new IPacketReceiveListener() { 1095 /* 1096 * (non-Javadoc) 1097 * @see com.digi.xbee.api.listeners.IPacketReceiveListener#packetReceived(com.digi.xbee.api.packet.XBeePacket) 1098 */ 1099 @Override 1100 public void packetReceived(XBeePacket receivedPacket) { 1101 // Check if it is the packet we are waiting for. 1102 if (((XBeeAPIPacket)receivedPacket).checkFrameID((((XBeeAPIPacket)sentPacket).getFrameID()))) { 1103 // Security check to avoid class cast exceptions. It has been observed that parallel processes 1104 // using the same connection but with different frame index may collide and cause this exception at some point. 1105 if (sentPacket instanceof XBeeAPIPacket 1106 && receivedPacket instanceof XBeeAPIPacket) { 1107 XBeeAPIPacket sentAPIPacket = (XBeeAPIPacket)sentPacket; 1108 XBeeAPIPacket receivedAPIPacket = (XBeeAPIPacket)receivedPacket; 1109 1110 // If the packet sent is an AT command, verify that the received one is an AT command response and 1111 // the command matches in both packets. 1112 if (sentAPIPacket.getFrameType() == APIFrameType.AT_COMMAND) { 1113 if (receivedAPIPacket.getFrameType() != APIFrameType.AT_COMMAND_RESPONSE) 1114 return; 1115 if (!((ATCommandPacket)sentAPIPacket).getCommand().equalsIgnoreCase(((ATCommandResponsePacket)receivedPacket).getCommand())) 1116 return; 1117 } 1118 // If the packet sent is a remote AT command, verify that the received one is a remote AT command response and 1119 // the command matches in both packets. 1120 if (sentAPIPacket.getFrameType() == APIFrameType.REMOTE_AT_COMMAND_REQUEST) { 1121 if (receivedAPIPacket.getFrameType() != APIFrameType.REMOTE_AT_COMMAND_RESPONSE) 1122 return; 1123 if (!((RemoteATCommandPacket)sentAPIPacket).getCommand().equalsIgnoreCase(((RemoteATCommandResponsePacket)receivedPacket).getCommand())) 1124 return; 1125 } 1126 } 1127 1128 // Verify that the sent packet is not the received one! This can happen when the echo mode is enabled in the 1129 // serial port. 1130 if (!isSamePacket(sentPacket, receivedPacket)) { 1131 responseList.add(receivedPacket); 1132 synchronized (responseList) { 1133 responseList.notify(); 1134 } 1135 } 1136 } 1137 } 1138 }; 1139 1140 return packetReceiveListener; 1141 } 1142 1143 /** 1144 * Returns whether the sent packet is the same than the received one. 1145 * 1146 * @param sentPacket The packet sent. 1147 * @param receivedPacket The packet received. 1148 * 1149 * @return {@code true} if the sent packet is the same than the received 1150 * one, {@code false} otherwise. 1151 * 1152 * @see com.digi.xbee.api.packet.XBeePacket 1153 */ 1154 private boolean isSamePacket(XBeePacket sentPacket, XBeePacket receivedPacket) { 1155 // TODO Should not we implement the {@code equals} method in the XBeePacket?? 1156 if (HexUtils.byteArrayToHexString(sentPacket.generateByteArray()).equals(HexUtils.byteArrayToHexString(receivedPacket.generateByteArray()))) 1157 return true; 1158 return false; 1159 } 1160 1161 /** 1162 * Writes the given XBee packet in the connection interface of this device. 1163 * 1164 * @param packet XBee packet to be written. 1165 * 1166 * @throws IOException if an I/O error occurs while writing the XBee packet 1167 * in the connection interface. 1168 * 1169 * @see com.digi.xbee.api.packet.XBeePacket 1170 */ 1171 private void writePacket(XBeePacket packet) throws IOException { 1172 logger.debug(toString() + "Sending XBee packet: \n{}", packet.toPrettyString()); 1173 // Write bytes with the required escaping mode. 1174 switch (operatingMode) { 1175 case API: 1176 default: 1177 connectionInterface.writeData(packet.generateByteArray()); 1178 break; 1179 case API_ESCAPE: 1180 connectionInterface.writeData(packet.generateByteArrayEscaped()); 1181 break; 1182 } 1183 } 1184 1185 /** 1186 * Returns the next Frame ID of this XBee device. 1187 * 1188 * @return The next Frame ID. 1189 */ 1190 protected int getNextFrameID() { 1191 if (isRemote()) 1192 return localXBeeDevice.getNextFrameID(); 1193 if (currentFrameID == 0xff) { 1194 // Reset counter. 1195 currentFrameID = 1; 1196 } else 1197 currentFrameID ++; 1198 return currentFrameID; 1199 } 1200 1201 /** 1202 * Sends the provided {@code XBeePacket} and determines if the transmission 1203 * status is success for synchronous transmissions. 1204 * 1205 * <p>If the status is not success, an {@code TransmitException} is thrown.</p> 1206 * 1207 * @param packet The {@code XBeePacket} to be sent. 1208 * @param asyncTransmission Determines whether the transmission must be 1209 * asynchronous. 1210 * 1211 * @throws InterfaceNotOpenException if this device connection is not open. 1212 * @throws NullPointerException if {@code packet == null}. 1213 * @throws TransmitException if {@code packet} is not an instance of 1214 * {@code TransmitStatusPacket} or 1215 * if {@code packet} is not an instance of 1216 * {@code TXStatusPacket} or 1217 * if its transmit status is different than 1218 * {@code XBeeTransmitStatus.SUCCESS}. 1219 * @throws XBeeException if there is any other XBee related error. 1220 * 1221 * @see com.digi.xbee.api.packet.XBeePacket 1222 */ 1223 protected void sendAndCheckXBeePacket(XBeePacket packet, boolean asyncTransmission) throws TransmitException, XBeeException { 1224 XBeePacket receivedPacket = null; 1225 1226 // Send the XBee packet. 1227 try { 1228 if (asyncTransmission) 1229 sendXBeePacketAsync(packet); 1230 else 1231 receivedPacket = sendXBeePacket(packet); 1232 } catch (IOException e) { 1233 throw new XBeeException("Error writing in the communication interface.", e); 1234 } 1235 1236 // If the transmission is async. we are done. 1237 if (asyncTransmission) 1238 return; 1239 1240 // Check if the packet received is a valid transmit status packet. 1241 if (receivedPacket == null) 1242 throw new TransmitException(null); 1243 if (receivedPacket instanceof TransmitStatusPacket) { 1244 if (((TransmitStatusPacket)receivedPacket).getTransmitStatus() == null) 1245 throw new TransmitException(null); 1246 else if (((TransmitStatusPacket)receivedPacket).getTransmitStatus() != XBeeTransmitStatus.SUCCESS) 1247 throw new TransmitException(((TransmitStatusPacket)receivedPacket).getTransmitStatus()); 1248 } else if (receivedPacket instanceof TXStatusPacket) { 1249 if (((TXStatusPacket)receivedPacket).getTransmitStatus() == null) 1250 throw new TransmitException(null); 1251 else if (((TXStatusPacket)receivedPacket).getTransmitStatus() != XBeeTransmitStatus.SUCCESS) 1252 throw new TransmitException(((TXStatusPacket)receivedPacket).getTransmitStatus()); 1253 } else 1254 throw new TransmitException(null); 1255 } 1256 1257 /** 1258 * Sets the configuration of the given IO line of this XBee device. 1259 * 1260 * @param ioLine The IO line to configure. 1261 * @param ioMode The IO mode to set to the IO line. 1262 * 1263 * @throws InterfaceNotOpenException if this device connection is not open. 1264 * @throws NullPointerException if {@code ioLine == null} or 1265 * if {@code ioMode == null}. 1266 * @throws TimeoutException if there is a timeout sending the set 1267 * configuration command. 1268 * @throws XBeeException if there is any other XBee related exception. 1269 * 1270 * @see #getIOConfiguration(IOLine) 1271 * @see com.digi.xbee.api.io.IOLine 1272 * @see com.digi.xbee.api.io.IOMode 1273 */ 1274 public void setIOConfiguration(IOLine ioLine, IOMode ioMode) throws TimeoutException, XBeeException { 1275 // Check IO line. 1276 if (ioLine == null) 1277 throw new NullPointerException("IO line cannot be null."); 1278 if (ioMode == null) 1279 throw new NullPointerException("IO mode cannot be null."); 1280 // Check connection. 1281 if (!connectionInterface.isOpen()) 1282 throw new InterfaceNotOpenException(); 1283 1284 setParameter(ioLine.getConfigurationATCommand(), new byte[]{(byte)ioMode.getID()}); 1285 } 1286 1287 /** 1288 * Returns the configuration mode of the provided IO line of this XBee 1289 * device. 1290 * 1291 * @param ioLine The IO line to get its configuration. 1292 * 1293 * @return The IO mode (configuration) of the provided IO line. 1294 * 1295 * @throws InterfaceNotOpenException if this device connection is not open. 1296 * @throws NullPointerException if {@code ioLine == null}. 1297 * @throws TimeoutException if there is a timeout sending the get 1298 * configuration command. 1299 * @throws XBeeException if there is any other XBee related exception. 1300 * 1301 * @see #setIOConfiguration(IOLine, IOMode) 1302 * @see com.digi.xbee.api.io.IOLine 1303 * @see com.digi.xbee.api.io.IOMode 1304 */ 1305 public IOMode getIOConfiguration(IOLine ioLine) throws TimeoutException, XBeeException { 1306 // Check IO line. 1307 if (ioLine == null) 1308 throw new NullPointerException("DIO pin cannot be null."); 1309 // Check connection. 1310 if (!connectionInterface.isOpen()) 1311 throw new InterfaceNotOpenException(); 1312 1313 // Check if the received configuration mode is valid. 1314 int ioModeValue = getParameter(ioLine.getConfigurationATCommand())[0]; 1315 IOMode dioMode = IOMode.getIOMode(ioModeValue, ioLine); 1316 if (dioMode == null) 1317 throw new OperationNotSupportedException("Received configuration mode '" + HexUtils.integerToHexString(ioModeValue, 1) + "' is not valid."); 1318 1319 // Return the configuration mode. 1320 return dioMode; 1321 } 1322 1323 /** 1324 * Sets the digital value (high or low) to the provided IO line of this 1325 * XBee device. 1326 * 1327 * @param ioLine The IO line to set its value. 1328 * @param ioValue The IOValue to set to the IO line ({@code HIGH} or 1329 * {@code LOW}). 1330 * 1331 * @throws InterfaceNotOpenException if this device connection is not open. 1332 * @throws NullPointerException if {@code ioLine == null} or 1333 * if {@code ioValue == null}. 1334 * @throws TimeoutException if there is a timeout sending the set DIO 1335 * command. 1336 * @throws XBeeException if there is any other XBee related exception. 1337 * 1338 * @see #getIOConfiguration(IOLine) 1339 * @see #setIOConfiguration(IOLine, IOMode) 1340 * @see com.digi.xbee.api.io.IOLine 1341 * @see com.digi.xbee.api.io.IOValue 1342 * @see com.digi.xbee.api.io.IOMode#DIGITAL_OUT_HIGH 1343 * @see com.digi.xbee.api.io.IOMode#DIGITAL_OUT_LOW 1344 */ 1345 public void setDIOValue(IOLine ioLine, IOValue ioValue) throws TimeoutException, XBeeException { 1346 // Check IO line. 1347 if (ioLine == null) 1348 throw new NullPointerException("IO line cannot be null."); 1349 // Check IO value. 1350 if (ioValue == null) 1351 throw new NullPointerException("IO value cannot be null."); 1352 // Check connection. 1353 if (!connectionInterface.isOpen()) 1354 throw new InterfaceNotOpenException(); 1355 1356 setParameter(ioLine.getConfigurationATCommand(), new byte[]{(byte)ioValue.getID()}); 1357 } 1358 1359 /** 1360 * Returns the digital value of the provided IO line of this XBee device. 1361 * 1362 * <p>The provided <b>IO line must be previously configured as digital I/O 1363 * </b>. To do so, use {@code setIOConfiguration} and the following 1364 * {@code IOMode}:</p> 1365 * 1366 * <ul> 1367 * <li>{@code IOMode.DIGITAL_IN} to configure as digital input.</li> 1368 * <li>{@code IOMode.DIGITAL_OUT_HIGH} to configure as digital output, high. 1369 * </li> 1370 * <li>{@code IOMode.DIGITAL_OUT_LOW} to configure as digital output, low. 1371 * </li> 1372 * </ul> 1373 * 1374 * @param ioLine The IO line to get its digital value. 1375 * 1376 * @return The digital value corresponding to the provided IO line. 1377 * 1378 * @throws InterfaceNotOpenException if this device connection is not open. 1379 * @throws NullPointerException if {@code ioLine == null}. 1380 * @throws TimeoutException if there is a timeout sending the get IO values 1381 * command. 1382 * @throws XBeeException if there is any other XBee related exception. 1383 * 1384 * @see #getIOConfiguration(IOLine) 1385 * @see #setIOConfiguration(IOLine, IOMode) 1386 * @see com.digi.xbee.api.io.IOLine 1387 * @see com.digi.xbee.api.io.IOValue 1388 * @see com.digi.xbee.api.io.IOMode#DIGITAL_IN 1389 * @see com.digi.xbee.api.io.IOMode#DIGITAL_OUT_HIGH 1390 * @see com.digi.xbee.api.io.IOMode#DIGITAL_OUT_LOW 1391 */ 1392 public IOValue getDIOValue(IOLine ioLine) throws TimeoutException, XBeeException { 1393 // Check IO line. 1394 if (ioLine == null) 1395 throw new NullPointerException("IO line cannot be null."); 1396 1397 // Obtain an IO Sample from the XBee device. 1398 IOSample ioSample = readIOSample(); 1399 1400 // Check if the IO sample contains the expected IO line and value. 1401 if (!ioSample.hasDigitalValues() || !ioSample.getDigitalValues().containsKey(ioLine)) 1402 throw new OperationNotSupportedException("Answer does not contain digital data for " + ioLine.getName() + "."); 1403 1404 // Return the digital value. 1405 return ioSample.getDigitalValues().get(ioLine); 1406 } 1407 1408 /** 1409 * Sets the duty cycle (in %) of the provided IO line of this XBee device. 1410 * 1411 * <p>The provided <b>IO line must be</b>:</p> 1412 * 1413 * <ul> 1414 * <li><b>PWM capable</b> ({@link IOLine#hasPWMCapability()}).</li> 1415 * <li>Previously <b>configured as PWM Output</b> (use 1416 * {@code setIOConfiguration} and {@code IOMode.PWM}).</li> 1417 * </ul> 1418 * 1419 * @param ioLine The IO line to set its duty cycle value. 1420 * @param dutyCycle The duty cycle of the PWM. 1421 * 1422 * @throws IllegalArgumentException if {@code ioLine.hasPWMCapability() == false} or 1423 * if {@code value < 0} or 1424 * if {@code value > 1023}. 1425 * @throws InterfaceNotOpenException if this device connection is not open. 1426 * @throws NullPointerException if {@code ioLine == null}. 1427 * @throws TimeoutException if there is a timeout sending the set PWM duty 1428 * cycle command. 1429 * @throws XBeeException if there is any other XBee related exception. 1430 * 1431 * @see #getIOConfiguration(IOLine) 1432 * @see #getIOConfiguration(IOLine) 1433 * @see #setIOConfiguration(IOLine, IOMode) 1434 * @see #getPWMDutyCycle(IOLine) 1435 * @see com.digi.xbee.api.io.IOLine 1436 * @see com.digi.xbee.api.io.IOMode#PWM 1437 */ 1438 public void setPWMDutyCycle(IOLine ioLine, double dutyCycle) throws TimeoutException, XBeeException { 1439 // Check IO line. 1440 if (ioLine == null) 1441 throw new NullPointerException("IO line cannot be null."); 1442 // Check if the IO line has PWM capability. 1443 if (!ioLine.hasPWMCapability()) 1444 throw new IllegalArgumentException("Provided IO line does not have PWM capability."); 1445 // Check duty cycle limits. 1446 if (dutyCycle < 0 || dutyCycle > 100) 1447 throw new IllegalArgumentException("Duty Cycle must be between 0% and 100%."); 1448 // Check connection. 1449 if (!connectionInterface.isOpen()) 1450 throw new InterfaceNotOpenException(); 1451 1452 // Convert the value. 1453 int finaldutyCycle = (int)(dutyCycle * 1023.0/100.0); 1454 1455 setParameter(ioLine.getPWMDutyCycleATCommand(), ByteUtils.intToByteArray(finaldutyCycle)); 1456 } 1457 1458 /** 1459 * Gets the PWM duty cycle (in %) corresponding to the provided IO line of 1460 * this XBee device. 1461 * 1462 * <p>The provided <b>IO line must be</b>:</p> 1463 * 1464 * <ul> 1465 * <li><b>PWM capable</b> ({@link IOLine#hasPWMCapability()}).</li> 1466 * <li>Previously <b>configured as PWM Output</b> (use 1467 * {@code setIOConfiguration} and {@code IOMode.PWM}).</li> 1468 * </ul> 1469 * 1470 * @param ioLine The IO line to get its PWM duty cycle. 1471 * 1472 * @return The PWM duty cycle value corresponding to the provided IO line 1473 * (0% - 100%). 1474 * 1475 * @throws IllegalArgumentException if {@code ioLine.hasPWMCapability() == false}. 1476 * @throws InterfaceNotOpenException if this device connection is not open. 1477 * @throws NullPointerException if {@code ioLine == null}. 1478 * @throws TimeoutException if there is a timeout sending the get PWM duty 1479 * cycle command. 1480 * @throws XBeeException if there is any other XBee related exception. 1481 * 1482 * @see #getIOConfiguration(IOLine) 1483 * @see #setIOConfiguration(IOLine, IOMode) 1484 * @see #setPWMDutyCycle(IOLine, double) 1485 * @see com.digi.xbee.api.io.IOLine 1486 * @see com.digi.xbee.api.io.IOMode#PWM 1487 */ 1488 public double getPWMDutyCycle(IOLine ioLine) throws TimeoutException, XBeeException { 1489 // Check IO line. 1490 if (ioLine == null) 1491 throw new NullPointerException("IO line cannot be null."); 1492 // Check if the IO line has PWM capability. 1493 if (!ioLine.hasPWMCapability()) 1494 throw new IllegalArgumentException("Provided IO line does not have PWM capability."); 1495 // Check connection. 1496 if (!connectionInterface.isOpen()) 1497 throw new InterfaceNotOpenException(); 1498 1499 byte[] value = getParameter(ioLine.getPWMDutyCycleATCommand()); 1500 1501 // Return the PWM duty cycle value. 1502 int readValue = ByteUtils.byteArrayToInt(value); 1503 return Math.round((readValue * 100.0/1023.0) * 100.0) / 100.0; 1504 } 1505 1506 /** 1507 * Returns the analog value of the provided IO line of this XBee device. 1508 * 1509 * <p>The provided <b>IO line must be previously configured as ADC</b>. To 1510 * do so, use {@code setIOConfiguration} and {@code IOMode.ADC}.</p> 1511 * 1512 * @param ioLine The IO line to get its analog value. 1513 * 1514 * @return The analog value corresponding to the provided IO line. 1515 * 1516 * @throws InterfaceNotOpenException if this device connection is not open. 1517 * @throws NullPointerException if {@code ioLine == null}. 1518 * @throws TimeoutException if there is a timeout sending the get IO values 1519 * command. 1520 * @throws XBeeException if there is any other XBee related exception. 1521 * 1522 * @see #getIOConfiguration(IOLine) 1523 * @see #setIOConfiguration(IOLine, IOMode) 1524 * @see com.digi.xbee.api.io.IOLine 1525 * @see com.digi.xbee.api.io.IOMode#ADC 1526 */ 1527 public int getADCValue(IOLine ioLine) throws TimeoutException, XBeeException { 1528 // Check IO line. 1529 if (ioLine == null) 1530 throw new NullPointerException("IO line cannot be null."); 1531 1532 // Obtain an IO Sample from the XBee device. 1533 IOSample ioSample = readIOSample(); 1534 1535 // Check if the IO sample contains the expected IO line and value. 1536 if (!ioSample.hasAnalogValues() || !ioSample.getAnalogValues().containsKey(ioLine)) 1537 throw new OperationNotSupportedException("Answer does not contain analog data for " + ioLine.getName() + "."); 1538 1539 // Return the analog value. 1540 return ioSample.getAnalogValues().get(ioLine); 1541 } 1542 1543 /** 1544 * Sets the 64-bit destination extended address of this XBee device. 1545 * 1546 * <p>{@link XBee64BitAddress#BROADCAST_ADDRESS} is the broadcast address 1547 * for the PAN. {@link XBee64BitAddress#COORDINATOR_ADDRESS} can be used to 1548 * address the Pan Coordinator.</p> 1549 * 1550 * @param xbee64BitAddress 64-bit destination address to be configured. 1551 * 1552 * @throws InterfaceNotOpenException if this device connection is not open. 1553 * @throws NullPointerException if {@code xbee64BitAddress == null}. 1554 * @throws TimeoutException if there is a timeout sending the set 1555 * destination address command. 1556 * @throws XBeeException if there is any other XBee related exception. 1557 * 1558 * @see #getDestinationAddress() 1559 * @see com.digi.xbee.api.models.XBee64BitAddress 1560 */ 1561 public void setDestinationAddress(XBee64BitAddress xbee64BitAddress) throws TimeoutException, XBeeException { 1562 if (xbee64BitAddress == null) 1563 throw new NullPointerException("Address cannot be null."); 1564 1565 // This method needs to apply changes after modifying the destination 1566 // address, but only if the destination address could be set successfully. 1567 boolean applyChanges = isApplyConfigurationChangesEnabled(); 1568 if (applyChanges) 1569 enableApplyConfigurationChanges(false); 1570 1571 byte[] address = xbee64BitAddress.getValue(); 1572 try { 1573 setParameter("DH", Arrays.copyOfRange(address, 0, 4)); 1574 setParameter("DL", Arrays.copyOfRange(address, 4, 8)); 1575 applyChanges(); 1576 } finally { 1577 // Always restore the old value of the AC. 1578 enableApplyConfigurationChanges(applyChanges); 1579 } 1580 } 1581 1582 /** 1583 * Returns the 64-bit destination extended address of this XBee device. 1584 * 1585 * <p>{@link XBee64BitAddress#BROADCAST_ADDRESS} is the broadcast address 1586 * for the PAN. {@link XBee64BitAddress#COORDINATOR_ADDRESS} can be used to 1587 * address the Pan Coordinator.</p> 1588 * 1589 * @return 64-bit destination address. 1590 * 1591 * @throws InterfaceNotOpenException if this device connection is not open. 1592 * @throws TimeoutException if there is a timeout sending the get 1593 * destination address command. 1594 * @throws XBeeException if there is any other XBee related exception. 1595 * 1596 * @see #setDestinationAddress(XBee64BitAddress) 1597 * @see com.digi.xbee.api.models.XBee64BitAddress 1598 */ 1599 public XBee64BitAddress getDestinationAddress() throws TimeoutException, XBeeException { 1600 byte[] dh = getParameter("DH"); 1601 byte[] dl = getParameter("DL"); 1602 byte[] address = new byte[dh.length + dl.length]; 1603 1604 System.arraycopy(dh, 0, address, 0, dh.length); 1605 System.arraycopy(dl, 0, address, dh.length, dl.length); 1606 1607 return new XBee64BitAddress(address); 1608 } 1609 1610 /** 1611 * Sets the IO sampling rate to enable periodic sampling in this XBee 1612 * device. 1613 * 1614 * <p>A sample rate of {@code 0} ms. disables this feature.</p> 1615 * 1616 * <p>All enabled digital IO and analog inputs will be sampled and 1617 * transmitted every {@code rate} milliseconds to the configured destination 1618 * address.</p> 1619 * 1620 * <p>The destination address can be configured using the 1621 * {@code setDestinationAddress(XBee64BitAddress)} method and retrieved by 1622 * {@code getDestinationAddress()}.</p> 1623 * 1624 * @param rate IO sampling rate in milliseconds. 1625 * 1626 * @throws IllegalArgumentException if {@code rate < 0} or {@code rate > 1627 * 0xFFFF}. 1628 * @throws InterfaceNotOpenException if this device connection is not open. 1629 * @throws TimeoutException if there is a timeout sending the set IO 1630 * sampling rate command. 1631 * @throws XBeeException if there is any other XBee related exception. 1632 * 1633 * @see #getDestinationAddress() 1634 * @see #setDestinationAddress(XBee64BitAddress) 1635 * @see #getIOSamplingRate() 1636 */ 1637 public void setIOSamplingRate(int rate) throws TimeoutException, XBeeException { 1638 // Check range. 1639 if (rate < 0 || rate > 0xFFFF) 1640 throw new IllegalArgumentException("Rate must be between 0 and 0xFFFF."); 1641 // Check connection. 1642 if (!connectionInterface.isOpen()) 1643 throw new InterfaceNotOpenException(); 1644 1645 setParameter("IR", ByteUtils.intToByteArray(rate)); 1646 } 1647 1648 /** 1649 * Returns the IO sampling rate of this XBee device. 1650 * 1651 * <p>A sample rate of {@code 0} means the IO sampling feature is disabled. 1652 * </p> 1653 * 1654 * <p>Periodic sampling allows this XBee module to take an IO sample and 1655 * transmit it to a remote device (configured in the destination address) 1656 * at the configured periodic rate (ms).</p> 1657 * 1658 * @return IO sampling rate in milliseconds. 1659 * 1660 * @throws InterfaceNotOpenException if this device connection is not open. 1661 * @throws TimeoutException if there is a timeout sending the get IO 1662 * sampling rate command. 1663 * @throws XBeeException if there is any other XBee related exception. 1664 * 1665 * @see #getDestinationAddress() 1666 * @see #setDestinationAddress(XBee64BitAddress) 1667 * @see #setIOSamplingRate(int) 1668 */ 1669 public int getIOSamplingRate() throws TimeoutException, XBeeException { 1670 // Check connection. 1671 if (!connectionInterface.isOpen()) 1672 throw new InterfaceNotOpenException(); 1673 1674 byte[] rate = getParameter("IR"); 1675 return ByteUtils.byteArrayToInt(rate); 1676 } 1677 1678 /** 1679 * Sets the digital IO lines of this XBee device to be monitored and 1680 * sampled whenever their status changes. 1681 * 1682 * <p>A {@code null} set of lines disables this feature.</p> 1683 * 1684 * <p>If a change is detected on an enabled digital IO pin, a digital IO 1685 * sample is immediately transmitted to the configured destination address. 1686 * </p> 1687 * 1688 * <p>The destination address can be configured using the 1689 * {@code setDestinationAddress(XBee64BitAddress)} method and retrieved by 1690 * {@code getDestinationAddress()}.</p> 1691 * 1692 * @param lines Set of IO lines to be monitored, {@code null} to disable 1693 * this feature. 1694 * 1695 * @throws InterfaceNotOpenException if this device connection is not open. 1696 * @throws TimeoutException if there is a timeout sending the set DIO 1697 * change detection command. 1698 * @throws XBeeException if there is any other XBee related exception. 1699 * 1700 * @see #getDestinationAddress() 1701 * @see #getDIOChangeDetection() 1702 * @see #setDestinationAddress(XBee64BitAddress) 1703 */ 1704 public void setDIOChangeDetection(Set<IOLine> lines) throws TimeoutException, XBeeException { 1705 // Check connection. 1706 if (!connectionInterface.isOpen()) 1707 throw new InterfaceNotOpenException(); 1708 1709 byte[] bitfield = new byte[2]; 1710 1711 if (lines != null) { 1712 for (IOLine line : lines) { 1713 int i = line.getIndex(); 1714 if (i < 8) 1715 bitfield[1] = (byte) (bitfield[1] | (1 << i)); 1716 else 1717 bitfield[0] = (byte) (bitfield[0] | (1 << i - 8)); 1718 } 1719 } 1720 1721 setParameter("IC", bitfield); 1722 } 1723 1724 /** 1725 * Returns the set of IO lines of this device that are monitored for 1726 * change detection. 1727 * 1728 * <p>A {@code null} set means the DIO change detection feature is disabled. 1729 * </p> 1730 * 1731 * <p>Modules can be configured to transmit to the configured destination 1732 * address a data sample immediately whenever a monitored digital IO line 1733 * changes state.</p> 1734 * 1735 * @return Set of digital IO lines that are monitored for change detection, 1736 * {@code null} if there are no monitored lines. 1737 * 1738 * @throws InterfaceNotOpenException if this device connection is not open. 1739 * @throws TimeoutException if there is a timeout sending the get DIO 1740 * change detection command. 1741 * @throws XBeeException if there is any other XBee related exception. 1742 * 1743 * @see #getDestinationAddress() 1744 * @see #setDestinationAddress(XBee64BitAddress) 1745 * @see #setDIOChangeDetection(Set) 1746 */ 1747 public Set<IOLine> getDIOChangeDetection() throws TimeoutException, XBeeException { 1748 // Check connection. 1749 if (!connectionInterface.isOpen()) 1750 throw new InterfaceNotOpenException(); 1751 1752 byte[] bitfield = getParameter("IC"); 1753 TreeSet<IOLine> lines = new TreeSet<IOLine>(); 1754 int mask = (bitfield[0] << 8) + (bitfield[1] & 0xFF); 1755 1756 for (int i = 0; i < 16; i++) { 1757 if (ByteUtils.isBitEnabled(mask, i)) 1758 lines.add(IOLine.getDIO(i)); 1759 } 1760 1761 if (lines.size() > 0) 1762 return lines; 1763 return null; 1764 } 1765 1766 /** 1767 * Applies changes to all command registers causing queued command register 1768 * values to be applied. 1769 * 1770 * <p>This method must be invoked if the 'apply configuration changes' 1771 * option is disabled and the changes to this XBee device parameters must 1772 * be applied.</p> 1773 * 1774 * <p>To know if the 'apply configuration changes' option is enabled, use 1775 * the {@code isApplyConfigurationChangesEnabled()} method. And to 1776 * enable/disable this feature, the method 1777 * {@code enableApplyConfigurationChanges(boolean)}.</p> 1778 * 1779 * <p>Applying changes does not imply the modifications will persist 1780 * through subsequent resets. To do so, use the {@code writeChanges()} 1781 * method.</p> 1782 * 1783 * @throws InterfaceNotOpenException if this device connection is not open. 1784 * @throws TimeoutException if there is a timeout sending the get Apply 1785 * Changes command. 1786 * @throws XBeeException if there is any other XBee related exception. 1787 * 1788 * @see #enableApplyConfigurationChanges(boolean) 1789 * @see #isApplyConfigurationChangesEnabled() 1790 * @see #setParameter(String, byte[]) 1791 * @see #writeChanges() 1792 */ 1793 public void applyChanges() throws TimeoutException, XBeeException { 1794 executeParameter("AC"); 1795 } 1796 1797 /** 1798 * Checks if the provided {@code ATCommandResponse} is valid throwing an 1799 * {@code ATCommandException} in case it is not. 1800 * 1801 * @param response The {@code ATCommandResponse} to check. 1802 * 1803 * @throws ATCommandException if {@code response == null} or 1804 * if {@code response.getResponseStatus() != ATCommandStatus.OK}. 1805 * 1806 * @see com.digi.xbee.api.models.ATCommandResponse 1807 */ 1808 protected void checkATCommandResponseIsValid(ATCommandResponse response) throws ATCommandException { 1809 if (response == null || response.getResponseStatus() == null) 1810 throw new ATCommandException(null); 1811 else if (response.getResponseStatus() != ATCommandStatus.OK) 1812 throw new ATCommandException(response.getResponseStatus()); 1813 } 1814 1815 /** 1816 * Returns an IO sample from this XBee device containing the value of all 1817 * enabled digital IO and analog input channels. 1818 * 1819 * @return An IO sample containing the value of all enabled digital IO and 1820 * analog input channels. 1821 * 1822 * @throws InterfaceNotOpenException if this device connection is not open. 1823 * @throws TimeoutException if there is a timeout getting the IO sample. 1824 * @throws XBeeException if there is any other XBee related exception. 1825 * 1826 * @see com.digi.xbee.api.io.IOSample 1827 */ 1828 public IOSample readIOSample() throws TimeoutException, XBeeException { 1829 // Check connection. 1830 if (!connectionInterface.isOpen()) 1831 throw new InterfaceNotOpenException(); 1832 1833 // Try to build an IO Sample from the sample payload. 1834 byte[] samplePayload = null; 1835 IOSample ioSample; 1836 1837 // The response to the IS command in local 802.15.4 devices is empty, 1838 // so we have to create a packet listener to receive the IO sample. 1839 if (!isRemote() && getXBeeProtocol() == XBeeProtocol.RAW_802_15_4) { 1840 executeParameter("IS"); 1841 samplePayload = receiveRaw802IOPacket(); 1842 if (samplePayload == null) 1843 throw new TimeoutException("Timeout waiting for the IO response packet."); 1844 } else 1845 samplePayload = getParameter("IS"); 1846 1847 try { 1848 ioSample = new IOSample(samplePayload); 1849 } catch (IllegalArgumentException e) { 1850 throw new XBeeException("Couldn't create the IO sample.", e); 1851 } catch (NullPointerException e) { 1852 throw new XBeeException("Couldn't create the IO sample.", e); 1853 } 1854 return ioSample; 1855 } 1856 1857 /** 1858 * Returns the latest 802.15.4 IO packet and returns its value. 1859 * 1860 * @return The value of the latest received 802.15.4 IO packet. 1861 */ 1862 private byte[] receiveRaw802IOPacket() { 1863 ioPacketReceived = false; 1864 ioPacketPayload = null; 1865 addPacketListener(IOPacketReceiveListener); 1866 synchronized (ioLock) { 1867 try { 1868 ioLock.wait(receiveTimeout); 1869 } catch (InterruptedException e) { } 1870 } 1871 removePacketListener(IOPacketReceiveListener); 1872 if (ioPacketReceived) 1873 return ioPacketPayload; 1874 return null; 1875 } 1876 1877 /** 1878 * Custom listener for 802.15.4 IO packets. It will try to receive an 1879 * 802.15.4 IO sample packet. 1880 * 1881 * <p>When an IO sample packet is received, it saves its payload and 1882 * notifies the object that was waiting for the reception.</p> 1883 */ 1884 private IPacketReceiveListener IOPacketReceiveListener = new IPacketReceiveListener() { 1885 /* 1886 * (non-Javadoc) 1887 * @see com.digi.xbee.api.listeners.IPacketReceiveListener#packetReceived(com.digi.xbee.api.packet.XBeePacket) 1888 */ 1889 @Override 1890 public void packetReceived(XBeePacket receivedPacket) { 1891 // Discard non API packets. 1892 if (!(receivedPacket instanceof XBeeAPIPacket)) 1893 return; 1894 // If we already have received an IO packet, ignore this packet. 1895 if (ioPacketReceived) 1896 return; 1897 1898 // Save the packet value (IO sample payload) 1899 switch (((XBeeAPIPacket)receivedPacket).getFrameType()) { 1900 case IO_DATA_SAMPLE_RX_INDICATOR: 1901 ioPacketPayload = ((IODataSampleRxIndicatorPacket)receivedPacket).getRFData(); 1902 break; 1903 case RX_IO_16: 1904 ioPacketPayload = ((RX16IOPacket)receivedPacket).getRFData(); 1905 break; 1906 case RX_IO_64: 1907 ioPacketPayload = ((RX64IOPacket)receivedPacket).getRFData(); 1908 break; 1909 default: 1910 return; 1911 } 1912 // Set the IO packet received flag. 1913 ioPacketReceived = true; 1914 1915 // Continue execution by notifying the lock object. 1916 synchronized (ioLock) { 1917 ioLock.notify(); 1918 } 1919 } 1920 }; 1921 1922 /** 1923 * Performs a software reset on this XBee device and blocks until the 1924 * process is completed. 1925 * 1926 * @throws TimeoutException if the configured time expires while waiting 1927 * for the command reply. 1928 * @throws XBeeException if there is any other XBee related exception. 1929 */ 1930 abstract public void reset() throws TimeoutException, XBeeException; 1931 1932 /** 1933 * Sets the given parameter with the provided value in this XBee device. 1934 * 1935 * <p>If the 'apply configuration changes' option is enabled in this device, 1936 * the configured value for the given parameter will be immediately applied, 1937 * if not the method {@code applyChanges()} must be invoked to apply it.</p> 1938 * 1939 * <p>Use:</p> 1940 * <ul> 1941 * <li>Method {@code isApplyConfigurationChangesEnabled()} to know 1942 * if the 'apply configuration changes' option is enabled.</li> 1943 * <li>Method {@code enableApplyConfigurationChanges(boolean)} to enable or 1944 * disable this option.</li> 1945 * </ul> 1946 * 1947 * <p>To make parameter modifications persist through subsequent resets use 1948 * the {@code writeChanges()} method.</p> 1949 * 1950 * @param parameter The name of the parameter to be set. 1951 * @param parameterValue The value of the parameter to set. 1952 * 1953 * @throws IllegalArgumentException if {@code parameter.length() != 2}. 1954 * @throws NullPointerException if {@code parameter == null} or 1955 * if {@code parameterValue == null}. 1956 * @throws TimeoutException if there is a timeout setting the parameter. 1957 * @throws XBeeException if {@code parameter} is not supported by the module or 1958 * if {@code parameterValue} is not supported or 1959 * if there is any other XBee related exception. 1960 * 1961 * @see #applyChanges() 1962 * @see #enableApplyConfigurationChanges(boolean) 1963 * @see #executeParameter(String) 1964 * @see #getParameter(String) 1965 * @see #isApplyConfigurationChangesEnabled() 1966 * @see #writeChanges() 1967 */ 1968 public void setParameter(String parameter, byte[] parameterValue) throws TimeoutException, XBeeException { 1969 if (parameterValue == null) 1970 throw new NullPointerException("Value of the parameter cannot be null."); 1971 1972 sendParameter(parameter, parameterValue); 1973 } 1974 1975 /** 1976 * Gets the value of the given parameter from this XBee device. 1977 * 1978 * @param parameter The name of the parameter to retrieve its value. 1979 * 1980 * @return A byte array containing the value of the parameter. 1981 * 1982 * @throws IllegalArgumentException if {@code parameter.length() != 2}. 1983 * @throws NullPointerException if {@code parameter == null}. 1984 * @throws TimeoutException if there is a timeout getting the parameter value. 1985 * @throws XBeeException if {@code parameter} is not supported by the module or 1986 * if there is any other XBee related exception. 1987 * 1988 * @see #executeParameter(String) 1989 * @see #setParameter(String, byte[]) 1990 */ 1991 public byte[] getParameter(String parameter) throws TimeoutException, XBeeException { 1992 byte[] parameterValue = sendParameter(parameter, null); 1993 1994 // Check if the response is null, if so throw an exception (maybe it was a write-only parameter). 1995 if (parameterValue == null) 1996 throw new OperationNotSupportedException("Couldn't get the '" + parameter + "' value."); 1997 return parameterValue; 1998 } 1999 2000 /** 2001 * Executes the given command in this XBee device. 2002 * 2003 * <p>This method is intended to be used for those AT parameters that cannot 2004 * be read or written, they just execute some action in the XBee module.</p> 2005 * 2006 * @param parameter The AT command to be executed. 2007 * 2008 * @throws IllegalArgumentException if {@code parameter.length() != 2}. 2009 * @throws NullPointerException if {@code parameter == null}. 2010 * @throws TimeoutException if there is a timeout executing the parameter. 2011 * @throws XBeeException if {@code parameter} is not supported by the module or 2012 * if there is any other XBee related exception. 2013 * 2014 * @see #getParameter(String) 2015 * @see #setParameter(String, byte[]) 2016 */ 2017 public void executeParameter(String parameter) throws TimeoutException, XBeeException { 2018 sendParameter(parameter, null); 2019 } 2020 2021 /** 2022 * Sends the given AT parameter to this XBee device with an optional 2023 * argument or value and returns the response (likely the value) of that 2024 * parameter in a byte array format. 2025 * 2026 * @param parameter The name of the AT command to be executed. 2027 * @param parameterValue The value of the parameter to set (if any). 2028 * 2029 * @return A byte array containing the value of the parameter. 2030 * 2031 * @throws IllegalArgumentException if {@code parameter.length() != 2}. 2032 * @throws NullPointerException if {@code parameter == null}. 2033 * @throws TimeoutException if there is a timeout executing the parameter. 2034 * @throws XBeeException if {@code parameter} is not supported by the module or 2035 * if {@code parameterValue} is not supported or 2036 * if there is any other XBee related exception. 2037 * 2038 * @see #getParameter(String) 2039 * @see #executeParameter(String) 2040 * @see #setParameter(String, byte[]) 2041 */ 2042 private byte[] sendParameter(String parameter, byte[] parameterValue) throws TimeoutException, XBeeException { 2043 if (parameter == null) 2044 throw new NullPointerException("Parameter cannot be null."); 2045 if (parameter.length() != 2) 2046 throw new IllegalArgumentException("Parameter must contain exactly 2 characters."); 2047 2048 ATCommand atCommand = new ATCommand(parameter, parameterValue); 2049 2050 // Create and send the AT Command. 2051 ATCommandResponse response = null; 2052 try { 2053 response = sendATCommand(atCommand); 2054 } catch (IOException e) { 2055 throw new XBeeException("Error writing in the communication interface.", e); 2056 } 2057 2058 // Check if AT Command response is valid. 2059 checkATCommandResponseIsValid(response); 2060 2061 // Return the response value. 2062 return response.getResponse(); 2063 } 2064 2065 /* 2066 * (non-Javadoc) 2067 * @see java.lang.Object#toString() 2068 */ 2069 @Override 2070 public String toString() { 2071 return connectionInterface.toString(); 2072 } 2073 2074 /** 2075 * Enables or disables the 'apply configuration changes' option for this 2076 * device. 2077 * 2078 * <p>Enabling this option means that when any parameter of this XBee 2079 * device is set, it will be also applied.</p> 2080 * 2081 * <p>If this option is disabled, the method {@code applyChanges()} must be 2082 * used in order to apply the changes in all the parameters that were 2083 * previously set.</p> 2084 * 2085 * @param enabled {@code true} to apply configuration changes when an XBee 2086 * parameter is set, {@code false} otherwise. 2087 * 2088 * @see #applyChanges() 2089 * @see #isApplyConfigurationChangesEnabled() 2090 */ 2091 public void enableApplyConfigurationChanges(boolean enabled) { 2092 applyConfigurationChanges = enabled; 2093 } 2094 2095 /** 2096 * Returns whether the 'apply configuration changes' option is enabled in 2097 * this device. 2098 * 2099 * <p>If this option is enabled, when any parameter of this XBee device is 2100 * set, it will be also applied.</p> 2101 * 2102 * <p>If this option is disabled, the method {@code applyChanges()} must be 2103 * used in order to apply the changes in all the parameters that were 2104 * previously set.</p> 2105 * 2106 * @return {@code true} if the option is enabled, {@code false} otherwise. 2107 * 2108 * @see #applyChanges() 2109 * @see #enableApplyConfigurationChanges(boolean) 2110 */ 2111 public boolean isApplyConfigurationChangesEnabled() { 2112 return applyConfigurationChanges; 2113 } 2114 2115 /** 2116 * Configures the 16-bit address (network address) of this XBee device with 2117 * the provided one. 2118 * 2119 * @param xbee16BitAddress The new 16-bit address. 2120 * 2121 * @throws InterfaceNotOpenException if this device connection is not open. 2122 * @throws NullPointerException if {@code xbee16BitAddress == null}. 2123 * @throws TimeoutException if there is a timeout setting the address. 2124 * @throws XBeeException if there is any other XBee related exception. 2125 * 2126 * @see #get16BitAddress() 2127 * @see com.digi.xbee.api.models.XBee16BitAddress 2128 */ 2129 protected void set16BitAddress(XBee16BitAddress xbee16BitAddress) throws TimeoutException, XBeeException { 2130 if (xbee16BitAddress == null) 2131 throw new NullPointerException("16-bit address canot be null."); 2132 2133 setParameter("MY", xbee16BitAddress.getValue()); 2134 2135 this.xbee16BitAddress = xbee16BitAddress; 2136 } 2137 2138 /** 2139 * Returns the operating PAN ID (Personal Area Network Identifier) of 2140 * this XBee device. 2141 * 2142 * <p>For modules to communicate they must be configured with the same 2143 * identifier. Only modules with matching IDs can communicate with each 2144 * other.This parameter allows multiple networks to co-exist on the same 2145 * physical channel.</p> 2146 * 2147 * @return The operating PAN ID of this XBee device. 2148 * 2149 * @throws InterfaceNotOpenException if this device connection is not open. 2150 * @throws TimeoutException if there is a timeout getting the PAN ID. 2151 * @throws XBeeException if there is any other XBee related exception. 2152 * 2153 * @see #setPANID(byte[]) 2154 */ 2155 public byte[] getPANID() throws TimeoutException, XBeeException { 2156 switch (getXBeeProtocol()) { 2157 case ZIGBEE: 2158 return getParameter("OP"); 2159 default: 2160 return getParameter("ID"); 2161 } 2162 } 2163 2164 /** 2165 * Sets the PAN ID (Personal Area Network Identifier) of this XBee device. 2166 * 2167 * <p>For modules to communicate they must be configured with the same 2168 * identifier. Only modules with matching IDs can communicate with each 2169 * other.This parameter allows multiple networks to co-exist on the same 2170 * physical channel.</p> 2171 * 2172 * @param panID The new PAN ID of this XBee device. 2173 * 2174 * @throws IllegalArgumentException if {@code panID.length == 0} or 2175 * if {@code panID.length > 8}. 2176 * @throws InterfaceNotOpenException if this device connection is not open. 2177 * @throws NullPointerException if {@code panID == null}. 2178 * @throws TimeoutException if there is a timeout setting the PAN ID. 2179 * @throws XBeeException if there is any other XBee related exception. 2180 * 2181 * @see #getPANID() 2182 */ 2183 public void setPANID(byte[] panID) throws TimeoutException, XBeeException { 2184 if (panID == null) 2185 throw new NullPointerException("PAN ID cannot be null."); 2186 if (panID.length == 0) 2187 throw new IllegalArgumentException("Length of the PAN ID cannot be 0."); 2188 if (panID.length > 8) 2189 throw new IllegalArgumentException("Length of the PAN ID cannot be longer than 8 bytes."); 2190 2191 setParameter("ID", panID); 2192 } 2193 2194 /** 2195 * Returns the output power level at which this XBee device transmits 2196 * conducted power. 2197 * 2198 * @return The output power level of this XBee device. 2199 * 2200 * @throws InterfaceNotOpenException if this device connection is not open. 2201 * @throws TimeoutException if there is a timeout getting the power level. 2202 * @throws XBeeException if there is any other XBee related exception. 2203 * 2204 * @see #setPowerLevel(PowerLevel) 2205 * @see com.digi.xbee.api.models.PowerLevel 2206 */ 2207 public PowerLevel getPowerLevel() throws TimeoutException, XBeeException { 2208 byte[] powerLevelValue = getParameter("PL"); 2209 return PowerLevel.get(ByteUtils.byteArrayToInt(powerLevelValue)); 2210 } 2211 2212 /** 2213 * Sets the output power level at which this XBee device transmits 2214 * conducted power. 2215 * 2216 * @param powerLevel The new output power level to be set in this XBee 2217 * device. 2218 * 2219 * @throws InterfaceNotOpenException if this device connection is not open. 2220 * @throws NullPointerException if {@code powerLevel == null}. 2221 * @throws TimeoutException if there is a timeout setting the power level. 2222 * @throws XBeeException if there is any other XBee related exception. 2223 * 2224 * @see #getPowerLevel() 2225 * @see com.digi.xbee.api.models.PowerLevel 2226 */ 2227 public void setPowerLevel(PowerLevel powerLevel) throws TimeoutException, XBeeException { 2228 if (powerLevel == null) 2229 throw new NullPointerException("Power level cannot be null."); 2230 2231 setParameter("PL", ByteUtils.intToByteArray(powerLevel.getValue())); 2232 } 2233 2234 /** 2235 * Returns the current association status of this XBee device. 2236 * 2237 * <p>It indicates occurrences of errors during the last association 2238 * request.</p> 2239 * 2240 * @return The association indication status of the XBee device. 2241 * 2242 * @throws InterfaceNotOpenException if this device connection is not open. 2243 * @throws TimeoutException if there is a timeout getting the association 2244 * indication status. 2245 * @throws XBeeException if there is any other XBee related exception. 2246 * 2247 * @see #forceDisassociate() 2248 * @see com.digi.xbee.api.models.AssociationIndicationStatus 2249 */ 2250 protected AssociationIndicationStatus getAssociationIndicationStatus() throws TimeoutException, XBeeException { 2251 byte[] associationIndicationValue = getParameter("AI"); 2252 return AssociationIndicationStatus.get(ByteUtils.byteArrayToInt(associationIndicationValue)); 2253 } 2254 2255 /** 2256 * Forces this XBee device to immediately disassociate from the network and 2257 * re-attempt to associate. 2258 * 2259 * <p>Only valid for End Devices.</p> 2260 * 2261 * @throws InterfaceNotOpenException if this device connection is not open. 2262 * @throws TimeoutException if there is a timeout executing the 2263 * disassociation command. 2264 * @throws XBeeException if there is any other XBee related exception. 2265 * 2266 * @see #getAssociationIndicationStatus() 2267 */ 2268 protected void forceDisassociate() throws TimeoutException, XBeeException { 2269 executeParameter("DA"); 2270 } 2271 2272 /** 2273 * Writes configurable parameter values to the non-volatile memory of this 2274 * XBee device so that parameter modifications persist through subsequent 2275 * resets. 2276 * 2277 * <p>Parameters values remain in this device's memory until overwritten by 2278 * subsequent use of this method.</p> 2279 * 2280 * <p>If changes are made without writing them to non-volatile memory, the 2281 * module reverts back to previously saved parameters the next time the 2282 * module is powered-on.</p> 2283 * 2284 * <p>Writing the parameter modifications does not mean those values are 2285 * immediately applied, this depends on the status of the 'apply 2286 * configuration changes' option. Use method 2287 * {@code isApplyConfigurationChangesEnabled()} to get its status and 2288 * {@code enableApplyConfigurationChanges(boolean)} to enable/disable the 2289 * option. If it is disable method {@code applyChanges()} can be used in 2290 * order to manually apply the changes.</p> 2291 * 2292 * @throws InterfaceNotOpenException if this device connection is not open. 2293 * @throws TimeoutException if there is a timeout executing the write 2294 * changes command. 2295 * @throws XBeeException if there is any other XBee related exception. 2296 * 2297 * @see #applyChanges() 2298 * @see #enableApplyConfigurationChanges(boolean) 2299 * @see #isApplyConfigurationChangesEnabled() 2300 * @see #setParameter(String, byte[]) 2301 */ 2302 public void writeChanges() throws TimeoutException, XBeeException { 2303 executeParameter("WR"); 2304 } 2305}