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