Skip to content

Commit f0289f6

Browse files
committed
devices: read the actual operating mode when the device is in AT mode
- Completed the list of operating modes. - If the device cannot be discovered in API mode, the library checks if it is in AT mode by entering command mode. In some scenarios like a hardware reset or recovery mode, the module could be in API mode, but the library cannot find it and determines that it is in AT mode (raising an exception). To avoid that, the library now asks for the actual operating mode after entering command mode. - Added a new method to exit command mode. Signed-off-by: Diego Escalona <diego.escalona@digi.com>
1 parent 38d684b commit f0289f6

File tree

3 files changed

+109
-16
lines changed

3 files changed

+109
-16
lines changed

library/src/main/java/com/digi/xbee/api/AbstractXBeeDevice.java

Lines changed: 76 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import com.digi.xbee.api.connection.IConnectionInterface;
3131
import com.digi.xbee.api.connection.bluetooth.AbstractBluetoothInterface;
3232
import com.digi.xbee.api.connection.ConnectionType;
33+
import com.digi.xbee.api.connection.serial.AbstractSerialPort;
3334
import com.digi.xbee.api.connection.serial.SerialPortParameters;
3435
import com.digi.xbee.api.exceptions.ATCommandException;
3536
import com.digi.xbee.api.exceptions.BluetoothAuthenticationException;
@@ -115,6 +116,8 @@ public abstract class AbstractXBeeDevice {
115116
// Constants.
116117
private static String COMMAND_MODE_CHAR = "+";
117118
private static String COMMAND_MODE_OK = "OK\r";
119+
private static String COMMAND_MODE_EXIT = "ATCN\r";
120+
private static String COMMAND_MODE_AP = "ATAP\r";
118121

119122
private static int TIMEOUT_RESET = 5000;
120123
protected static int TIMEOUT_READ_PACKET = 3000;
@@ -151,7 +154,7 @@ public abstract class AbstractXBeeDevice {
151154
protected final static int TIMEOUT_BEFORE_COMMAND_MODE = 1200;
152155

153156
/**
154-
* Timeout to wait after entering in command mode: {@value} ms.
157+
* Timeout to wait after entering and exiting command mode: {@value} ms.
155158
*
156159
* <p>It is used to determine the operating mode of the module (this
157160
* library only supports API modes, not transparent mode).</p>
@@ -161,7 +164,7 @@ public abstract class AbstractXBeeDevice {
161164
*
162165
* @see XBeeDevice#determineOperatingMode()
163166
*/
164-
protected final static int TIMEOUT_ENTER_COMMAND_MODE = 1500;
167+
protected final static int DEFAULT_GUARD_TIME = 1500;
165168

166169
// Variables.
167170
protected IConnectionInterface connectionInterface;
@@ -3148,7 +3151,7 @@ protected void open() throws XBeeException {
31483151
if (operatingMode == OperatingMode.UNKNOWN) {
31493152
close();
31503153
throw new InvalidOperatingModeException("Could not determine operating mode.");
3151-
} else if (operatingMode == OperatingMode.AT) {
3154+
} else if (operatingMode != OperatingMode.API && operatingMode != OperatingMode.API_ESCAPE) {
31523155
close();
31533156
throw new InvalidOperatingModeException(operatingMode);
31543157
}
@@ -3212,16 +3215,23 @@ protected OperatingMode determineOperatingMode() throws OperationNotSupportedExc
32123215
// It is necessary to wait at least 1 second to enter in
32133216
// command mode after sending any data to the device.
32143217
Thread.sleep(TIMEOUT_BEFORE_COMMAND_MODE);
3215-
// Try to enter in AT command mode, if so the module is in AT mode.
3216-
boolean success = enterATCommandMode();
3217-
if (success)
3218-
return OperatingMode.AT;
3218+
// Try to enter in AT command mode and get the actual mode.
3219+
if (enterATCommandMode()) {
3220+
operatingMode = getActualMode();
3221+
logger.debug(toString() + "Using {}.", operatingMode.getName());
3222+
// Update the data reader with the correct mode.
3223+
dataReader.setXBeeReaderMode(operatingMode);
3224+
return operatingMode;
3225+
}
32193226
} catch (TimeoutException e1) {
32203227
logger.error(e1.getMessage(), e1);
32213228
} catch (InvalidOperatingModeException e1) {
32223229
logger.error(e1.getMessage(), e1);
32233230
} catch (InterruptedException e1) {
32243231
logger.error(e1.getMessage(), e1);
3232+
} finally {
3233+
// Exit AT command mode.
3234+
exitATCommandMode();
32253235
}
32263236
} catch (InvalidOperatingModeException e) {
32273237
logger.error("Invalid operating mode", e);
@@ -3246,7 +3256,7 @@ protected OperatingMode determineOperatingMode() throws OperationNotSupportedExc
32463256
private boolean enterATCommandMode() throws InvalidOperatingModeException, TimeoutException {
32473257
if (!connectionInterface.isOpen())
32483258
throw new InterfaceNotOpenException();
3249-
if (operatingMode != OperatingMode.AT)
3259+
if (operatingMode == OperatingMode.API || operatingMode == OperatingMode.API_ESCAPE)
32503260
throw new InvalidOperatingModeException("Invalid mode. Command mode can be only accessed while in AT mode.");
32513261

32523262
// Enter in AT command mode (send '+++'). The process waits 1,5 seconds for the 'OK\n'.
@@ -3258,7 +3268,7 @@ private boolean enterATCommandMode() throws InvalidOperatingModeException, Timeo
32583268
connectionInterface.writeData(COMMAND_MODE_CHAR.getBytes());
32593269

32603270
// Wait some time to let the module generate a response.
3261-
Thread.sleep(TIMEOUT_ENTER_COMMAND_MODE);
3271+
Thread.sleep(DEFAULT_GUARD_TIME);
32623272

32633273
// Read data from the device (it should answer with 'OK\r').
32643274
int readBytes = connectionInterface.readData(readData);
@@ -3272,14 +3282,68 @@ private boolean enterATCommandMode() throws InvalidOperatingModeException, Timeo
32723282

32733283
// Read data was 'OK\r'.
32743284
return true;
3275-
} catch (IOException e) {
3276-
logger.error(e.getMessage(), e);
3277-
} catch (InterruptedException e) {
3285+
} catch (IOException | InterruptedException e) {
32783286
logger.error(e.getMessage(), e);
32793287
}
32803288
return false;
32813289
}
32823290

3291+
/**
3292+
* Exits AT command mode. The XBee device has to be in command mode.
3293+
*
3294+
* @throws InterfaceNotOpenException if this device connection is not open.
3295+
*
3296+
* @since 1.3.1
3297+
*/
3298+
private void exitATCommandMode() {
3299+
if (!connectionInterface.isOpen())
3300+
throw new InterfaceNotOpenException();
3301+
3302+
try {
3303+
// Send the exit ('ATCN\r') command.
3304+
connectionInterface.writeData(COMMAND_MODE_EXIT.getBytes());
3305+
// Wait the guard time.
3306+
Thread.sleep(DEFAULT_GUARD_TIME);
3307+
} catch (IOException | InterruptedException e) {
3308+
logger.error(e.getMessage(), e);
3309+
}
3310+
}
3311+
3312+
/**
3313+
* Gets and returns the actual operating mode of the XBee device reading
3314+
* the {@code AP} parameter in AT command mode.
3315+
*
3316+
* @return The actual operating mode of the XBee device or
3317+
* {@code OperatingMode.UNKNOWN} if the mode could not be read.
3318+
*
3319+
* @throws InterfaceNotOpenException if this device connection is not open.
3320+
*
3321+
* @since 1.3.1
3322+
*/
3323+
private OperatingMode getActualMode() throws InvalidOperatingModeException {
3324+
if (!connectionInterface.isOpen())
3325+
throw new InterfaceNotOpenException();
3326+
3327+
byte[] readData = new byte[256];
3328+
try {
3329+
// Clear the serial input stream.
3330+
if (connectionInterface instanceof AbstractSerialPort)
3331+
((AbstractSerialPort)connectionInterface).purge();
3332+
// Send the 'AP' command.
3333+
connectionInterface.writeData(COMMAND_MODE_AP.getBytes());
3334+
Thread.sleep(100);
3335+
// Read the 'AP' answer.
3336+
int readBytes = connectionInterface.readData(readData);
3337+
if (readBytes == 0)
3338+
return OperatingMode.UNKNOWN;
3339+
// Return the corresponding operating mode for the AP answer.
3340+
return OperatingMode.get(Integer.parseInt(new String(readData).trim(), 16));
3341+
} catch (IOException | NumberFormatException | InterruptedException e) {
3342+
logger.error(e.getMessage(), e);
3343+
}
3344+
return OperatingMode.UNKNOWN;
3345+
}
3346+
32833347
/**
32843348
* Returns whether the connection interface associated to this device is
32853349
* already open.

library/src/main/java/com/digi/xbee/api/DataReader.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,7 @@ public void run() {
705705
if (connectionInterface.getInputStream() != null) {
706706
switch (mode) {
707707
case AT:
708+
default:
708709
break;
709710
case API:
710711
case API_ESCAPE:
@@ -719,8 +720,6 @@ public void run() {
719720
}
720721
}
721722
break;
722-
default:
723-
break;
724723
}
725724
} else if (connectionInterface.getInputStream() == null)
726725
break;

library/src/main/java/com/digi/xbee/api/models/OperatingMode.java

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
/**
2-
* Copyright 2017, Digi International Inc.
1+
/*
2+
* Copyright 2017-2019, Digi International Inc.
33
*
44
* This Source Code Form is subject to the terms of the Mozilla Public
55
* License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -15,6 +15,8 @@
1515
*/
1616
package com.digi.xbee.api.models;
1717

18+
import java.util.HashMap;
19+
1820
/**
1921
* Enumerates the different working modes of the XBee device. The operating
2022
* mode establishes the way a user communicates with an XBee device through
@@ -26,13 +28,24 @@ public enum OperatingMode {
2628
AT(0, "AT mode"),
2729
API(1, "API mode"),
2830
API_ESCAPE(2, "API mode with escaped characters"),
31+
/** @since 1.3.1 */
32+
MICROPYTHON(4, "MicroPython REPL"),
33+
/** @since 1.3.1 */
34+
BYPASS(5, "Bypass mode"),
2935
UNKNOWN(3, "Unknown");
3036

3137
// Variables
3238
private final int id;
3339

3440
private final String name;
3541

42+
private final static HashMap<Integer, OperatingMode> lookupTable = new HashMap<Integer, OperatingMode>();
43+
44+
static {
45+
for (OperatingMode operatingMode:values())
46+
lookupTable.put(operatingMode.getID(), operatingMode);
47+
}
48+
3649
/**
3750
* Class constructor. Instantiates a new {@code OperatingMode} enumeration
3851
* entry with the given parameters.
@@ -63,6 +76,23 @@ public String getName() {
6376
return name;
6477
}
6578

79+
/**
80+
* Returns the {@code OperatingMode} entry associated to the given value.
81+
*
82+
* @param value Value of the {@code OperatingMode} to retrieve.
83+
*
84+
* @return The {@code OperatingMode} entry associated to the given value,
85+
* {@code #UNKNOWN} if the value could not be found in the list.
86+
*
87+
* @since 1.3.1
88+
*/
89+
public static OperatingMode get(int value) {
90+
OperatingMode operatingMode = lookupTable.get(value);
91+
if (operatingMode != null)
92+
return operatingMode;
93+
return OperatingMode.UNKNOWN;
94+
}
95+
6696
/*
6797
* (non-Javadoc)
6898
* @see java.lang.Enum#toString()

0 commit comments

Comments
 (0)