mirror of https://github.com/ARMmbed/mbed-os.git
A Reference CellularInterface driver
* Implements CellularInterface * Reference design for CellularInterface implementations * Uses an external mbed-os IP stack and talks to modem over PPP.pull/4119/head
parent
ccbf00571f
commit
1afc7bfbef
|
@ -0,0 +1,778 @@
|
|||
/* Copyright (c) 2017 ARM 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 "ReferenceCellularDriver.h"
|
||||
|
||||
#include <string.h>
|
||||
#include "modem_api.h"
|
||||
#include "platform/BufferedSerial.h"
|
||||
#include "nsapi_ppp.h"
|
||||
#if MBED_CONF_REF_CELL_DRV_APN_LOOKUP
|
||||
#include "APN_db.h"
|
||||
#endif //MBED_CONF_REF_CELL_DRV_APN_LOOKUP
|
||||
#if defined(FEATURE_COMMON_PAL)
|
||||
#include "mbed_trace.h"
|
||||
#define TRACE_GROUP "UCID"
|
||||
#else
|
||||
#define tr_debug(...) (void(0)) //dummies if feature common pal is not added
|
||||
#define tr_info(...) (void(0)) //dummies if feature common pal is not added
|
||||
#define tr_error(...) (void(0)) //dummies if feature common pal is not added
|
||||
#endif //defined(FEATURE_COMMON_PAL)
|
||||
|
||||
#define BAUD_RATE 115200
|
||||
|
||||
/**
|
||||
* PDP (packet data profile) Context
|
||||
*/
|
||||
#define CTX "1"
|
||||
|
||||
/**
|
||||
* Output Enter sequence for the modem , default CR
|
||||
*/
|
||||
#define OUTPUT_ENTER_KEY "\r"
|
||||
|
||||
#if MBED_CONF_REF_CELL_DRV_AT_PARSER_BUFFER_SIZE
|
||||
#define AT_PARSER_BUFFER_SIZE MBED_CONF_REF_CELL_DRV_AT_PARSER_BUFFER_SIZE //bytes
|
||||
#else
|
||||
#define AT_PARSER_BUFFER_SIZE 256 //bytes
|
||||
#endif //MMBED_CONF_REF_CELL_DRV_AT_PARSER_BUFFER_SIZE
|
||||
|
||||
#if MBED_CONF_REF_CELL_DRV_AT_PARSER_TIMEOUT
|
||||
#define AT_PARSER_TIMEOUT MBED_CONF_REF_CELL_DRV_AT_PARSER_TIMEOUT
|
||||
#else
|
||||
#define AT_PARSER_TIMEOUT 8*1000 //miliseconds
|
||||
#endif //MBED_CONF_REF_CELL_DRV_AT_PARSER_TIMEOUT
|
||||
|
||||
static bool initialized;
|
||||
static bool set_credentials_api_used;
|
||||
static bool set_sim_pin_check_request;
|
||||
static bool change_pin;
|
||||
static device_info dev_info;
|
||||
static modem_t mdm_object;
|
||||
|
||||
static void parser_abort(ATParser *at)
|
||||
{
|
||||
at->abort();
|
||||
}
|
||||
|
||||
static bool get_CCID(ATParser *at)
|
||||
{
|
||||
// Returns the ICCID (Integrated Circuit Card ID) of the SIM-card.
|
||||
// ICCID is a serial number identifying the SIM.
|
||||
bool success = at->send("AT+CCID") && at->recv("+CCID: %20[^\n]\nOK\n", dev_info.ccid);
|
||||
tr_debug("DevInfo: CCID=%s", dev_info.ccid);
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool get_IMSI(ATParser *at)
|
||||
{
|
||||
// International mobile subscriber identification
|
||||
bool success = at->send("AT+CIMI") && at->recv("%15[^\n]\nOK\n", dev_info.imsi);
|
||||
tr_debug("DevInfo: IMSI=%s", dev_info.imsi);
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool get_IMEI(ATParser *at)
|
||||
{
|
||||
// International mobile equipment identifier
|
||||
bool success = at->send("AT+CGSN") && at->recv("%15[^\n]\nOK\n", dev_info.imei);
|
||||
tr_debug("DevInfo: IMEI=%s", dev_info.imei);
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool get_MEID(ATParser *at)
|
||||
{
|
||||
// Mobile equipment identifier
|
||||
bool success = at->send("AT+GSN")
|
||||
&& at->recv("%18[^\n]\nOK\n", dev_info.meid);
|
||||
tr_debug("DevInfo: MEID=%s", dev_info.meid);
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool set_CMGF(ATParser *at)
|
||||
{
|
||||
// Preferred message format
|
||||
// set AT+CMGF=[mode] , 0 PDU mode, 1 text mode
|
||||
bool success = at->send("AT+CMGF=1") && at->recv("OK");
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool set_CNMI(ATParser *at)
|
||||
{
|
||||
// New SMS indication configuration
|
||||
// set AT+CMTI=[mode, index] , 0 PDU mode, 1 text mode
|
||||
// Multiple URCs for SMS, i.e., CMT, CMTI, UCMT, CBMI, CDSI as DTE could be following any of these SMS formats
|
||||
bool success = at->send("AT+CNMI=2,"CTX) && at->recv("OK");
|
||||
return success;
|
||||
}
|
||||
|
||||
static void CMTI_URC(ATParser *at)
|
||||
{
|
||||
// our CMGF = 1, i.e., text mode. So we expect response in this format:
|
||||
//+CMTI: <mem>,<index>,
|
||||
at->recv(": %*u,%*u");
|
||||
tr_info("New SMS received");
|
||||
|
||||
}
|
||||
|
||||
static void CMT_URC(ATParser *at)
|
||||
{
|
||||
// our CMGF = 1, i.e., text mode. So we expect response in this format:
|
||||
//+CMT: <oa>,[<alpha>],<scts>[,<tooa>,
|
||||
//<fo>,<pid>,<dcs>,<sca>,<tosca>,
|
||||
//<length>]<CR><LF><data>
|
||||
// By default detailed SMS header CSDH=0 , so we are not expecting [,<tooa>,
|
||||
//<fo>,<pid>,<dcs>,<sca>,<tosca>
|
||||
char sms[50];
|
||||
char service_timestamp[15];
|
||||
at->recv(": %49[^\"]\",,%14[^\"]\"\n", sms, service_timestamp);
|
||||
|
||||
tr_info("SMS:%s, %s", service_timestamp, sms);
|
||||
|
||||
}
|
||||
|
||||
static bool set_atd(ATParser *at)
|
||||
{
|
||||
bool success = at->send("ATD*99***"CTX"#") && at->recv("CONNECT");
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables SIM pin check lock
|
||||
*/
|
||||
static nsapi_error_t do_sim_pin_check(ATParser *at, const char *pin)
|
||||
{
|
||||
bool success;
|
||||
if (set_sim_pin_check_request) {
|
||||
/* use the SIM locked */
|
||||
success = at->send("AT+CLCK=\"SC\",1,\"%s\"", pin) && at->recv("OK");
|
||||
} else {
|
||||
/* use the SIM unlocked */
|
||||
success = at->send("AT+CLCK=\"SC\",0,\"%s\"",pin) && at->recv("OK");
|
||||
}
|
||||
|
||||
if (success) return NSAPI_ERROR_OK;
|
||||
|
||||
return NSAPI_ERROR_AUTH_FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the pin code for the SIM card
|
||||
*/
|
||||
static nsapi_error_t do_change_sim_pin(ATParser *at, const char *old_pin, const char *new_pin)
|
||||
{
|
||||
/* changes the SIM pin */
|
||||
bool success = at->send("AT+CPWD=\"SC\",\"%s\",\"%s\"", old_pin, new_pin) && at->recv("OK");
|
||||
if (success) {
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
|
||||
return NSAPI_ERROR_AUTH_FAILURE;
|
||||
}
|
||||
|
||||
static void set_nwk_reg_status_csd(unsigned int status)
|
||||
{
|
||||
switch (status) {
|
||||
case CSD_NOT_REGISTERED_NOT_SEARCHING:
|
||||
case CSD_NOT_REGISTERED_SEARCHING:
|
||||
break;
|
||||
case CSD_REGISTERED:
|
||||
case CSD_REGISTERED_ROAMING:
|
||||
tr_debug("Registered for circuit switched service");
|
||||
break;
|
||||
case CSD_REGISTRATION_DENIED:
|
||||
tr_debug("Circuit switched service denied");
|
||||
break;
|
||||
case CSD_UNKNOWN_COVERAGE:
|
||||
tr_debug("Out of circuit switched service coverage");
|
||||
break;
|
||||
case CSD_SMS_ONLY:
|
||||
tr_debug("SMS service only");
|
||||
break;
|
||||
case CSD_SMS_ONLY_ROAMING:
|
||||
tr_debug("SMS service only");
|
||||
break;
|
||||
case CSD_CSFB_NOT_PREFERRED:
|
||||
tr_debug("Registered for circuit switched service with CSFB not preferred");
|
||||
break;
|
||||
default:
|
||||
tr_debug("Unknown circuit switched service registration status. %u", status);
|
||||
break;
|
||||
}
|
||||
|
||||
dev_info.reg_status_csd = static_cast<nwk_registration_status_csd>(status);
|
||||
}
|
||||
|
||||
static void set_nwk_reg_status_psd(unsigned int status)
|
||||
{
|
||||
switch (status) {
|
||||
case PSD_NOT_REGISTERED_NOT_SEARCHING:
|
||||
case PSD_NOT_REGISTERED_SEARCHING:
|
||||
break;
|
||||
case PSD_REGISTERED:
|
||||
case PSD_REGISTERED_ROAMING:
|
||||
tr_debug("Registered for packet switched service");
|
||||
break;
|
||||
case PSD_REGISTRATION_DENIED:
|
||||
tr_debug("Packet switched service denied");
|
||||
break;
|
||||
case PSD_UNKNOWN_COVERAGE:
|
||||
tr_debug("Out of packet switched service coverage");
|
||||
break;
|
||||
case PSD_EMERGENCY_SERVICES_ONLY:
|
||||
tr_debug("Limited access for packet switched service. Emergency use only.");
|
||||
break;
|
||||
default:
|
||||
tr_debug("Unknown packet switched service registration status. %u", status);
|
||||
break;
|
||||
}
|
||||
|
||||
dev_info.reg_status_psd = static_cast<nwk_registration_status_psd>(status);
|
||||
}
|
||||
|
||||
static bool 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);
|
||||
}
|
||||
|
||||
static bool is_registered_psd()
|
||||
{
|
||||
return (dev_info.reg_status_psd == PSD_REGISTERED) ||
|
||||
(dev_info.reg_status_psd == PSD_REGISTERED_ROAMING);
|
||||
}
|
||||
|
||||
ReferenceCellularDriver::ReferenceCellularDriver(PinName tx, PinName rx, int baud, bool debugOn)
|
||||
{
|
||||
_new_pin = NULL;
|
||||
_pin = NULL;
|
||||
_at = NULL;
|
||||
_dcd = NULL;
|
||||
_apn = "internet";
|
||||
_uname = NULL;
|
||||
_pwd = NULL;
|
||||
_debug_trace_on = false;
|
||||
|
||||
// Set up File Handle
|
||||
_fh = new BufferedSerial(tx, rx, BAUD_RATE);
|
||||
|
||||
if (debugOn) {
|
||||
_debug_trace_on = true;
|
||||
}
|
||||
|
||||
dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING;
|
||||
dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING;
|
||||
dev_info.ppp_connection_up = false;
|
||||
|
||||
}
|
||||
|
||||
ReferenceCellularDriver::~ReferenceCellularDriver()
|
||||
{
|
||||
delete _fh;
|
||||
delete _at;
|
||||
delete _dcd;
|
||||
}
|
||||
|
||||
void ReferenceCellularDriver::modem_debug_on(bool on)
|
||||
{
|
||||
_debug_trace_on = on;
|
||||
}
|
||||
|
||||
void ReferenceCellularDriver::connection_status_cb(Callback<void(nsapi_error_t)> cb)
|
||||
{
|
||||
_connection_status_cb = cb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public API. Sets up the flag for the driver to enable or disable SIM pin check
|
||||
* at the next boot.
|
||||
*/
|
||||
void ReferenceCellularDriver::set_sim_pin_check(bool check)
|
||||
{
|
||||
set_sim_pin_check_request = check;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public API. Sets up the flag for the driver to change pin code for SIM card
|
||||
*/
|
||||
void ReferenceCellularDriver::set_new_sim_pin(const char *new_pin)
|
||||
{
|
||||
change_pin = true;
|
||||
_new_pin = new_pin;
|
||||
}
|
||||
|
||||
bool ReferenceCellularDriver::nwk_registration(uint8_t nwk_type)
|
||||
{
|
||||
bool success = false;
|
||||
bool registered = false;
|
||||
|
||||
char str[35];
|
||||
int retcode;
|
||||
int retry_counter = 0;
|
||||
unsigned int reg_status;
|
||||
|
||||
success = nwk_type == PACKET_SWITCHED ?
|
||||
_at->send("AT+CGREG=0") :
|
||||
_at->send("AT+CREG=0") && _at->recv("OK\n");
|
||||
|
||||
success = _at->send("AT+COPS=0") //initiate auto-registration
|
||||
&& _at->recv("OK");
|
||||
if (!success) {
|
||||
tr_error("Modem not responding.");
|
||||
return false;
|
||||
}
|
||||
|
||||
//Network search
|
||||
//If not registered after 60 attempts, i.e., 30 seconds wait, give up
|
||||
tr_debug("Searching Network ...");
|
||||
|
||||
while (!registered) {
|
||||
|
||||
if (retry_counter > 60) {
|
||||
success = false;
|
||||
goto give_up;
|
||||
}
|
||||
|
||||
success = nwk_type == PACKET_SWITCHED ?
|
||||
_at->send("AT+CGREG?")
|
||||
&& _at->recv("+CGREG: %34[^\n]\n", str)
|
||||
&& _at->recv("OK\n") :
|
||||
_at->send("AT+CREG?")
|
||||
&& _at->recv("+CREG: %34[^\n]\n", str)
|
||||
&& _at->recv("OK\n");
|
||||
|
||||
retcode = sscanf(str, "%*u,%u", ®_status);
|
||||
|
||||
if (retcode >= 1) {
|
||||
if (nwk_type == PACKET_SWITCHED) {
|
||||
set_nwk_reg_status_psd(reg_status);
|
||||
if (is_registered_psd()) {
|
||||
registered = true;
|
||||
}
|
||||
} else if (nwk_type == CIRCUIT_SWITCHED) {
|
||||
set_nwk_reg_status_csd(reg_status);
|
||||
if (is_registered_csd()) {
|
||||
registered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (registered) {
|
||||
break;
|
||||
} else {
|
||||
wait_ms(500);
|
||||
}
|
||||
|
||||
retry_counter++;
|
||||
}
|
||||
|
||||
give_up:
|
||||
return registered;
|
||||
}
|
||||
|
||||
bool ReferenceCellularDriver::is_connected()
|
||||
{
|
||||
return dev_info.ppp_connection_up;
|
||||
}
|
||||
|
||||
// Get the SIM card going.
|
||||
nsapi_error_t ReferenceCellularDriver::initialize_sim_card()
|
||||
{
|
||||
nsapi_error_t nsapi_error = NSAPI_ERROR_AUTH_FAILURE;
|
||||
int retry_count = 0;
|
||||
bool done = false;
|
||||
|
||||
/* SIM initialization 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]\nOK\n", pinstr)) {
|
||||
if (strcmp(pinstr, "SIM PIN") == 0) {
|
||||
if (!_at->send("AT+CPIN=\"%s\"", _pin) || !_at->recv("OK")) {
|
||||
tr_debug("PIN correct");
|
||||
nsapi_error = NSAPI_ERROR_OK;
|
||||
}
|
||||
} else if (strcmp(pinstr, "READY") == 0) {
|
||||
tr_debug("SIM Ready");
|
||||
nsapi_error = NSAPI_ERROR_OK;
|
||||
done = true;
|
||||
} else {
|
||||
tr_debug("Unexpected response from SIM: \"%s\"", pinstr);
|
||||
}
|
||||
}
|
||||
|
||||
/* wait for a second before retry */
|
||||
wait_ms(1000);
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
tr_error("SIM not ready.");
|
||||
}
|
||||
|
||||
return nsapi_error;
|
||||
}
|
||||
|
||||
void ReferenceCellularDriver::set_sim_pin(const char *pin) {
|
||||
/* overwrite the default pin by user provided pin */
|
||||
_pin = pin;
|
||||
}
|
||||
|
||||
nsapi_error_t ReferenceCellularDriver::setup_context_and_credentials()
|
||||
{
|
||||
bool success;
|
||||
|
||||
if (!_apn) {
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
|
||||
bool try_ipv6 = false;
|
||||
const char *auth = _uname && _pwd ? "CHAP:" : "";
|
||||
|
||||
retry_without_ipv6:
|
||||
success = _at->send("AT"
|
||||
"+FCLASS=0;" // set to connection (ATD) to data mode
|
||||
"+CGDCONT="CTX",\"%s\",\"%s%s\"",
|
||||
try_ipv6 ? "IPV4V6" : "IP", auth, _apn
|
||||
)
|
||||
&& _at->recv("OK");
|
||||
|
||||
if (!success && try_ipv6) {
|
||||
try_ipv6 = false;
|
||||
goto retry_without_ipv6;
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
_at->recv("OK");
|
||||
}
|
||||
|
||||
return success ? NSAPI_ERROR_OK : NSAPI_ERROR_PARAMETER;
|
||||
|
||||
}
|
||||
|
||||
void ReferenceCellularDriver::set_credentials(const char *apn, const char *uname,
|
||||
const char *pwd)
|
||||
{
|
||||
_apn = apn;
|
||||
_uname = uname;
|
||||
_pwd = pwd;
|
||||
set_credentials_api_used = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ReferenceCellularDriver::setup_at_parser()
|
||||
{
|
||||
if (_at) {
|
||||
return;
|
||||
}
|
||||
|
||||
_at = new ATParser(_fh, OUTPUT_ENTER_KEY, AT_PARSER_BUFFER_SIZE, AT_PARSER_TIMEOUT,
|
||||
_debug_trace_on ? true : false);
|
||||
|
||||
/* Error cases, out of band handling */
|
||||
_at->oob("ERROR", callback(parser_abort, _at));
|
||||
_at->oob("+CME ERROR", callback(parser_abort, _at));
|
||||
_at->oob("+CMS ERROR", callback(parser_abort, _at));
|
||||
_at->oob("NO CARRIER", callback(parser_abort, _at));
|
||||
|
||||
/* URCs, handled out of band */
|
||||
_at->oob("+CMT", callback(CMT_URC, _at));
|
||||
_at->oob("+CMTI", callback(CMTI_URC, _at));
|
||||
}
|
||||
|
||||
void ReferenceCellularDriver::shutdown_at_parser()
|
||||
{
|
||||
delete _at;
|
||||
_at = NULL;
|
||||
}
|
||||
|
||||
nsapi_error_t ReferenceCellularDriver::connect(const char *sim_pin, const char *apn, const char *uname, const char *pwd)
|
||||
{
|
||||
if (!sim_pin) {
|
||||
return NSAPI_ERROR_PARAMETER;
|
||||
}
|
||||
|
||||
if (apn) {
|
||||
_apn = apn;
|
||||
}
|
||||
|
||||
if (uname && pwd) {
|
||||
_uname = uname;
|
||||
_pwd = pwd;
|
||||
} else {
|
||||
_uname = NULL;
|
||||
_pwd = NULL;
|
||||
}
|
||||
|
||||
_pin = sim_pin;
|
||||
|
||||
return connect();
|
||||
}
|
||||
|
||||
nsapi_error_t ReferenceCellularDriver::connect()
|
||||
{
|
||||
nsapi_error_t retcode;
|
||||
bool success;
|
||||
bool did_init = false;
|
||||
|
||||
if (dev_info.ppp_connection_up) {
|
||||
return NSAPI_ERROR_IS_CONNECTED;
|
||||
}
|
||||
|
||||
#if MBED_CONF_REF_CELL_DRV_APN_LOOKUP && !set_credentials_api_used
|
||||
const char *apn_config;
|
||||
do {
|
||||
#endif
|
||||
|
||||
retry_init:
|
||||
|
||||
/* setup AT parser */
|
||||
setup_at_parser();
|
||||
|
||||
if (!initialized) {
|
||||
|
||||
/* If we are using serial interface, we want to make sure that DCD line is
|
||||
* not connected as long as we are using ATParser.
|
||||
* As soon as we get into data mode, we would like to attach an interrupt line
|
||||
* to DCD line.
|
||||
* Here, we detach the line */
|
||||
BufferedSerial *serial = static_cast<BufferedSerial *>(_fh);
|
||||
serial->set_data_carrier_detect(NC);
|
||||
|
||||
|
||||
if (!power_up_modem()) {
|
||||
return NSAPI_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
retcode = initialize_sim_card();
|
||||
if (retcode != NSAPI_ERROR_OK) {
|
||||
return retcode;
|
||||
}
|
||||
|
||||
success = nwk_registration(PACKET_SWITCHED) //perform network registration
|
||||
&& get_CCID(_at) //get integrated circuit ID of the SIM
|
||||
&& get_IMSI(_at) //get international mobile subscriber information
|
||||
&& get_IMEI(_at) //get international mobile equipment identifier
|
||||
&& get_MEID(_at) //its same as IMEI
|
||||
&& set_CMGF(_at) //set message format for SMS
|
||||
&& set_CNMI(_at); //set new SMS indication
|
||||
|
||||
if (!success) {
|
||||
return NSAPI_ERROR_NO_CONNECTION;
|
||||
}
|
||||
|
||||
/* Check if user want skip SIM pin checking on boot up */
|
||||
if (set_sim_pin_check_request) {
|
||||
retcode = do_sim_pin_check(_at, _pin);
|
||||
if (retcode != NSAPI_ERROR_OK) {
|
||||
return retcode;
|
||||
}
|
||||
/* set this request to false, as it is unnecessary to repeat in case of retry */
|
||||
set_sim_pin_check_request = false;
|
||||
}
|
||||
|
||||
/* check if the user requested a sim pin change */
|
||||
if (change_pin) {
|
||||
retcode = do_change_sim_pin(_at, _pin, _new_pin);
|
||||
if (retcode != NSAPI_ERROR_OK) {
|
||||
return retcode;
|
||||
}
|
||||
/* set this request to false, as it is unnecessary to repeat in case of retry */
|
||||
change_pin = false;
|
||||
}
|
||||
|
||||
#if MBED_CONF_REF_CELL_DRV_APN_LOOKUP && !set_credentials_api_used
|
||||
apn_config = apnconfig(dev_info.imsi);
|
||||
if (apn_config) {
|
||||
_apn = _APN_GET(apn_config);
|
||||
_uname = _APN_GET(apn_config);
|
||||
_pwd = _APN_GET(apn_config);
|
||||
}
|
||||
|
||||
_apn = _apn ? _apn : NULL;
|
||||
_uname = _uname ? _uname : NULL;
|
||||
_pwd = _pwd ? _pwd : NULL;
|
||||
#endif
|
||||
|
||||
//sets up APN and IP protocol for external PDP context
|
||||
retcode = setup_context_and_credentials();
|
||||
if (retcode != NSAPI_ERROR_OK) {
|
||||
return retcode;
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
shutdown_at_parser();
|
||||
return NSAPI_ERROR_NO_CONNECTION;
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
did_init = true;
|
||||
} else {
|
||||
/* If we were already initialized, we expect to receive NO_CARRIER response
|
||||
* from the modem as we were kicked out of Data mode */
|
||||
_at->recv("NO CARRIER");
|
||||
success = _at->send("AT") && _at->recv("OK");
|
||||
}
|
||||
|
||||
/* Attempt to enter data mode */
|
||||
success = set_atd(_at); //enter into Data mode with the modem
|
||||
if (!success) {
|
||||
power_down_modem();
|
||||
initialized = false;
|
||||
|
||||
/* if we were previously initialized , i.e., not in this particular attempt,
|
||||
* we want to re-initialize */
|
||||
if (!did_init) {
|
||||
goto retry_init;
|
||||
}
|
||||
|
||||
/* shutdown AT parser before notifying application of the failure */
|
||||
shutdown_at_parser();
|
||||
|
||||
return NSAPI_ERROR_NO_CONNECTION;
|
||||
}
|
||||
|
||||
/* This is the success case.
|
||||
* Save RAM, discard AT Parser as we have entered Data mode. */
|
||||
shutdown_at_parser();
|
||||
|
||||
/* Here we would like to attach an interrupt line to DCD pin
|
||||
* We cast back the serial interface from the file handle and set
|
||||
* the DCD line. */
|
||||
BufferedSerial *serial = static_cast<BufferedSerial *>(_fh);
|
||||
serial->set_data_carrier_detect(MDMDCD, MDMDCD_POLARITY);
|
||||
|
||||
/* Initialize PPP
|
||||
* mbed_ppp_init() is a blocking call, it will block until
|
||||
* connected, or timeout after 30 seconds*/
|
||||
retcode = nsapi_ppp_connect(_fh, _connection_status_cb, _uname, _pwd);
|
||||
if (retcode == NSAPI_ERROR_OK) {
|
||||
dev_info.ppp_connection_up = true;
|
||||
}
|
||||
|
||||
#if MBED_CONF_REF_CELL_DRV_APN_LOOKUP && !set_credentials_api_used
|
||||
} while(!dev_info.ppp_connection_up && apn_config && *apn_config);
|
||||
#endif
|
||||
return retcode;
|
||||
}
|
||||
|
||||
/**
|
||||
* User initiated disconnect
|
||||
*
|
||||
* Disconnects from PPP connection only and brings down the underlying network
|
||||
* interface
|
||||
*/
|
||||
nsapi_error_t ReferenceCellularDriver::disconnect()
|
||||
{
|
||||
nsapi_error_t ret = nsapi_ppp_disconnect(_fh);
|
||||
if (ret == NSAPI_ERROR_OK) {
|
||||
dev_info.ppp_connection_up = false;
|
||||
return NSAPI_ERROR_OK;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *ReferenceCellularDriver::get_ip_address()
|
||||
{
|
||||
return nsapi_ppp_get_ip_addr(_fh);
|
||||
}
|
||||
|
||||
const char *ReferenceCellularDriver::get_netmask()
|
||||
{
|
||||
return nsapi_ppp_get_netmask(_fh);
|
||||
}
|
||||
|
||||
const char *ReferenceCellularDriver::get_gateway()
|
||||
{
|
||||
return nsapi_ppp_get_ip_addr(_fh);
|
||||
}
|
||||
|
||||
/** Power down modem
|
||||
* Uses AT command to do it */
|
||||
void ReferenceCellularDriver::power_down_modem()
|
||||
{
|
||||
modem_power_down(&mdm_object);
|
||||
modem_deinit(&mdm_object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Powers up the modem
|
||||
*
|
||||
* Enables the GPIO lines to the modem and then wriggles the power line in short pulses.
|
||||
*/
|
||||
bool ReferenceCellularDriver::power_up_modem()
|
||||
{
|
||||
/* Initialize GPIO lines */
|
||||
modem_init(&mdm_object);
|
||||
/* Give modem a little time to settle down */
|
||||
wait(0.25);
|
||||
|
||||
bool success = false;
|
||||
|
||||
int retry_count = 0;
|
||||
while (true) {
|
||||
modem_power_up(&mdm_object);
|
||||
/* Modem tends to spit out noise during power up - don't confuse the parser */
|
||||
_at->flush();
|
||||
/* It is mandatory to avoid sending data to the serial port during the first 200 ms
|
||||
* of the module startup. Telit_xE910 Global form factor App note.
|
||||
* Not necessary for all types of modems however. Let's wait just to be on the safe side */
|
||||
wait_ms(200);
|
||||
_at->set_timeout(1000);
|
||||
if (_at->send("AT") && _at->recv("OK")) {
|
||||
tr_info("Modem Ready.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (++retry_count > 10) {
|
||||
goto failure;
|
||||
}
|
||||
}
|
||||
|
||||
_at->set_timeout(8000);
|
||||
|
||||
/*For more details regarding DCD and DTR circuitry, please refer to Modem AT manual */
|
||||
success = _at->send("AT"
|
||||
"E0;" //turn off modem echoing
|
||||
"+CMEE=2;"//turn on verbose responses
|
||||
"&K0"//turn off RTC/CTS handshaking
|
||||
"+IPR=115200;"//setup baud rate
|
||||
"&C1;"//set DCD circuit(109), changes in accordance with the carrier detect status
|
||||
"&D0")//set DTR circuit, we ignore the state change of DTR
|
||||
&& _at->recv("OK");
|
||||
|
||||
if (!success) {
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* If everything alright, return from here with success*/
|
||||
return success;
|
||||
|
||||
failure:
|
||||
tr_error("Preliminary modem setup failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pointer to the underlying network stack
|
||||
*/
|
||||
NetworkStack *ReferenceCellularDriver::get_stack()
|
||||
{
|
||||
return nsapi_ppp_get_stack();
|
||||
}
|
||||
|
|
@ -0,0 +1,277 @@
|
|||
/* Copyright (c) 2017 ARM 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.
|
||||
*/
|
||||
|
||||
#ifndef REFERENCE_CELLULAR_DRIVER_
|
||||
#define REFERENCE_CELLULAR_DRIVER_
|
||||
|
||||
#include "mbed.h"
|
||||
#include "nsapi.h"
|
||||
#include "rtos.h"
|
||||
#include "FileHandle.h"
|
||||
#include "InterruptIn.h"
|
||||
#include "ATParser.h"
|
||||
#include "PinNames.h"
|
||||
|
||||
// Forward declaration
|
||||
class NetworkStack;
|
||||
|
||||
/**
|
||||
* Network registration status
|
||||
* UBX-13001820 - AT Commands Example Application Note (Section 4.1.4.5)
|
||||
*/
|
||||
typedef enum {
|
||||
GSM=0,
|
||||
COMPACT_GSM=1,
|
||||
UTRAN=2,
|
||||
EDGE=3,
|
||||
HSDPA=4,
|
||||
HSUPA=5,
|
||||
HSDPA_HSUPA=6,
|
||||
LTE=7
|
||||
} radio_access_nwk_type;
|
||||
|
||||
/**
|
||||
* Used in registration process to tell which type of network
|
||||
* to connect.
|
||||
*/
|
||||
typedef enum {
|
||||
CIRCUIT_SWITCHED=0,
|
||||
PACKET_SWITCHED
|
||||
} nwk_type;
|
||||
|
||||
/**
|
||||
* Circuit Switched network registration status (CREG Usage)
|
||||
* UBX-13001820 - AT Commands Example Application Note (Section 7.10.3)
|
||||
*/
|
||||
typedef enum {
|
||||
CSD_NOT_REGISTERED_NOT_SEARCHING=0,
|
||||
CSD_REGISTERED=1,
|
||||
CSD_NOT_REGISTERED_SEARCHING=2,
|
||||
CSD_REGISTRATION_DENIED=3,
|
||||
CSD_UNKNOWN_COVERAGE=4,
|
||||
CSD_REGISTERED_ROAMING=5,
|
||||
CSD_SMS_ONLY=6,
|
||||
CSD_SMS_ONLY_ROAMING=7,
|
||||
CSD_CSFB_NOT_PREFERRED=9
|
||||
} nwk_registration_status_csd;
|
||||
|
||||
/**
|
||||
* Packet Switched network registration status (CGREG Usage)
|
||||
* UBX-13001820 - AT Commands Example Application Note (Section 18.27.3)
|
||||
*/
|
||||
typedef enum {
|
||||
PSD_NOT_REGISTERED_NOT_SEARCHING=0,
|
||||
PSD_REGISTERED=1,
|
||||
PSD_NOT_REGISTERED_SEARCHING=2,
|
||||
PSD_REGISTRATION_DENIED=3,
|
||||
PSD_UNKNOWN_COVERAGE=4,
|
||||
PSD_REGISTERED_ROAMING=5,
|
||||
PSD_EMERGENCY_SERVICES_ONLY=8
|
||||
} nwk_registration_status_psd;
|
||||
|
||||
typedef struct {
|
||||
char ccid[20+1]; //!< Integrated Circuit Card ID
|
||||
char imsi[15+1]; //!< International Mobile Station Identity
|
||||
char imei[15+1]; //!< International Mobile Equipment Identity
|
||||
char meid[18+1]; //!< Mobile Equipment IDentifier
|
||||
int flags;
|
||||
bool ppp_connection_up;
|
||||
radio_access_nwk_type rat;
|
||||
nwk_registration_status_csd reg_status_csd;
|
||||
nwk_registration_status_psd reg_status_psd;
|
||||
} device_info;
|
||||
|
||||
/** ReferenceCellularDriver class
|
||||
*
|
||||
* This interface serves as the controller/driver for the Cellular
|
||||
* modems (tested with UBLOX_C027 and MTS_DRAGONFLY_F411RE).
|
||||
*/
|
||||
class ReferenceCellularDriver : public CellularInterface {
|
||||
|
||||
public:
|
||||
ReferenceCellularDriver(PinName tx = MDMTXD, PinName rx = MDMRXD, int baud = MBED_CONF_REF_CELL_DRV_BAUD_RATE, bool debugOn = false);
|
||||
~ReferenceCellularDriver();
|
||||
|
||||
/** Set the Cellular network credentials
|
||||
*
|
||||
* Please check documentation of connect() for default behaviour of APN settings.
|
||||
*
|
||||
* @param apn Access point name
|
||||
* @param uname optionally, Username
|
||||
* @param pwd optionally, password
|
||||
*/
|
||||
virtual void set_credentials(const char *apn, const char *uname = 0,
|
||||
const char *pwd = 0);
|
||||
|
||||
/** Set the pin code for SIM card
|
||||
*
|
||||
* @param sim_pin PIN for the SIM card
|
||||
*/
|
||||
virtual void set_sim_pin(const char *sim_pin);
|
||||
|
||||
/** Start the interface
|
||||
*
|
||||
* Attempts to connect to a Cellular network.
|
||||
* This driver is written mainly for data network connections as CellularInterface
|
||||
* is NetworkInterface. That's why connect() call internally calls nwk_registration()
|
||||
* method with parameter PACKET_SWITCHED network. Circuit switched hook and registration
|
||||
* process is implemented and left in the driver for future extension/subclass support,e.g.,
|
||||
* an SMS or GPS extension.
|
||||
*
|
||||
* @param sim_pin PIN for the SIM card
|
||||
* @param apn optionally, access point name
|
||||
* @param uname optionally, Username
|
||||
* @param pwd optionally, password
|
||||
* @return NSAPI_ERROR_OK on success, or negative error code on failure
|
||||
*/
|
||||
virtual nsapi_error_t connect(const char *sim_pin, const char *apn = 0,
|
||||
const char *uname = 0, const char *pwd = 0);
|
||||
|
||||
|
||||
/** Attempt to connect to the Cellular network
|
||||
*
|
||||
* Brings up the network interface. Connects to the Cellular Radio
|
||||
* network and then brings up the underlying network stack to be used
|
||||
* by the cellular modem over PPP interface.
|
||||
*
|
||||
* If the SIM requires a PIN, and it is not set/invalid, NSAPI_ERROR_AUTH_ERROR is returned.
|
||||
* For APN setup, default behaviour is to use 'internet' as APN string and assuming no authentication
|
||||
* is required, i.e., username and password are no set. Optionally, a database lookup can be requested
|
||||
* by turning on the APN database lookup feature. In order to do so, add 'MBED_REF_CELL_DRV_APN_LOOKUP'
|
||||
* in your mbed_app.json. APN database is by no means exhaustive. It contains a short list of some public
|
||||
* APNs with publicly available usernames and passwords (if required) in some particular countries only.
|
||||
* Lookup is done using IMSI (International mobile subscriber identifier).
|
||||
* Please note that even if 'MBED_REF_CELL_DRV_APN_LOOKUP' config option is set, 'set_credentials()' api still
|
||||
* gets the precedence. If the aforementioned API is not used and the config option is set but no match is found in
|
||||
* the lookup table then the driver tries to resort to default APN settings.
|
||||
*
|
||||
* Preferred method is to setup APN using 'set_credentials()' API.
|
||||
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual nsapi_error_t connect();
|
||||
|
||||
/** Attempt to disconnect from the network
|
||||
*
|
||||
* Brings down the network interface. Shuts down the PPP interface
|
||||
* of the underlying network stack. Does not bring down the Radio network
|
||||
*
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual nsapi_error_t disconnect();
|
||||
|
||||
/** Adds or removes a SIM facility lock
|
||||
*
|
||||
* Can be used to enable or disable SIM pin check at device startup.
|
||||
* This API sets up flags for the driver which would either enable or disable
|
||||
* SIM pin checking depending upon the user provided argument while establishing
|
||||
* connection. It doesn't do anything immediately other than setting up flags.
|
||||
*
|
||||
* @param set can be set to true if the SIM pin check is supposed to be enabled
|
||||
* and vice versa.
|
||||
*/
|
||||
void set_sim_pin_check(bool set);
|
||||
|
||||
/** Change the pin for the SIM card
|
||||
*
|
||||
* Provide the new pin for your SIM card with this API. Old pin code will be assumed to
|
||||
* be set using set_SIM_pin() API. This API have no immediate effect. While establishing
|
||||
* connection, driver will change the SIM pin for the next boot.
|
||||
*
|
||||
* @param new_pin new pin to be used in string format
|
||||
*/
|
||||
void set_new_sim_pin(const char *new_pin);
|
||||
|
||||
/** Check if the connection is currently established or not
|
||||
*
|
||||
* @return true/false If the cellular module have successfully acquired a carrier and is
|
||||
* connected to an external packet data network using PPP, isConnected()
|
||||
* API returns true and false otherwise.
|
||||
*/
|
||||
virtual bool is_connected();
|
||||
|
||||
/** Get the local IP address
|
||||
*
|
||||
* @return Null-terminated representation of the local IP address
|
||||
* or null if no IP address has been recieved
|
||||
*/
|
||||
virtual const char *get_ip_address();
|
||||
|
||||
/** Get the local network mask
|
||||
*
|
||||
* @return Null-terminated representation of the local network mask
|
||||
* or null if no network mask has been recieved
|
||||
*/
|
||||
virtual const char *get_netmask();
|
||||
|
||||
/** Get the local gateways
|
||||
*
|
||||
* @return Null-terminated representation of the local gateway
|
||||
* or null if no network mask has been recieved
|
||||
*/
|
||||
virtual const char *get_gateway();
|
||||
|
||||
/** Get notified if the connection gets lost
|
||||
*
|
||||
* @param cb user defined callback
|
||||
*/
|
||||
void connection_status_cb(Callback<void(nsapi_error_t)> cb);
|
||||
|
||||
/** Turn modem debug traces on
|
||||
*
|
||||
* @param on set true to enable debug traces
|
||||
*/
|
||||
void modem_debug_on(bool on);
|
||||
|
||||
private:
|
||||
FileHandle *_fh;
|
||||
ATParser *_at;
|
||||
InterruptIn *_dcd;
|
||||
const char *_new_pin;
|
||||
const char *_pin;
|
||||
const char *_apn;
|
||||
const char *_uname;
|
||||
const char *_pwd;
|
||||
bool _debug_trace_on;
|
||||
Callback<void(nsapi_error_t)> _connection_status_cb;
|
||||
void setup_at_parser();
|
||||
void shutdown_at_parser();
|
||||
nsapi_error_t initialize_sim_card();
|
||||
nsapi_error_t setup_context_and_credentials();
|
||||
bool power_up_modem();
|
||||
void power_down_modem();
|
||||
|
||||
protected:
|
||||
/** Provide access to the underlying stack
|
||||
*
|
||||
* @return The underlying network stack
|
||||
*/
|
||||
virtual NetworkStack *get_stack();
|
||||
|
||||
/** Starts network registration process.
|
||||
*
|
||||
* Potential users could be subclasses who are not network interface
|
||||
* but would like to use CellularInterface infrastructure to register
|
||||
* with a cellular network, e.g., an SMS extension to CellularInterface.
|
||||
*
|
||||
* @param nwk_type type of network to connect, defaults to packet switched network
|
||||
*
|
||||
* @return true if registration is successful
|
||||
*/
|
||||
bool nwk_registration(uint8_t nwk_type=PACKET_SWITCHED);
|
||||
|
||||
};
|
||||
|
||||
#endif //REFERENCE_CELLULAR_DRIVER_
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "ref-cell-drv",
|
||||
"config": {
|
||||
"baud-rate": 115200,
|
||||
"apn-lookup": false,
|
||||
"at-parser-buffer-size": 256,
|
||||
"at-parser-timeout": 8000
|
||||
},
|
||||
"target_overrides": {
|
||||
"*": {
|
||||
"target.features_add": ["COMMON_PAL"]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -248,7 +248,7 @@
|
|||
"supported_form_factors": ["ARDUINO"],
|
||||
"core": "Cortex-M3",
|
||||
"supported_toolchains": ["ARM", "uARM", "GCC_ARM", "GCC_CR", "IAR"],
|
||||
"extra_labels": ["NXP", "LPC176X", "FLASH_CMSIS_ALGO", "UBLOX_MODEM"],
|
||||
"extra_labels": ["NXP", "LPC176X", "FLASH_CMSIS_ALGO", "UBLOX_MODEM_GENERIC", "GENERIC_MODEM"],
|
||||
"macros": ["TARGET_LPC1768"],
|
||||
"inherits": ["LPCTarget"],
|
||||
"device_has": ["ANALOGIN", "ANALOGOUT", "CAN", "DEBUG_AWARENESS", "ERROR_RED", "ETHERNET", "I2C", "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", "PORTOUT", "PWMOUT", "RTC", "SERIAL", "SERIAL_FC", "SLEEP", "SPI", "SPISLAVE", "STDIO_MESSAGES", "FLASH", "MODEM"],
|
||||
|
@ -1337,7 +1337,7 @@
|
|||
"inherits": ["Target"],
|
||||
"core": "Cortex-M4F",
|
||||
"supported_toolchains": ["ARM", "uARM", "GCC_ARM", "IAR"],
|
||||
"extra_labels": ["STM", "STM32F4", "STM32F411RE"],
|
||||
"extra_labels": ["STM", "STM32F4", "STM32F411RE", "DRAGONFLY_MODEM", "GENERIC_MODEM"],
|
||||
"macros": ["HSE_VALUE=26000000", "VECT_TAB_OFFSET=0x08010000","TRANSACTION_QUEUE_SIZE_SPI=2", "RTC_LSI=1"],
|
||||
"post_binary_hook": {
|
||||
"function": "MTSCode.combine_bins_mts_dragonfly",
|
||||
|
|
Loading…
Reference in New Issue