diff --git a/UNITTESTS/features/cellular/framework/AT/at_cellulardevice/at_cellulardevicetest.cpp b/UNITTESTS/features/cellular/framework/AT/at_cellulardevice/at_cellulardevicetest.cpp index 6c14c61c58..17f1807e16 100644 --- a/UNITTESTS/features/cellular/framework/AT/at_cellulardevice/at_cellulardevicetest.cpp +++ b/UNITTESTS/features/cellular/framework/AT/at_cellulardevice/at_cellulardevicetest.cpp @@ -232,6 +232,34 @@ TEST_F(TestAT_CellularDevice, test_AT_CellularDevice_modem_debug_on) dev.close_sms(); } +TEST_F(TestAT_CellularDevice, test_AT_CellularDevice_set_power_save_mode) +{ + EventQueue que; + FileHandle_stub fh1; + ATHandler at(&fh1, que, 0, ","); + + AT_CellularDevice dev(&fh1); + ATHandler_stub::nsapi_error_value = NSAPI_ERROR_OK; + EXPECT_TRUE(NSAPI_ERROR_OK == dev.set_power_save_mode(0, 0)); + + EXPECT_TRUE(NSAPI_ERROR_OK == dev.set_power_save_mode(10, 0)); + + EXPECT_TRUE(NSAPI_ERROR_OK == dev.set_power_save_mode(912, 0)); + + EXPECT_TRUE(NSAPI_ERROR_OK == dev.set_power_save_mode(1834, 1834)); + + EXPECT_TRUE(NSAPI_ERROR_OK == dev.set_power_save_mode(18345, 18345)); + + EXPECT_TRUE(NSAPI_ERROR_OK == dev.set_power_save_mode(101234, 101234)); + + EXPECT_TRUE(NSAPI_ERROR_OK == dev.set_power_save_mode(1012345, 1012345)); + + EXPECT_TRUE(NSAPI_ERROR_OK == dev.set_power_save_mode(39612345, 39612345)); + + ATHandler_stub::nsapi_error_value = NSAPI_ERROR_DEVICE_ERROR; + EXPECT_TRUE(NSAPI_ERROR_DEVICE_ERROR == dev.set_power_save_mode(0)); +} + TEST_F(TestAT_CellularDevice, test_AT_CellularDevice_get_send_delay) { FileHandle_stub fh1; diff --git a/UNITTESTS/features/cellular/framework/AT/at_cellularpower/at_cellularpowertest.cpp b/UNITTESTS/features/cellular/framework/AT/at_cellularpower/at_cellularpowertest.cpp index ac4d9cf1b4..35c05f5541 100644 --- a/UNITTESTS/features/cellular/framework/AT/at_cellularpower/at_cellularpowertest.cpp +++ b/UNITTESTS/features/cellular/framework/AT/at_cellularpower/at_cellularpowertest.cpp @@ -123,34 +123,6 @@ TEST_F(TestAT_CellularPower, test_AT_CellularPower_reset) EXPECT_TRUE(NSAPI_ERROR_DEVICE_ERROR == pow.reset()); } -TEST_F(TestAT_CellularPower, test_AT_CellularPower_opt_power_save_mode) -{ - EventQueue que; - FileHandle_stub fh1; - ATHandler at(&fh1, que, 0, ","); - - AT_CellularPower pow(at); - ATHandler_stub::nsapi_error_value = NSAPI_ERROR_OK; - EXPECT_TRUE(NSAPI_ERROR_OK == pow.opt_power_save_mode(0, 0)); - - EXPECT_TRUE(NSAPI_ERROR_OK == pow.opt_power_save_mode(10, 0)); - - EXPECT_TRUE(NSAPI_ERROR_OK == pow.opt_power_save_mode(912, 0)); - - EXPECT_TRUE(NSAPI_ERROR_OK == pow.opt_power_save_mode(1834, 1834)); - - EXPECT_TRUE(NSAPI_ERROR_OK == pow.opt_power_save_mode(18345, 18345)); - - EXPECT_TRUE(NSAPI_ERROR_OK == pow.opt_power_save_mode(101234, 101234)); - - EXPECT_TRUE(NSAPI_ERROR_OK == pow.opt_power_save_mode(1012345, 1012345)); - - EXPECT_TRUE(NSAPI_ERROR_OK == pow.opt_power_save_mode(39612345, 39612345)); - - ATHandler_stub::nsapi_error_value = NSAPI_ERROR_DEVICE_ERROR; - EXPECT_TRUE(NSAPI_ERROR_DEVICE_ERROR == pow.opt_power_save_mode(0, 0)); -} - TEST_F(TestAT_CellularPower, test_AT_CellularPower_is_device_ready) { EventQueue que; diff --git a/UNITTESTS/stubs/AT_CellularDevice_stub.cpp b/UNITTESTS/stubs/AT_CellularDevice_stub.cpp index 169929df30..4d2f7363a5 100644 --- a/UNITTESTS/stubs/AT_CellularDevice_stub.cpp +++ b/UNITTESTS/stubs/AT_CellularDevice_stub.cpp @@ -151,6 +151,11 @@ void AT_CellularDevice::modem_debug_on(bool on) } +nsapi_error_t AT_CellularDevice::set_power_save_mode(int periodic_time, int active_time) +{ + return NSAPI_ERROR_OK; +} + nsapi_error_t AT_CellularDevice::init_module() { return NSAPI_ERROR_OK; diff --git a/UNITTESTS/stubs/AT_CellularPower_stub.cpp b/UNITTESTS/stubs/AT_CellularPower_stub.cpp index 4b2a0f0ecd..d5a383456e 100644 --- a/UNITTESTS/stubs/AT_CellularPower_stub.cpp +++ b/UNITTESTS/stubs/AT_CellularPower_stub.cpp @@ -55,11 +55,6 @@ nsapi_error_t AT_CellularPower::reset() return NSAPI_ERROR_OK; } -nsapi_error_t AT_CellularPower::opt_power_save_mode(int periodic_time, int active_time) -{ - return NSAPI_ERROR_OK; -} - nsapi_error_t AT_CellularPower::set_device_ready_urc_cb(mbed::Callback callback) { return NSAPI_ERROR_OK; diff --git a/UNITTESTS/target_h/myCellularDevice.h b/UNITTESTS/target_h/myCellularDevice.h index 7190f8f6f9..1e5e4c8078 100644 --- a/UNITTESTS/target_h/myCellularDevice.h +++ b/UNITTESTS/target_h/myCellularDevice.h @@ -109,6 +109,11 @@ public: virtual void modem_debug_on(bool on) {} + nsapi_error_t set_power_save_mode(int periodic_time, int active_time) + { + return NSAPI_ERROR_OK; + } + virtual nsapi_error_t init_module() { return 0; diff --git a/features/cellular/TESTS/api/cellular_power/main.cpp b/features/cellular/TESTS/api/cellular_power/main.cpp index fb32d1144b..cb7b0378a0 100644 --- a/features/cellular/TESTS/api/cellular_power/main.cpp +++ b/features/cellular/TESTS/api/cellular_power/main.cpp @@ -87,16 +87,6 @@ static void test_power_interface() TEST_ASSERT(err == NSAPI_ERROR_OK); wait_for_power(pwr); - wait(1); - err = pwr->opt_power_save_mode(0, 0); - TEST_ASSERT(err == NSAPI_ERROR_OK || err == NSAPI_ERROR_DEVICE_ERROR); - if (err == NSAPI_ERROR_DEVICE_ERROR) { - if (!(strcmp(devi, "TELIT_HE910") == 0 || strcmp(devi, "QUECTEL_BG96") == 0)) { // TELIT_HE910 and QUECTEL_BG96 just gives an error and no specific error number so we can't know is this real error or that modem/network does not support the command - TEST_ASSERT(((AT_CellularPower *)pwr)->get_device_error().errCode == 100 && // 100 == unknown command for modem - ((AT_CellularPower *)pwr)->get_device_error().errType == 3); // 3 == CME error from the modem - } - } - err = pwr->off(); TEST_ASSERT(err == NSAPI_ERROR_OK || err == NSAPI_ERROR_UNSUPPORTED); diff --git a/features/cellular/framework/API/CellularDevice.h b/features/cellular/framework/API/CellularDevice.h index 12ddb7315a..d79a250e6b 100644 --- a/features/cellular/framework/API/CellularDevice.h +++ b/features/cellular/framework/API/CellularDevice.h @@ -18,6 +18,7 @@ #ifndef CELLULAR_DEVICE_H_ #define CELLULAR_DEVICE_H_ +#include "CellularUtil.h" #include "CellularTargets.h" #include "CellularStateMachine.h" #include "Callback.h" @@ -249,6 +250,18 @@ public: */ virtual void modem_debug_on(bool on) = 0; + /** Set power save mode + * + * @remark See 3GPP TS 27.007 PSM for details + * + * @param periodic_time in seconds to enable power save, or zero to disable + * @param active_time in seconds to wait before entering power save mode + * + * @return NSAPI_ERROR_OK on success + * NSAPI_ERROR_DEVICE_ERROR on failure + */ + virtual nsapi_error_t set_power_save_mode(int periodic_time, int active_time = 0) = 0; + /** Initialize cellular module must be called right after module is ready. * For example, when multiple modules are supported in a single AT driver this function detects * and adapts to an actual module at runtime. diff --git a/features/cellular/framework/API/CellularPower.h b/features/cellular/framework/API/CellularPower.h index 881a934232..61246df5ed 100644 --- a/features/cellular/framework/API/CellularPower.h +++ b/features/cellular/framework/API/CellularPower.h @@ -93,18 +93,6 @@ public: */ virtual nsapi_error_t reset() = 0; - /** Opt for power save setting on cellular device. If both parameters are zero, this disables PSM. - * - * @remark See 3GPP TS 27.007 PSM for details - * - * @param periodic_time Timeout in seconds IoT subsystem is not expecting messaging - * @param active_time Timeout in seconds IoT subsystem waits for response - * - * @return NSAPI_ERROR_OK on success - * NSAPI_ERROR_DEVICE_ERROR on failure - */ - virtual nsapi_error_t opt_power_save_mode(int periodic_time, int active_time) = 0; - /** Check whether the device is ready to accept commands. * * @return NSAPI_ERROR_OK on success diff --git a/features/cellular/framework/AT/AT_CellularDevice.cpp b/features/cellular/framework/AT/AT_CellularDevice.cpp index 8d8b3de1a4..1c987cc200 100644 --- a/features/cellular/framework/AT/AT_CellularDevice.cpp +++ b/features/cellular/framework/AT/AT_CellularDevice.cpp @@ -27,6 +27,7 @@ #include "UARTSerial.h" #include "FileHandle.h" +using namespace mbed_cellular_util; using namespace events; using namespace mbed; @@ -365,6 +366,134 @@ void AT_CellularDevice::modem_debug_on(bool on) ATHandler::set_debug_list(_modem_debug_on); } +nsapi_error_t AT_CellularDevice::set_power_save_mode(int periodic_time, int active_time) +{ + _at->lock(); + + if (periodic_time == 0 && active_time == 0) { + // disable PSM + _at->cmd_start("AT+CPSMS="); + _at->write_int(0); + _at->cmd_stop_read_resp(); + } else { + const int PSMTimerBits = 5; + + /** + Table 10.5.163a/3GPP TS 24.008: GPRS Timer 3 information element + + Bits 5 to 1 represent the binary coded timer value. + + Bits 6 to 8 defines the timer value unit for the GPRS timer as follows: + 8 7 6 + 0 0 0 value is incremented in multiples of 10 minutes + 0 0 1 value is incremented in multiples of 1 hour + 0 1 0 value is incremented in multiples of 10 hours + 0 1 1 value is incremented in multiples of 2 seconds + 1 0 0 value is incremented in multiples of 30 seconds + 1 0 1 value is incremented in multiples of 1 minute + 1 1 0 value is incremented in multiples of 320 hours (NOTE 1) + 1 1 1 value indicates that the timer is deactivated (NOTE 2). + */ + char pt[8 + 1]; // timer value encoded as 3GPP IE + const int ie_value_max = 0x1f; + uint32_t periodic_timer = 0; + if (periodic_time <= 2 * ie_value_max) { // multiples of 2 seconds + periodic_timer = periodic_time / 2; + strcpy(pt, "01100000"); + } else { + if (periodic_time <= 30 * ie_value_max) { // multiples of 30 seconds + periodic_timer = periodic_time / 30; + strcpy(pt, "10000000"); + } else { + if (periodic_time <= 60 * ie_value_max) { // multiples of 1 minute + periodic_timer = periodic_time / 60; + strcpy(pt, "10100000"); + } else { + if (periodic_time <= 10 * 60 * ie_value_max) { // multiples of 10 minutes + periodic_timer = periodic_time / (10 * 60); + strcpy(pt, "00000000"); + } else { + if (periodic_time <= 60 * 60 * ie_value_max) { // multiples of 1 hour + periodic_timer = periodic_time / (60 * 60); + strcpy(pt, "00100000"); + } else { + if (periodic_time <= 10 * 60 * 60 * ie_value_max) { // multiples of 10 hours + periodic_timer = periodic_time / (10 * 60 * 60); + strcpy(pt, "01000000"); + } else { // multiples of 320 hours + int t = periodic_time / (320 * 60 * 60); + if (t > ie_value_max) { + t = ie_value_max; + } + periodic_timer = t; + strcpy(pt, "11000000"); + } + } + } + } + } + } + + uint_to_binary_str(periodic_timer, &pt[3], sizeof(pt) - 3, PSMTimerBits); + pt[8] = '\0'; + + /** + Table 10.5.172/3GPP TS 24.008: GPRS Timer information element + + Bits 5 to 1 represent the binary coded timer value. + + Bits 6 to 8 defines the timer value unit for the GPRS timer as follows: + + 8 7 6 + 0 0 0 value is incremented in multiples of 2 seconds + 0 0 1 value is incremented in multiples of 1 minute + 0 1 0 value is incremented in multiples of decihours + 1 1 1 value indicates that the timer is deactivated. + + Other values shall be interpreted as multiples of 1 minute in this version of the protocol. + */ + char at[8 + 1]; + uint32_t active_timer; // timer value encoded as 3GPP IE + if (active_time <= 2 * ie_value_max) { // multiples of 2 seconds + active_timer = active_time / 2; + strcpy(at, "00000000"); + } else { + if (active_time <= 60 * ie_value_max) { // multiples of 1 minute + active_timer = (1 << 5) | (active_time / 60); + strcpy(at, "00100000"); + } else { // multiples of decihours + int t = active_time / (6 * 60); + if (t > ie_value_max) { + t = ie_value_max; + } + active_timer = t; + strcpy(at, "01000000"); + } + } + + uint_to_binary_str(active_timer, &at[3], sizeof(at) - 3, PSMTimerBits); + at[8] = '\0'; + + // request for both GPRS and LTE + _at->cmd_start("AT+CPSMS="); + _at->write_int(1); + _at->write_string(pt); + _at->write_string(at); + _at->write_string(pt); + _at->write_string(at); + _at->cmd_stop_read_resp(); + + if (_at->get_last_error() != NSAPI_ERROR_OK) { + tr_warn("Power save mode not enabled!"); + } else { + // network may not agree with power save options but + // that should be fine as timeout is not longer than requested + } + } + + return _at->unlock_return_error(); +} + nsapi_error_t AT_CellularDevice::init_module() { #if MBED_CONF_MBED_TRACE_ENABLE diff --git a/features/cellular/framework/AT/AT_CellularDevice.h b/features/cellular/framework/AT/AT_CellularDevice.h index 2068417dba..90808303c7 100644 --- a/features/cellular/framework/AT/AT_CellularDevice.h +++ b/features/cellular/framework/AT/AT_CellularDevice.h @@ -70,6 +70,8 @@ public: virtual void modem_debug_on(bool on); + virtual nsapi_error_t set_power_save_mode(int periodic_time, int active_time = 0); + virtual nsapi_error_t init_module(); virtual ATHandler *get_at_handler(FileHandle *fh); diff --git a/features/cellular/framework/AT/AT_CellularPower.cpp b/features/cellular/framework/AT/AT_CellularPower.cpp index 152723e21e..5889b1e900 100644 --- a/features/cellular/framework/AT/AT_CellularPower.cpp +++ b/features/cellular/framework/AT/AT_CellularPower.cpp @@ -21,8 +21,6 @@ #include "CellularTargets.h" #include "nsapi_types.h" -static const int PSMTimerBits = 5; - using namespace mbed_cellular_util; using namespace mbed; @@ -76,132 +74,6 @@ nsapi_error_t AT_CellularPower::reset() return _at.unlock_return_error(); } -nsapi_error_t AT_CellularPower::opt_power_save_mode(int periodic_time, int active_time) -{ - _at.lock(); - - if (periodic_time == 0 && active_time == 0) { - // disable PSM - _at.cmd_start("AT+CPSMS="); - _at.write_int(0); - _at.cmd_stop_read_resp(); - } else { - /** - Table 10.5.163a/3GPP TS 24.008: GPRS Timer 3 information element - - Bits 5 to 1 represent the binary coded timer value. - - Bits 6 to 8 defines the timer value unit for the GPRS timer as follows: - 8 7 6 - 0 0 0 value is incremented in multiples of 10 minutes - 0 0 1 value is incremented in multiples of 1 hour - 0 1 0 value is incremented in multiples of 10 hours - 0 1 1 value is incremented in multiples of 2 seconds - 1 0 0 value is incremented in multiples of 30 seconds - 1 0 1 value is incremented in multiples of 1 minute - 1 1 0 value is incremented in multiples of 320 hours (NOTE 1) - 1 1 1 value indicates that the timer is deactivated (NOTE 2). - */ - char pt[8 + 1]; // timer value encoded as 3GPP IE - const int ie_value_max = 0x1f; - uint32_t periodic_timer = 0; - if (periodic_time <= 2 * ie_value_max) { // multiples of 2 seconds - periodic_timer = periodic_time / 2; - strcpy(pt, "01100000"); - } else { - if (periodic_time <= 30 * ie_value_max) { // multiples of 30 seconds - periodic_timer = periodic_time / 30; - strcpy(pt, "10000000"); - } else { - if (periodic_time <= 60 * ie_value_max) { // multiples of 1 minute - periodic_timer = periodic_time / 60; - strcpy(pt, "10100000"); - } else { - if (periodic_time <= 10 * 60 * ie_value_max) { // multiples of 10 minutes - periodic_timer = periodic_time / (10 * 60); - strcpy(pt, "00000000"); - } else { - if (periodic_time <= 60 * 60 * ie_value_max) { // multiples of 1 hour - periodic_timer = periodic_time / (60 * 60); - strcpy(pt, "00100000"); - } else { - if (periodic_time <= 10 * 60 * 60 * ie_value_max) { // multiples of 10 hours - periodic_timer = periodic_time / (10 * 60 * 60); - strcpy(pt, "01000000"); - } else { // multiples of 320 hours - int t = periodic_time / (320 * 60 * 60); - if (t > ie_value_max) { - t = ie_value_max; - } - periodic_timer = t; - strcpy(pt, "11000000"); - } - } - } - } - } - } - - uint_to_binary_str(periodic_timer, &pt[3], sizeof(pt) - 3, PSMTimerBits); - pt[8] = '\0'; - - /** - Table 10.5.172/3GPP TS 24.008: GPRS Timer information element - - Bits 5 to 1 represent the binary coded timer value. - - Bits 6 to 8 defines the timer value unit for the GPRS timer as follows: - - 8 7 6 - 0 0 0 value is incremented in multiples of 2 seconds - 0 0 1 value is incremented in multiples of 1 minute - 0 1 0 value is incremented in multiples of decihours - 1 1 1 value indicates that the timer is deactivated. - - Other values shall be interpreted as multiples of 1 minute in this version of the protocol. - */ - char at[8 + 1]; - uint32_t active_timer; // timer value encoded as 3GPP IE - if (active_time <= 2 * ie_value_max) { // multiples of 2 seconds - active_timer = active_time / 2; - strcpy(at, "00000000"); - } else { - if (active_time <= 60 * ie_value_max) { // multiples of 1 minute - active_timer = (1 << 5) | (active_time / 60); - strcpy(at, "00100000"); - } else { // multiples of decihours - int t = active_time / (6 * 60); - if (t > ie_value_max) { - t = ie_value_max; - } - active_timer = t; - strcpy(at, "01000000"); - } - } - - uint_to_binary_str(active_timer, &at[3], sizeof(at) - 3, PSMTimerBits); - at[8] = '\0'; - - // request for both GPRS and LTE - _at.cmd_start("AT+CPSMS="); - _at.write_int(1); - _at.write_string(pt); - _at.write_string(at); - _at.write_string(pt); - _at.write_string(at); - _at.cmd_stop_read_resp(); - - if (_at.get_last_error() != NSAPI_ERROR_OK) { - tr_warn("Power save mode not enabled!"); - } else { - // network may not agree with power save options but - // that should be fine as timeout is not longer than requested - } - } - - return _at.unlock_return_error(); -} - nsapi_error_t AT_CellularPower::is_device_ready() { _at.lock(); diff --git a/features/cellular/framework/AT/AT_CellularPower.h b/features/cellular/framework/AT/AT_CellularPower.h index 6a4cc77e8c..bc34586934 100644 --- a/features/cellular/framework/AT/AT_CellularPower.h +++ b/features/cellular/framework/AT/AT_CellularPower.h @@ -44,8 +44,6 @@ public: virtual nsapi_error_t reset(); - virtual nsapi_error_t opt_power_save_mode(int periodic_time, int active_time); - virtual nsapi_error_t is_device_ready(); virtual nsapi_error_t set_device_ready_urc_cb(mbed::Callback callback);