Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: example-ublox-cellular-psm
UbloxCellularBase.cpp
- Committer:
- fahimalavi
- Date:
- 2019-08-29
- Revision:
- 31:b0a6a610d114
- Parent:
- 30:38230504a646
- Child:
- 32:1fc4850f1253
File content as of revision 31:b0a6a610d114:
/* Copyright (c) 2019 ublox Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "UARTSerial.h"
#include "APN_db.h"
#include "UbloxCellularBase.h"
#include "onboard_modem_api.h"
#ifdef FEATURE_COMMON_PAL
#include "mbed_trace.h"
#define TRACE_GROUP "UCB"
#else
#define tr_debug(format, ...) debug_if(_debug_trace_on, format "\n", ## __VA_ARGS__)
#define tr_info(format, ...) debug_if(_debug_trace_on, format "\n", ## __VA_ARGS__)
#define tr_warn(format, ...) debug_if(_debug_trace_on, format "\n", ## __VA_ARGS__)
#define tr_error(format, ...) debug_if(_debug_trace_on, format "\n", ## __VA_ARGS__)
#endif
/* Array to convert the 3G qual number into a median EC_NO_LEV number.
*/
/* 0 1 2 3 4 5 6 7 */
/* 44, 41, 35, 29, 23, 17, 11, 7*/
const int qualConvert3G[] = {-2, -4, -7, -10, -13, -16, -19, -21};
/* Array to convert the 3G "rssi" number into a dBm RSCP value rounded up to the
* nearest whole number.
*/
const int rscpConvert3G[] = {-108, -105, -103, -100, -98, -96, -94, -93, /* 0 - 7 */
-91, -89, -88, -85, -83, -80, -78, -76, /* 8 - 15 */
-74, -73, -70, -68, -66, -64, -63, -60, /* 16 - 23 */
-58, -56, -54, -53, -51, -49, -48, -46}; /* 24 - 31 */
/* Array to convert the LTE rssi number into a dBm value rounded up to the
* nearest whole number.
*/
const int rssiConvertLte[] = {-118, -115, -113, -110, -108, -105, -103, -100, /* 0 - 7 */
-98, -95, -93, -90, -88, -85, -83, -80, /* 8 - 15 */
-78, -76, -74, -73, -71, -69, -68, -65, /* 16 - 23 */
-63, -61, -60, -59, -58, -55, -53, -48}; /* 24 - 31 */
/**********************************************************************
* PRIVATE METHODS
**********************************************************************/
void UbloxCellularBase::set_nwk_reg_status_csd(int status)
{
switch (status) {
case CSD_NOT_REGISTERED_NOT_SEARCHING:
case CSD_NOT_REGISTERED_SEARCHING:
tr_info("Not (yet) registered for circuit switched service");
break;
case CSD_REGISTERED:
case CSD_REGISTERED_ROAMING:
tr_info("Registered for circuit switched service");
break;
case CSD_REGISTRATION_DENIED:
tr_info("Circuit switched service denied");
break;
case CSD_UNKNOWN_COVERAGE:
tr_info("Out of circuit switched service coverage");
break;
case CSD_SMS_ONLY:
tr_info("SMS service only");
break;
case CSD_SMS_ONLY_ROAMING:
tr_info("SMS service only");
break;
case CSD_CSFB_NOT_PREFERRED:
tr_info("Registered for circuit switched service with CSFB not preferred");
break;
default:
tr_info("Unknown circuit switched service registration status. %d", status);
break;
}
_dev_info.reg_status_csd = static_cast<NetworkRegistrationStatusCsd>(status);
}
void UbloxCellularBase::set_nwk_reg_status_psd(int status)
{
switch (status) {
case PSD_NOT_REGISTERED_NOT_SEARCHING:
case PSD_NOT_REGISTERED_SEARCHING:
tr_info("Not (yet) registered for packet switched service");
break;
case PSD_REGISTERED:
case PSD_REGISTERED_ROAMING:
tr_info("Registered for packet switched service");
break;
case PSD_REGISTRATION_DENIED:
tr_info("Packet switched service denied");
break;
case PSD_UNKNOWN_COVERAGE:
tr_info("Out of packet switched service coverage");
break;
case PSD_EMERGENCY_SERVICES_ONLY:
tr_info("Limited access for packet switched service. Emergency use only.");
break;
default:
tr_info("Unknown packet switched service registration status. %d", status);
break;
}
_dev_info.reg_status_psd = static_cast<NetworkRegistrationStatusPsd>(status);
}
void UbloxCellularBase::set_nwk_reg_status_eps(int status)
{
switch (status) {
case EPS_NOT_REGISTERED_NOT_SEARCHING:
case EPS_NOT_REGISTERED_SEARCHING:
tr_info("Not (yet) registered for EPS service");
break;
case EPS_REGISTERED:
case EPS_REGISTERED_ROAMING:
tr_info("Registered for EPS service");
break;
case EPS_REGISTRATION_DENIED:
tr_info("EPS service denied");
break;
case EPS_UNKNOWN_COVERAGE:
tr_info("Out of EPS service coverage");
break;
case EPS_EMERGENCY_SERVICES_ONLY:
tr_info("Limited access for EPS service. Emergency use only.");
break;
default:
tr_info("Unknown EPS service registration status. %d", status);
break;
}
_dev_info.reg_status_eps = static_cast<NetworkRegistrationStatusEps>(status);
}
#ifdef TARGET_UBLOX_C030_R412M
void UbloxCellularBase::set_modem_psm_state(int status)
{
switch (status) {
case ASLEEP:
tr_info("Modem is going in PSM sleep");
break;
case AWAKE:
tr_info("Modem is awake from PSM sleep");
break;
default:
tr_info("Unknown PSM state. %d", status);
break;
}
_dev_info.modem_psm_state = static_cast<ModemPSMState>(status);
}
#endif
void UbloxCellularBase::set_rat(int acTStatus)
{
switch (acTStatus) {
case GSM:
case COMPACT_GSM:
tr_info("Connected in GSM");
break;
case UTRAN:
tr_info("Connected to UTRAN");
break;
case EDGE:
tr_info("Connected to EDGE");
break;
case HSDPA:
tr_info("Connected to HSDPA");
break;
case HSUPA:
tr_info("Connected to HSPA");
break;
case HSDPA_HSUPA:
tr_info("Connected to HDPA/HSPA");
break;
case LTE:
tr_info("Connected to LTE");
break;
case EC_GSM_IoT:
tr_info("Connected to EC_GSM_IoT");
break;
case E_UTRAN_NB_S1:
tr_info("Connected to E_UTRAN NB1");
break;
default:
tr_info("Unknown RAT %d", acTStatus);
break;
}
_dev_info.rat = static_cast<RadioAccessNetworkType>(acTStatus);
}
bool UbloxCellularBase::get_iccid()
{
bool success;
LOCK();
MBED_ASSERT(_at != NULL);
// Returns the ICCID (Integrated Circuit Card ID) of the SIM-card.
// ICCID is a serial number identifying the SIM.
// AT Command Manual UBX-13002752, section 4.12
success = _at->send("AT+CCID") && _at->recv("+CCID: %20[^\n]\nOK\n", _dev_info.iccid);
tr_info("DevInfo: ICCID=%s", _dev_info.iccid);
UNLOCK();
return success;
}
bool UbloxCellularBase::get_imsi()
{
bool success;
LOCK();
MBED_ASSERT(_at != NULL);
// International mobile subscriber identification
// AT Command Manual UBX-13002752, section 4.11
success = _at->send("AT+CIMI") && _at->recv("%15[^\n]\nOK\n", _dev_info.imsi);
tr_info("DevInfo: IMSI=%s", _dev_info.imsi);
UNLOCK();
return success;
}
bool UbloxCellularBase::get_imei()
{
bool success;
LOCK();
MBED_ASSERT(_at != NULL);
// International mobile equipment identifier
// AT Command Manual UBX-13002752, section 4.7
success = _at->send("AT+CGSN") && _at->recv("%15[^\n]\nOK\n", _dev_info.imei);
tr_info("DevInfo: IMEI=%s", _dev_info.imei);
UNLOCK();
return success;
}
bool UbloxCellularBase::get_meid()
{
bool success;
LOCK();
MBED_ASSERT(_at != NULL);
// Mobile equipment identifier
// AT Command Manual UBX-13002752, section 4.8
success = _at->send("AT+GSN") && _at->recv("%18[^\n]\nOK\n", _dev_info.meid);
tr_info("DevInfo: MEID=%s", _dev_info.meid);
UNLOCK();
return success;
}
bool UbloxCellularBase::set_sms()
{
bool success = false;
char buf[32];
LOCK();
MBED_ASSERT(_at != NULL);
// Set up SMS format and enable URC
// AT Command Manual UBX-13002752, section 11
if (_at->send("AT+CMGF=1") && _at->recv("OK")) {
tr_debug("SMS in text mode");
if (_at->send("AT+CNMI=2,1") && _at->recv("OK")) {
tr_debug("SMS URC enabled");
// Set to CS preferred since PS preferred doesn't work
// on some networks
if (_at->send("AT+CGSMS=1") && _at->recv("OK")) {
tr_debug("SMS set to CS preferred");
success = true;
memset (buf, 0, sizeof (buf));
if (_at->send("AT+CSCA?") &&
_at->recv("+CSCA: \"%31[^\"]\"", buf) &&
_at->recv("OK")) {
tr_info("SMS Service Centre address is \"%s\"", buf);
}
}
}
}
UNLOCK();
return success;
}
void UbloxCellularBase::parser_abort_cb()
{
_at->abort();
}
// Callback for CME ERROR and CMS ERROR.
void UbloxCellularBase::CMX_ERROR_URC()
{
char buf[48];
if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
tr_debug("AT error %s", buf);
}
parser_abort_cb();
}
// Callback for circuit switched registration URC.
void UbloxCellularBase::CREG_URC()
{
char buf[10];
int status;
int acTStatus;
// If this is the URC it will be a single
// digit followed by \n. If it is the
// answer to a CREG query, it will be
// a ": %d,%d\n" where the second digit
// indicates the status
// Note: not calling _at->recv() from here as we're
// already in an _at->recv()
if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
if (sscanf(buf, ": %*d,%d,%*d,%*d,%d,", &status, &acTStatus) == 2) {
set_nwk_reg_status_csd(status);
set_rat(acTStatus);
} else if (sscanf(buf, ": %*d,%d", &status) == 1) {
set_nwk_reg_status_csd(status);
} else if (sscanf(buf, ": %d", &status) == 1) {
set_nwk_reg_status_csd(status);
}
}
}
// Callback for packet switched registration URC.
void UbloxCellularBase::CGREG_URC()
{
char buf[10];
int status;
int acTStatus;
// If this is the URC it will be a single
// digit followed by \n. If it is the
// answer to a CGREG query, it will be
// a ": %d,%d\n" where the second digit
// indicates the status
// Note: not calling _at->recv() from here as we're
// already in an _at->recv()
if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
if (sscanf(buf, ": %*d,%d,%*d,%*d,%d,", &status, &acTStatus) == 2) {
set_nwk_reg_status_csd(status);
set_rat(acTStatus);
} else if (sscanf(buf, ": %*d,%d", &status) == 1) {
set_nwk_reg_status_psd(status);
} else if (sscanf(buf, ": %d", &status) == 1) {
set_nwk_reg_status_psd(status);
}
}
}
// Callback for EPS registration URC.
void UbloxCellularBase::CEREG_URC()
{
char buf[10];
int status;
int acTStatus;
// If this is the URC it will be a single
// digit followed by \n. If it is the
// answer to a CEREG query, it will be
// a ": %d,%d\n" where the second digit
// indicates the status
// Note: not calling _at->recv() from here as we're
// already in an _at->recv()
if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
if (sscanf(buf, ": %*d,%d,%*d,%*d,%d,", &status, &acTStatus) == 2) {
set_nwk_reg_status_csd(status);
set_rat(acTStatus);
} else if (sscanf(buf, ": %*d,%d", &status) == 1) {
set_nwk_reg_status_eps(status);
} else if (sscanf(buf, ": %d", &status) == 1) {
set_nwk_reg_status_eps(status);
}
}
}
// Callback UMWI, just filtering it out.
void UbloxCellularBase::UMWI_URC()
{
char buf[10];
// Note: not calling _at->recv() from here as we're
// already in an _at->recv()
read_at_to_char(buf, sizeof (buf), '\n');
}
#ifdef TARGET_UBLOX_C030_R412M
// Callback UUPSMR, set/clear flag for modem psm state.
void UbloxCellularBase::UUPSMR_URC()
{
int status;
char buf[10];
if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
if (sscanf(buf, ": %d", &status) == 1) {
set_modem_psm_state(status);
//call application registered callbacks
if (status == AWAKE) { //modem coming out of sleep
if (_func_psm_coming_out) {
_func_psm_coming_out(_cb_param_psm_coming_out);
}
} else if(status == ASLEEP) { //modem going into sleep
if (_func_psm_going_in) {
_func_psm_going_in(_cb_param_psm_going_in);
}
}
}
}
}
#endif
/**********************************************************************
* PROTECTED METHODS
**********************************************************************/
#if MODEM_ON_BOARD
void UbloxCellularBase::modem_init()
{
::onboard_modem_init();
}
void UbloxCellularBase::modem_deinit()
{
::onboard_modem_deinit();
}
void UbloxCellularBase::modem_power_up()
{
::onboard_modem_power_up();
}
void UbloxCellularBase::modem_power_down()
{
::onboard_modem_power_down();
}
#else
void UbloxCellularBase::modem_init()
{
// Meant to be overridden
}
void UbloxCellularBase::modem_deinit()
{
// Meant to be overridden
}
void UbloxCellularBase::modem_power_up()
{
// Meant to be overridden
}
void UbloxCellularBase::modem_power_down()
{
// Mmeant to be overridden
}
#endif
// Constructor.
// Note: to allow this base class to be inherited as a virtual base class
// by everyone, it takes no parameters. See also comment above classInit()
// in the header file.
UbloxCellularBase::UbloxCellularBase()
{
_pin = NULL;
_at = NULL;
_at_timeout = AT_PARSER_TIMEOUT;
_fh = NULL;
_modem_initialised = false;
_sim_pin_check_enabled = false;
_debug_trace_on = false;
_dev_info.dev = DEV_TYPE_NONE;
_dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING;
_dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING;
_dev_info.reg_status_eps = EPS_NOT_REGISTERED_NOT_SEARCHING;
#ifdef TARGET_UBLOX_C030_R412M
_dev_info.modem_psm_state = AWAKE;
_psm_status = false;
_cb_param_psm_going_in = NULL;
_func_psm_going_in = NULL;
_cb_param_psm_coming_out = NULL;
_func_psm_coming_out = NULL;
#endif
#ifdef TARGET_UBLOX_C030_R41XM
_edrx_configured = false;
#endif
}
// Destructor.
UbloxCellularBase::~UbloxCellularBase()
{
deinit();
delete _at;
delete _fh;
}
// Initialise the portions of this class that are parameterised.
void UbloxCellularBase::baseClassInit(PinName tx, PinName rx,
int baud, bool debug_on)
{
// Only initialise ourselves if it's not already been done
if (_at == NULL) {
if (_debug_trace_on == false) {
_debug_trace_on = debug_on;
}
_baud = baud;
// Set up File Handle for buffered serial comms with cellular module
// (which will be used by the AT parser)
// Note: the UART is initialised to run no faster than 115200 because
// the modems cannot reliably auto-baud at faster rates. The faster
// rate is adopted later with a specific AT command and the
// UARTSerial rate is adjusted at that time
if (baud > 115200) {
baud = 115200;
}
_fh = new UARTSerial(tx, rx, baud);
// Set up the AT parser
#ifdef TARGET_UBLOX_C030_R41XM
_at = new UbloxATCmdParser(_fh, OUTPUT_ENTER_KEY, AT_PARSER_BUFFER_SIZE,
_at_timeout, _debug_trace_on);
#else
_at = new ATCmdParser(_fh, OUTPUT_ENTER_KEY, AT_PARSER_BUFFER_SIZE,
_at_timeout, _debug_trace_on);
#endif
// Error cases, out of band handling
_at->oob("ERROR", callback(this, &UbloxCellularBase::parser_abort_cb));
_at->oob("+CME ERROR", callback(this, &UbloxCellularBase::CMX_ERROR_URC));
_at->oob("+CMS ERROR", callback(this, &UbloxCellularBase::CMX_ERROR_URC));
// Registration status, out of band handling
_at->oob("+CREG", callback(this, &UbloxCellularBase::CREG_URC));
_at->oob("+CGREG", callback(this, &UbloxCellularBase::CGREG_URC));
_at->oob("+CEREG", callback(this, &UbloxCellularBase::CEREG_URC));
// Capture the UMWI, just to stop it getting in the way
_at->oob("+UMWI", callback(this, &UbloxCellularBase::UMWI_URC));
#ifdef TARGET_UBLOX_C030_R412M
// Handle PSM URC for going in and coming out of PSM
_at->oob("+UUPSMR", callback(this, &UbloxCellularBase::UUPSMR_URC));
#endif
}
}
// Set the AT parser timeout.
// Note: the AT interface should be locked before this is called.
void UbloxCellularBase::at_set_timeout(int timeout) {
MBED_ASSERT(_at != NULL);
_at_timeout = timeout;
_at->set_timeout(timeout);
}
// Read up to size bytes from the AT interface up to a "end".
// Note: the AT interface should be locked before this is called.
int UbloxCellularBase::read_at_to_char(char * buf, int size, char end)
{
int count = 0;
int x = 0;
if (size > 0) {
for (count = 0; (count < size) && (x >= 0) && (x != end); count++) {
x = _at->getc();
*(buf + count) = (char) x;
}
count--;
*(buf + count) = 0;
// Convert line endings:
// If end was '\n' (0x0a) and the preceding character was 0x0d, then
// overwrite that with null as well.
if ((count > 0) && (end == '\n') && (*(buf + count - 1) == '\x0d')) {
count--;
*(buf + count) = 0;
}
}
return count;
}
// Power up the modem.
// Enables the GPIO lines to the modem and then wriggles the power line in short pulses.
bool UbloxCellularBase::power_up()
{
bool success = false;
int at_timeout;
LOCK();
at_timeout = _at_timeout; // Has to be inside LOCK()s
MBED_ASSERT(_at != NULL);
/* Initialize GPIO lines */
tr_info("Powering up modem...");
modem_init();
/* Give modem a little time to settle down */
wait_ms(250);
for (int retry_count = 0; !success && (retry_count < 20); retry_count++) {
//In case of SARA-R4, modem takes a while to turn on, constantly toggling the power pin every ~2 secs causes the modem to never power up.
if ( (retry_count % 5) == 0) {
modem_power_up();
}
wait_ms(500);
// Modem tends to spit out noise during power up - don't confuse the parser
_at->flush();
at_set_timeout(1000);
if (_at->send("AT")) {
// C027 needs a delay here
wait_ms(100);
if (_at->recv("OK")) {
success = true;
}
}
at_set_timeout(at_timeout);
}
if (success) {
// Set the final baud rate
if (_at->send("AT+IPR=%d", _baud) && _at->recv("OK")) {
// Need to wait for things to be sorted out on the modem side
wait_ms(100);
((UARTSerial *)_fh)->set_baud(_baud);
}
// Turn off modem echoing and turn on verbose responses
success = _at->send("ATE0;+CMEE=2") && _at->recv("OK") &&
// The following commands are best sent separately
_at->send("AT&K0") && _at->recv("OK") && // Turn off RTC/CTS handshaking
_at->send("AT&C1") && _at->recv("OK") && // Set DCD circuit(109), changes in accordance with the carrier detect status
_at->send("AT&D0") && _at->recv("OK"); // Set DTR circuit, we ignore the state change of DTR
}
if (!success) {
tr_error("Preliminary modem setup failed.");
}
UNLOCK();
return success;
}
// Power down modem via AT interface.
void UbloxCellularBase::power_down()
{
LOCK();
MBED_ASSERT(_at != NULL);
// power-off modem
modem_power_down();
modem_deinit();
if (_modem_initialised && (_at != NULL)) {
int at_timeout = _at_timeout; // Save previous timeout
_at->set_timeout(1000);
// Check modem is powered off
if(_at->send("AT") && _at->recv("OK")) {
_at->send("AT+CPWROFF") && _at->recv("OK");
}
_at->set_timeout(at_timeout);
}
_dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING;
_dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING;
_dev_info.reg_status_eps = EPS_NOT_REGISTERED_NOT_SEARCHING;
UNLOCK();
}
// Get the device ID.
bool UbloxCellularBase::set_device_identity(DeviceType *dev)
{
char buf[20];
bool success;
LOCK();
MBED_ASSERT(_at != NULL);
success = _at->send("ATI") && _at->recv("%19[^\n]\nOK\n", buf);
if (success) {
if (strstr(buf, "SARA-G35"))
*dev = DEV_SARA_G35;
else if (strstr(buf, "LISA-U200-03S"))
*dev = DEV_LISA_U2_03S;
else if (strstr(buf, "LISA-U2"))
*dev = DEV_LISA_U2;
else if (strstr(buf, "SARA-U2"))
*dev = DEV_SARA_U2;
else if (strstr(buf, "SARA-R4"))
*dev = DEV_SARA_R4;
else if (strstr(buf, "LEON-G2"))
*dev = DEV_LEON_G2;
else if (strstr(buf, "TOBY-L2"))
*dev = DEV_TOBY_L2;
else if (strstr(buf, "MPCI-L2"))
*dev = DEV_MPCI_L2;
}
UNLOCK();
return success;
}
// Send initialisation AT commands that are specific to the device.
bool UbloxCellularBase::device_init(DeviceType dev)
{
bool success = false;
LOCK();
MBED_ASSERT(_at != NULL);
if ((dev == DEV_LISA_U2) || (dev == DEV_LEON_G2) || (dev == DEV_TOBY_L2)) {
success = _at->send("AT+UGPIOC=20,2") && _at->recv("OK");
} else if ((dev == DEV_SARA_U2) || (dev == DEV_SARA_G35)) {
success = _at->send("AT+UGPIOC=16,2") && _at->recv("OK");
} else {
success = true;
}
UNLOCK();
return success;
}
// Get the SIM card going.
bool UbloxCellularBase::initialise_sim_card()
{
bool success = false;
int retry_count = 0;
bool done = false;
LOCK();
MBED_ASSERT(_at != NULL);
/* SIM initialisation may take a significant amount, so an error is
* kind of expected. We should retry 10 times until we succeed or timeout. */
for (retry_count = 0; !done && (retry_count < 10); retry_count++) {
char pinstr[16];
if (_at->send("AT+CPIN?") && _at->recv("+CPIN: %15[^\n]\n", pinstr) &&
_at->recv("OK")) {
done = true;
if (strcmp(pinstr, "SIM PIN") == 0) {
_sim_pin_check_enabled = true;
if (_at->send("AT+CPIN=\"%s\"", _pin)) {
if (_at->recv("OK")) {
tr_info("PIN correct");
success = true;
} else {
tr_error("Incorrect PIN");
}
}
} else if (strcmp(pinstr, "READY") == 0) {
_sim_pin_check_enabled = false;
tr_info("No PIN required");
success = true;
} else {
tr_debug("Unexpected response from SIM: \"%s\"", pinstr);
}
}
/* wait for a second before retry */
wait_ms(1000);
}
if (done) {
tr_info("SIM Ready.");
} else {
tr_error("SIM not ready.");
}
UNLOCK();
return success;
}
/**********************************************************************
* PUBLIC METHODS
**********************************************************************/
// Initialise the modem.
bool UbloxCellularBase::init(const char *pin)
{
int x;
MBED_ASSERT(_at != NULL);
if (!_modem_initialised) {
if (power_up()) {
tr_info("Modem Ready.");
if (pin != NULL) {
_pin = pin;
}
#ifdef TARGET_UBLOX_C027
if (set_functionality_mode(FUNC_MIN)) {
#else
if (set_functionality_mode(FUNC_AIRPLANE)) {
#endif
if (initialise_sim_card()) {
int mno_profile;
#ifdef TARGET_UBLOX_C030_R412M
if (_psm_status == false) { //psm is not enabled by application yet so disable it at start-up
set_power_saving_mode(0, 0);
}
#endif
#ifdef TARGET_UBLOX_C030_R41XM
if (_at->is_idle_mode_enabled() == false) {
set_idle_mode(false); //disable idle mode at start up
}
if(_edrx_configured == false) {
// A special form of the command can be given as +CEDRXS=3.
// In this form, eDRX will be disabled and data for all parameters in the command +CEDRXS will be removed or,
// if available, set to the manufacturer specific default values.
set_receive_period(3,UbloxCellularBase::EDRXGSM_A_Gb_mode);
set_receive_period(3,UbloxCellularBase::EDRXEUTRAN_WB_S1_mode);
set_receive_period(3,UbloxCellularBase::EDRXEUTRAN_NB_S1_mode);
}
get_receive_period();
if (get_mno_profile(&mno_profile))
tr_info("Current MNO profile is: %d", mno_profile);
#endif
if (set_device_identity(&_dev_info.dev) && // Set up device identity
device_init(_dev_info.dev)) {// Initialise this device
// Get the integrated circuit ID of the SIM
if (get_iccid()) {
// Try a few times to get the IMSI (since on some modems this can
// take a while to be retrieved, especially if a SIM PIN
// was set)
for (x = 0; (x < 3) && !get_imsi(); x++) {
wait_ms(1000);
}
if (x < 3) { // If we got the IMSI, can get the others
if (get_imei() && // Get international mobile equipment identifier
get_meid() && // Probably the same as the IMEI
set_sms()) { // And set up SMS
// The modem is initialised.
_modem_initialised = true;
tr_info("Modem initialized");
}
}
}
}
}
}
}
}
return _modem_initialised;
}
// Perform registration.
bool UbloxCellularBase::nwk_registration()
{
bool atSuccess = false;
bool registered = false;
int status;
int at_timeout;
LOCK();
at_timeout = _at_timeout; // Has to be inside LOCK()s
MBED_ASSERT(_at != NULL);
if (!is_registered_psd() && !is_registered_csd() && !is_registered_eps()) {
if (set_functionality_mode(FUNC_FULL)) {
tr_info("Searching Network...");
// Enable the packet switched and network registration unsolicited result codes
if (_at->send("AT+CREG=1") && _at->recv("OK") &&
_at->send("AT+CGREG=1") && _at->recv("OK")) {
atSuccess = true;
if (_at->send("AT+CEREG=1")) {
_at->recv("OK");
// Don't check return value as this works for LTE only
}
if (atSuccess) {
// See if we are already in automatic mode
if (_at->send("AT+COPS?") && _at->recv("+COPS: %d", &status) &&
_at->recv("OK")) {
// If not, set it
if (status != 0) {
// Don't check return code here as there's not much
// we can do if this fails.
_at->send("AT+COPS=0") && _at->recv("OK");
}
}
// Query the registration status directly as well,
// just in case
if (_at->send("AT+CREG?") && _at->recv("OK")) {
// Answer will be processed by URC
}
if (_at->send("AT+CGREG?") && _at->recv("OK")) {
// Answer will be processed by URC
}
if (_at->send("AT+CEREG?")) {
_at->recv("OK");
// Don't check return value as this works for LTE only
}
}
}
// Wait for registration to succeed
at_set_timeout(1000);
for (int waitSeconds = 0; !registered && (waitSeconds < 180); waitSeconds++) {
registered = is_registered_psd() || is_registered_csd() || is_registered_eps();
_at->recv(UNNATURAL_STRING);
}
at_set_timeout(at_timeout);
if (registered) {
// This should return quickly but sometimes the status field is not returned
// so make the timeout short
at_set_timeout(1000);
if (_at->send("AT+COPS?") && _at->recv("+COPS: %*d,%*d,\"%*[^\"]\",%d\n", &status)) {
set_rat(status);
}
at_set_timeout(at_timeout);
}
}
} else {
registered = true;
}
UNLOCK();
return registered;
}
bool UbloxCellularBase::is_registered_csd()
{
return (_dev_info.reg_status_csd == CSD_REGISTERED) ||
(_dev_info.reg_status_csd == CSD_REGISTERED_ROAMING) ||
(_dev_info.reg_status_csd == CSD_CSFB_NOT_PREFERRED);
}
bool UbloxCellularBase::is_registered_psd()
{
return (_dev_info.reg_status_psd == PSD_REGISTERED) ||
(_dev_info.reg_status_psd == PSD_REGISTERED_ROAMING);
}
bool UbloxCellularBase::is_registered_eps()
{
return (_dev_info.reg_status_eps == EPS_REGISTERED) ||
(_dev_info.reg_status_eps == EPS_REGISTERED_ROAMING);
}
// Perform deregistration.
bool UbloxCellularBase::nwk_deregistration()
{
bool success = false;
LOCK();
MBED_ASSERT(_at != NULL);
int at_timeout = _at_timeout; // Has to be inside LOCK()s
at_set_timeout(3*60*1000); //command has 3 minutes timeout
if (_at->send("AT+COPS=2") && _at->recv("OK")) {
_dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING;
_dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING;
_dev_info.reg_status_eps = EPS_NOT_REGISTERED_NOT_SEARCHING;
success = true;
}
at_set_timeout(at_timeout);
UNLOCK();
return success;
}
// Put the modem into its lowest power state.
void UbloxCellularBase::deinit()
{
power_down();
_modem_initialised = false;
}
// Set the PIN.
void UbloxCellularBase::set_pin(const char *pin) {
_pin = pin;
}
// Enable or disable SIM pin checking.
bool UbloxCellularBase::sim_pin_check_enable(bool enableNotDisable)
{
bool success = false;;
LOCK();
MBED_ASSERT(_at != NULL);
if (_pin != NULL) {
if (_sim_pin_check_enabled && !enableNotDisable) {
// Disable the SIM lock
if (_at->send("AT+CLCK=\"SC\",0,\"%s\"", _pin) && _at->recv("OK")) {
_sim_pin_check_enabled = false;
success = true;
}
} else if (!_sim_pin_check_enabled && enableNotDisable) {
// Enable the SIM lock
if (_at->send("AT+CLCK=\"SC\",1,\"%s\"", _pin) && _at->recv("OK")) {
_sim_pin_check_enabled = true;
success = true;
}
} else {
success = true;
}
}
UNLOCK();
return success;
}
// Change the pin code for the SIM card.
bool UbloxCellularBase::change_sim_pin(const char *pin)
{
bool success = false;;
LOCK();
MBED_ASSERT(_at != NULL);
// Change the SIM pin
if ((pin != NULL) && (_pin != NULL)) {
if (_at->send("AT+CPWD=\"SC\",\"%s\",\"%s\"", _pin, pin) && _at->recv("OK")) {
_pin = pin;
success = true;
}
}
UNLOCK();
return success;
}
// Get the IMEI.
bool UbloxCellularBase::get_imei(char *imei_to_send, int size)
{
bool success;
LOCK();
MBED_ASSERT(_at != NULL);
// International mobile equipment identifier
// AT Command Manual UBX-13002752, section 4.7
success = _at->send("AT+CGSN") && _at->recv("%15[^\n]\nOK\n", _dev_info.imei);
tr_info("DevInfo: IMEI=%s", _dev_info.imei);
if (success) {
memcpy(imei_to_send,_dev_info.imei,size);
imei_to_send[size-1] = '\0';
}
UNLOCK();
return success;
}
// Get the IMEI of the module.
const char *UbloxCellularBase::imei()
{
return _dev_info.imei;
}
// Get the Mobile Equipment ID (which may be the same as the IMEI).
const char *UbloxCellularBase::meid()
{
return _dev_info.meid;
}
// Get the IMSI of the SIM.
const char *UbloxCellularBase::imsi()
{
// (try) to update the IMSI, just in case the SIM has changed
get_imsi();
return _dev_info.imsi;
}
// Get the ICCID of the SIM.
const char *UbloxCellularBase::iccid()
{
// (try) to update the ICCID, just in case the SIM has changed
get_iccid();
return _dev_info.iccid;
}
// Get the RSSI in dBm.
int UbloxCellularBase::rssi()
{
char buf[7] = {0};
int rssi = 0;
int qual = 0;
int rssiRet = 0;
bool success;
LOCK();
MBED_ASSERT(_at != NULL);
success = _at->send("AT+CSQ") && _at->recv("+CSQ: %6[^\n]\nOK\n", buf);
if (success) {
if (sscanf(buf, "%d,%d", &rssi, &qual) == 2) {
// AT+CSQ returns a coded RSSI value and an RxQual value
// For 2G an RSSI of 0 corresponds to -113 dBm or less,
// an RSSI of 31 corresponds to -51 dBm or less and hence
// each value is a 2 dB step.
// For LTE the mapping is defined in the array rssiConvertLte[].
// For 3G the mapping to RSCP is defined in the array rscpConvert3G[]
// and the RSSI value is then RSCP - the EC_NO_LEV number derived
// by putting the qual number through qualConvert3G[].
if ((rssi >= 0) && (rssi <= 31)) {
switch (_dev_info.rat) {
case UTRAN:
case HSDPA:
case HSUPA:
case HSDPA_HSUPA:
// 3G
if ((qual >= 0) && (qual <= 7)) {
qual = qualConvert3G[qual];
rssiRet = rscpConvert3G[rssi];
rssiRet -= qual;
}
break;
case LTE:
// LTE
rssiRet = rssiConvertLte[rssi];
break;
case GSM:
case COMPACT_GSM:
case EDGE:
default:
// GSM or assumed GSM if the RAT is not known
rssiRet = -(113 - (rssi << 2));
break;
}
}
}
}
UNLOCK();
return rssiRet;
}
//RAT should be set in a detached state (AT+COPS=2)
bool UbloxCellularBase::set_modem_rat(RAT selected_rat, RAT preferred_rat, RAT second_preferred_rat)
{
bool success = false;
char command[16] = {0x00};
//check if modem is registered with network
if (is_registered_csd() || is_registered_psd() || is_registered_eps()) {
tr_error("RAT should only be set in detached state");
return false;
}
if (preferred_rat != NOT_USED && second_preferred_rat != NOT_USED) {
sprintf(command, "AT+URAT=%d,%d,%d", selected_rat, preferred_rat, second_preferred_rat);
} else if (preferred_rat != NOT_USED) {
sprintf(command, "AT+URAT=%d,%d", selected_rat, preferred_rat);
} else if (second_preferred_rat != NOT_USED) {
sprintf(command, "AT+URAT=%d,%d", selected_rat, second_preferred_rat);
} else {
sprintf(command, "AT+URAT=%d", selected_rat);
}
LOCK();
if (_at->send(command) && _at->recv("OK")) {
success = true;
} else {
tr_error("unable to set the specified RAT");
success = false;
}
UNLOCK();
return success;
}
bool UbloxCellularBase::get_modem_rat(int *selected_rat, int *preferred_rat, int *second_preferred_rat)
{
bool success = false;
char buf[24] = {0x00};
if (selected_rat == NULL || preferred_rat == NULL || second_preferred_rat == NULL) {
tr_info("invalid pointers");
return false;
}
MBED_ASSERT(_at != NULL);
*selected_rat = NOT_USED;
*preferred_rat = NOT_USED;
*second_preferred_rat = NOT_USED;
LOCK();
if (_at->send("AT+URAT?") && _at->recv("%23[^\n]\nOK\n", buf)) {
if (sscanf(buf, "+URAT: %d,%d,%d", selected_rat, preferred_rat, second_preferred_rat) == 3) {
success = true;
} else if (sscanf(buf, "+URAT: %d,%d", selected_rat, preferred_rat) == 2) {
success = true;
} else if (sscanf(buf, "+URAT: %d", selected_rat) == 1) {
success = true;
}
}
UNLOCK();
return success;
}
//application should call init() or connect() in order to initialize the modem
bool UbloxCellularBase::reboot_modem()
{
return (set_functionality_mode(FUNC_RESET));
}
bool UbloxCellularBase::set_functionality_mode(FunctionalityMode mode)
{
bool return_val = false;
int at_timeout;
LOCK();
MBED_ASSERT(_at != NULL);
at_timeout = _at_timeout; // Has to be inside LOCK()s
at_set_timeout(3*60*1000); //command has 3 minutes timeout
if (_at->send("AT+CFUN=%d", mode) && _at->recv("OK")) {
return_val = true;
}
if (mode == FUNC_RESET || mode == FUNC_RESET_WITH_SIM) {
_modem_initialised = false;
}
at_set_timeout(at_timeout);
UNLOCK();
return return_val;
}
bool UbloxCellularBase::get_functionality_mode(int *mode)
{
bool return_val = false;
if (mode == NULL) {
return false;
}
LOCK();
MBED_ASSERT(_at != NULL);
if ( (_at->send("AT+CFUN?") && _at->recv("+CFUN: %d", mode) && _at->recv("OK")) ) {
return_val = true;
}
UNLOCK();
return return_val;
}
#ifdef TARGET_UBLOX_C030_R41XM
bool UbloxCellularBase::set_mno_profile(MNOProfile profile)
{
bool return_val = false;
int current_profile;
MNOProfile arr[MAX_NUM_PROFILES] = { SW_DEFAULT, SIM_ICCID, ATT, TMO, VODAFONE, DT, STANDARD_EU
#ifdef TARGET_UBLOX_C030_R410M
, VERIZON, TELSTRA, CT, SPRINT, TELUS
#endif
};
if (is_registered_csd() || is_registered_psd() || is_registered_eps()) {
tr_error("MNO profile should only be set in detached state");
return false;
}
if (get_mno_profile(¤t_profile)) {
if (current_profile == profile) { //Ref to UBX-18019856 7.1.7, parameters will be updated only if we switch to another profile first
for (uint8_t index = 0; index < MAX_NUM_PROFILES; index++) { //get the index of current profile and use the next one
if (arr[index] == current_profile) {
index = ((index + 1) % MAX_NUM_PROFILES);
current_profile = arr[index];
break;
}
}
LOCK();
if (_at->send("AT+UMNOPROF=%d", current_profile) && _at->recv("OK")) {
tr_error("temporary MNO profile set: %d", current_profile);
}
UNLOCK();
}
LOCK();
if (_at->send("AT+UMNOPROF=%d", profile) && _at->recv("OK")) {
return_val = true;
} else {
tr_error("unable to set user specified profile");
}
UNLOCK();
} else {
tr_error("could not read MNO profile");
}
return return_val;
}
bool UbloxCellularBase::get_mno_profile(int *profile)
{
bool return_val = false;
char buf[4] = {0x00};
if (profile == NULL) {
return false;
}
LOCK();
MBED_ASSERT(_at != NULL);
if (_at->send("AT+UMNOPROF?") && _at->recv("+UMNOPROF: %3[^\n]\nOK\n", buf)) {
*profile = atoi(buf);
return_val = true;
}
UNLOCK();
return return_val;
}
// Enable or Disable the UPSV power saving mode
bool UbloxCellularBase::set_idle_mode(bool enable)
{
#ifdef TARGET_UBLOX_C030_R412M
if (_psm_status == true && enable == true) {
return false;
}
#endif
bool success = false;
LOCK();
MBED_ASSERT(_at != NULL);
if (_at->send("AT+UPSV=%d", enable ? 4 : 0) && _at->recv("OK")) {
if (enable == true) {
_at->idle_mode_enabled();
}
else {
_at->idle_mode_disabled();
}
success = true;
}
UNLOCK();
return success;
}
bool UbloxCellularBase::get_idle_mode(int *status)
{
bool return_val = false;
if (status == NULL) {
return false;
}
LOCK();
MBED_ASSERT(_at != NULL);
if ( (_at->send("AT+UPSV?") && _at->recv("+UPSV: %d", status) && _at->recv("OK")) ) {
if (*status == 4) {
*status = 1;
}
return_val = true;
}
UNLOCK();
return return_val;
}
int UbloxCellularBase::set_receive_period(int mode, tEDRXAccessTechnology act_type, uint8_t edrx_value) {
char edrx[5];
uint_to_binary_str(edrx_value, edrx, 5, 4);
edrx[4] = '\0';
int status = 1;
LOCK();
if (_at->send("AT+CEDRXS=%d,%d,\"%s\"", mode, act_type, edrx) && _at->recv("OK")) {
_edrx_configured = true;
status = 0;
}
else {
status = 1;
}
UNLOCK();
return status;
}
int UbloxCellularBase::set_receive_period(int mode, tEDRXAccessTechnology act_type) {
int status = 1;
LOCK();
if (_at->send("AT+CEDRXS=%d,%d", mode, act_type) && _at->recv("OK")) {
status = 0;
}
else {
status = 1;
}
UNLOCK();
return status;
}
int UbloxCellularBase::set_receive_period(int mode) {
int status = 1;
LOCK();
if (_at->send("AT+CEDRXS=%d", mode) && _at->recv("OK")) {
status = 0;
}
else {
status = 1;
}
UNLOCK();
return status;
}
uint32_t UbloxCellularBase::get_receive_period() {
uint32_t edrx_value = 2;
char buf[24] = {0x00};
char edrx_val[5];
tEDRXAccessTechnology act_type;
LOCK();
if (_at->send("AT+CEDRXS?") && _at->recv("%23[^\n]\nOK\n", buf)) {
if (sscanf(buf, "+CEDRXS: %d,\"%s\"", (int *)&act_type, edrx_val) == 2) {
edrx_value = binary_str_to_uint(edrx_val,4);
}
}
if (_at->send("AT+CEDRXRDP") && _at->recv("OK")) {
}
tr_info("edrx_value. %d", edrx_value);
UNLOCK();
return edrx_value;
}
void UbloxCellularBase::uint_to_binary_str(uint32_t num, char* str, int str_size, int bit_cnt)
{
if (!str || str_size < bit_cnt) {
return;
}
int tmp, pos = 0;
for (int i = 31; i >= 0; i--) {
tmp = num >> i;
if (i < bit_cnt) {
if (tmp&1) {
str[pos] = 1 + '0';
} else {
str[pos] = 0 + '0';
}
pos++;
}
}
}
uint32_t UbloxCellularBase::binary_str_to_uint(const char *binary_string, int binary_string_length)
{
if (!binary_string || !binary_string_length) {
return 0;
}
int integer_output = 0, base_exp = 1;
for (int i = binary_string_length - 1; i >= 0; i--) {
if (binary_string[i] == '1') {
integer_output += (base_exp << (binary_string_length - (i+1)));
}
}
return integer_output;
}
bool UbloxCellularBase::set_band_bitmask(RAT rat, uint64_t bitmask) {
bool status = false;
UBandmaskRAT eBandMastRat;
if(rat == LTE_CATM1) {
eBandMastRat = UBANDMASK_RAT_LTE_CATM1;
}
else if(rat == LTE_CATNB1) {
eBandMastRat = UBANDMASK_RAT_LTE_CATNB1;
}
else {
tr_error("Invalid RAT for Band mask selection: %d", rat);
return false;
}
tr_info("UBANDMASK RAT %d, bitmask : %llu", eBandMastRat, bitmask);
LOCK();
if (_at->send("AT+UBANDMASK=%d,%llu", eBandMastRat, bitmask) && _at->recv("OK")) {
status = true;
}
UNLOCK();
return status;
}
bool UbloxCellularBase::get_band_bitmask(uint64_t *m1_bitmask, uint64_t *nb1_bitmask) {
bool status = false;
int eBandMastRat0, eBandMastRat1;
LOCK();
if(_at->send("AT+UBANDMASK?") && _at->recv("+UBANDMASK: %d,%llu,%d,%llu", &eBandMastRat0, m1_bitmask, &eBandMastRat1, nb1_bitmask) && _at->recv("OK")) {
status = true;
}
UNLOCK();
return status;
}
#endif //TARGET_UBLOX_C030_R41XM
#ifdef TARGET_UBLOX_C030_R412M
bool UbloxCellularBase::get_power_saving_mode(int *status, int *periodic_time, int *active_time)
{
char pt_encoded[8+1];// timer value encoded as 3GPP IE
char at_encoded[8+1];// timer value encoded as 3GPP IE
int value, multiplier;
bool return_val;
if (status == NULL || periodic_time == NULL || active_time == NULL) {
return false;
}
LOCK();
//+UCPSMS:1,,,"01000011","01000011"
if (_at->send("AT+UCPSMS?") && _at->recv("+UCPSMS:%d,,,\"%8c\",\"%8c\"\n", status, pt_encoded, at_encoded)) {
if (*status == true) {
//PSM is enabled, decode the timer values, periodic TAU first
value = (pt_encoded[7]- '0');
value += (pt_encoded[6]- '0') << 1;
value += (pt_encoded[5]- '0') << 2;
value += (pt_encoded[4]- '0') << 3;
value += (pt_encoded[3]- '0') << 4;
multiplier = (pt_encoded[2]- '0');
multiplier += (pt_encoded[1]- '0') << 1;
multiplier += (pt_encoded[0]- '0') << 2;
switch(multiplier) {
//10 minutes
case 0:
value = value*10*60;
break;
//1 hour
case 1:
value = value*60*60;
break;
//10 hours
case 2:
value = value*10*60*60;
break;
//2 seconds
case 3:
value = value*2;
break;
//30 seconds
case 4:
value = value*30;
break;
//1 minute
case 5:
value = value*60;
break;
//320 hours
case 6:
value = value*320*60*60;
break;
default:
value = 0;
break;
}
*periodic_time = value;
//decode the active time
value = (at_encoded[7]- '0');
value += (at_encoded[6]- '0') << 1;
value += (at_encoded[5]- '0') << 2;
value += (at_encoded[4]- '0') << 3;
value += (at_encoded[3]- '0') << 4;
multiplier = (at_encoded[2]- '0');
multiplier += (at_encoded[1]- '0') << 1;
multiplier += (at_encoded[0]- '0') << 2;
switch(multiplier) {
//2 seconds
case 0:
value = value*2;
break;
//1 minute
case 1:
value = value*60;
break;
//decihours (6minutes)
case 2:
value = value*6*60;
break;
default:
value = 0;
break;
}
*active_time = value;
}
return_val = true;
} else {
return_val = false;
}
UNLOCK();
return return_val;
}
bool UbloxCellularBase::set_power_saving_mode(int periodic_time, int active_time)
{
if (_at->is_idle_mode_enabled() == true && periodic_time != 0 && active_time != 0 ) {
return false;
}
bool return_val = false;
LOCK();
int at_timeout = _at_timeout;
at_set_timeout(10000); //AT+CPSMS has response time of < 10s
//check if modem supports PSM URCs
if (_at->send("AT+UPSMR?") && _at->recv("OK")) {
if (periodic_time == 0 && active_time == 0) {
// disable PSM
if (_at->send("AT+CPSMS=0") && _at->recv("OK")) {
if (_at->send("AT+UPSMR=0") && _at->recv("OK")) {//disable the URC
//de-register the callback
detach_cb_psm_going_in();
detach_cb_psm_coming_out();
_psm_status = false;
return_val = true;
}
}
} else { //PSM string encoding code borrowed from AT_CellularPower.cpp
/**
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, 5);
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, 5);
at[8] = '\0';
if (_at->send("AT+CPSMS=1,,,\"%s\",\"%s\"", pt, at) && _at->recv("OK")) {
if (_at->send("AT+UPSMR=1") && _at->recv("OK")) {//enable the PSM URC
tr_info("PSM enabled successfully!");
_psm_status = true;
return_val = true;
} else {
tr_error("PSM URCs not supported");
return_val = false;
}
} else {
tr_error("+CPSMS command failed");
return_val = false;
}
}
} else {
tr_error("PSM URCs not supported by this version of modem");
}
at_set_timeout(at_timeout);
UNLOCK();
return return_val;
}
bool UbloxCellularBase::is_modem_awake()
{
return (_dev_info.modem_psm_state == AWAKE);
}
//application should call init() or connect() in order to initialize the modem
void UbloxCellularBase::wakeup_modem()
{
LOCK();
MBED_ASSERT(_at != NULL);
tr_info("Waking up modem...");
modem_power_up();
_dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING;
_dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING;
_dev_info.reg_status_eps = EPS_NOT_REGISTERED_NOT_SEARCHING;
_modem_initialised = false;
UNLOCK();
}
#endif
// End of File