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.
Dependencies: ublox-at-cellular-interface
UbloxATCellularInterfaceExt.cpp
- Committer:
- fahim.alavi@u-blox.com
- Date:
- 2019-01-28
- Revision:
- 17:ac64a6b90925
- Parent:
- 15:6f0a1ecc8cec
- Child:
- 28:4427f2e6bbab
File content as of revision 17:ac64a6b90925:
/* Copyright (c) 2017 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 "UbloxATCellularInterfaceExt.h"
#include "APN_db.h"
#ifdef FEATURE_COMMON_PAL
#include "mbed_trace.h"
#define TRACE_GROUP "UCAD"
#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
/**********************************************************************
* PROTECTED METHODS: HTTP
**********************************************************************/
// Callback for HTTP result code handling.
void UbloxATCellularInterfaceExt::UUHTTPCR_URC()
{
char buf[32];
int a, b, c;
// Note: not calling _at->recv() from here as we're
// already in an _at->recv()
// +UUHTTPCR: <profile_id>,<op_code>,<param_val>
if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
if (sscanf(buf, ": %d,%d,%d", &a, &b, &c) == 3) {
_httpProfiles[a].cmd = b; // Command
_httpProfiles[a].result = c; // Result
debug_if(_debug_trace_on, "%s on profile %d, result code is %d\n", getHttpCmd((HttpCmd) b), a, c);
}
}
}
// Find a given profile. NOTE: LOCK() before calling.
int UbloxATCellularInterfaceExt::findProfile(int modemHandle)
{
for (unsigned int profile = 0; profile < (sizeof(_httpProfiles)/sizeof(_httpProfiles[0]));
profile++) {
if (_httpProfiles[profile].modemHandle == modemHandle) {
return profile;
}
}
return HTTP_PROF_UNUSED;
}
// Return a string representing an HTTP AT command.
const char *UbloxATCellularInterfaceExt::getHttpCmd(HttpCmd httpCmd)
{
const char * str = "HTTP command not recognised";
switch (httpCmd) {
case HTTP_HEAD:
str = "HTTP HEAD command";
break;
case HTTP_GET:
str = "HTTP GET command";
break;
case HTTP_DELETE:
str = "HTTP DELETE command";
break;
case HTTP_PUT:
str = "HTTP PUT command";
break;
case HTTP_POST_FILE:
str = "HTTP POST file command";
break;
case HTTP_POST_DATA:
str = "HTTP POST data command";
break;
default:
break;
}
return str;
}
/**********************************************************************
* PROTECTED METHODS: FTP
**********************************************************************/
// Callback for FTP result code handling.
void UbloxATCellularInterfaceExt::UUFTPCR_URC()
{
char buf[64];
char md5[32];
// Note: not calling _at->recv() from here as we're
// already in an _at->recv()
// +UUFTPCR: <op_code>,<ftp_result>[,<md5_sum>]
if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
if (sscanf(buf, ": %d,%d,%32[^\n]\n", &_lastFtpOpCodeResult, &_lastFtpResult, md5) == 3) {
// Store the MD5 sum if we can
if ((_ftpBuf != NULL) && (_ftpBufLen >= 32)) {
memcpy (_ftpBuf, md5, 32);
if (_ftpBufLen == 33) {
*(buf + 32) = 0; // Add a terminator if there's room
}
}
}
debug_if(_debug_trace_on, "%s result code is %d\n",
getFtpCmd((FtpCmd) _lastFtpOpCodeResult), _lastFtpResult);
}
}
// Callback for FTP data handling.
void UbloxATCellularInterfaceExt::UUFTPCD_URC()
{
char buf[32];
char *ftpBufPtr = _ftpBuf;
int ftpDataLen;
// Note: not calling _at->recv() from here as we're
// already in an _at->recv()
// +UUFTPCD: <op_code>,<ftp_data_len>,<ftp_data_in_quotes>
if (read_at_to_char(buf, sizeof(buf), '\"') > 0) {
if (sscanf(buf, ": %d,%d,\"", &_lastFtpOpCodeData, &ftpDataLen) == 2) {
if ((ftpBufPtr != NULL) && (_ftpBufLen > 0)) {
if (ftpDataLen + 1 > _ftpBufLen) { // +1 for terminator
ftpDataLen = _ftpBufLen - 1;
}
ftpBufPtr += _at->read(ftpBufPtr, ftpDataLen);
*ftpBufPtr = 0; // Add terminator
}
}
}
}
// Return a string representing an FTP AT command.
const char *UbloxATCellularInterfaceExt::getFtpCmd(FtpCmd ftpCmd)
{
const char * str = "FTP command not recognised";
switch (ftpCmd) {
case FTP_LOGOUT:
str = "FTP log out command";
break;
case FTP_LOGIN:
str = "FTP log in command";
break;
case FTP_DELETE_FILE:
str = "FTP delete file command";
break;
case FTP_RENAME_FILE:
str = "FTP rename file command";
break;
case FTP_GET_FILE:
str = "FTP get file command";
break;
case FTP_PUT_FILE:
str = "FTP put file command";
break;
case FTP_CD:
str = "FTP change directory command";
break;
case FTP_MKDIR:
str = "FTP make directory command";
break;
case FTP_RMDIR:
str = "FTP remove directory command";
break;
case FTP_FILE_INFO:
str = "FTP file info command";
break;
case FTP_LS:
str = "FTP directory list command";
break;
case FTP_FOTA_FILE:
str = "FTP FOTA file command";
break;
default:
break;
}
return str;
}
/**********************************************************************
* PROTECTED METHODS: Cell Locate
**********************************************************************/
// Callback for UULOCIND handling.
void UbloxATCellularInterfaceExt::UULOCIND_URC()
{
char buf[32];
int a, b;
// Note: not calling _at->recv() from here as we're
// already in an _at->recv()
// +UULOCIND: <step>,<result>
if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
if (sscanf(buf, " %d,%d", &a, &b) == 2) {
switch (a) {
case 0:
debug_if(_debug_trace_on, "Network scan start\n");
break;
case 1:
debug_if(_debug_trace_on, "Network scan end\n");
break;
case 2:
debug_if(_debug_trace_on, "Requesting data from server\n");
break;
case 3:
debug_if(_debug_trace_on, "Received data from server\n");
break;
case 4:
debug_if(_debug_trace_on, "Sending feedback to server\n");
break;
default:
debug_if(_debug_trace_on, "Unknown step\n");
break;
}
switch (b) {
case 0:
// No error
break;
case 1:
debug_if(_debug_trace_on, "Wrong URL!\n");
break;
case 2:
debug_if(_debug_trace_on, "HTTP error!\n");
break;
case 3:
debug_if(_debug_trace_on, "Create socket error!\n");
break;
case 4:
debug_if(_debug_trace_on, "Close socket error!\n");
break;
case 5:
debug_if(_debug_trace_on, "Write to socket error!\n");
break;
case 6:
debug_if(_debug_trace_on, "Read from socket error!\n");
break;
case 7:
debug_if(_debug_trace_on, "Connection/DNS error!\n");
break;
case 8:
debug_if(_debug_trace_on, "Authentication token problem!\n");
break;
case 9:
debug_if(_debug_trace_on, "Generic error!\n");
break;
case 10:
debug_if(_debug_trace_on, "User terminated!\n");
break;
case 11:
debug_if(_debug_trace_on, "No data from server!\n");
break;
default:
debug_if(_debug_trace_on, "Unknown result!\n");
break;
}
}
}
}
// Callback for UULOC URC handling.
void UbloxATCellularInterfaceExt::UULOC_URC()
{
int a, b;
// Note: not calling _at->recv() from here as we're
// already in an _at->recv()
// +UHTTPCR: <profile_id>,<op_code>,<param_val>
if (read_at_to_char(urcBuf, sizeof (urcBuf), '\n') > 0) {
// Response type 1
// +UULOC: <date>,<time>,<lat>,<long>,<alt>,<uncertainty>,<speed>, <direction>,<vertical_acc>,<sensor_used>,<SV_used>,<antenna_status>, <jamming_status>
if (sscanf(urcBuf, " %d/%d/%d,%d:%d:%d.%*d,%f,%f,%d,%d,%d,%d,%d,%d,%d,%*d,%*d",
&_loc[0].time.tm_mday, &_loc[0].time.tm_mon,
&_loc[0].time.tm_year, &_loc[0].time.tm_hour,
&_loc[0].time.tm_min, &_loc[0].time.tm_sec,
&_loc[0].latitude, &_loc[0].longitude, &_loc[0].altitude,
&_loc[0].uncertainty, &_loc[0].speed, &_loc[0].direction,
&_loc[0].verticalAcc,
&b, &_loc[0].svUsed) == 15) {
debug_if(_debug_trace_on, "Position found at index 0\n");
_loc[0].sensor = (b == 0) ? CELL_LAST : (b == 1) ? CELL_GNSS :
(b == 2) ? CELL_LOCATE : (b == 3) ? CELL_HYBRID : CELL_LAST;
_loc[0].time.tm_year -= 1900;
_loc[0].time.tm_mon -= 1;
_loc[0].time.tm_wday = 0;
_loc[0].time.tm_yday = 0;
_loc[0].validData = true;
// Uncertainty can appear as 4294967, which is
// (2^32 - 1) / 1000, or -1. Since it is confusing
// for the user to get a large positive number instead
// of 0 -1, set it to -1 in th1s case.
if (_loc[0].uncertainty == 4294967) {
_loc[0].uncertainty = -1;
}
_locExpPos = 1;
_locRcvPos++;
// Response type 2, sensor used 1
// +UULOC: <sol>,<num>,<sensor_used>,<date>,<time>,<lat>,<long>,<alt>,<uncertainty>,<speed>, <direction>,<vertical_acc>,,<SV_used>,<antenna_status>, <jamming_status>
} else if (sscanf(urcBuf, " %d,%d,%d,%d/%d/%d,%d:%d:%d.%*d,%f,%f,%d,%d,%d,%d,%d,%d,%*d,%*d",
&a, &_locExpPos, &b,
&_loc[CELL_MAX_HYP - 1].time.tm_mday,
&_loc[CELL_MAX_HYP - 1].time.tm_mon,
&_loc[CELL_MAX_HYP - 1].time.tm_year,
&_loc[CELL_MAX_HYP - 1].time.tm_hour,
&_loc[CELL_MAX_HYP - 1].time.tm_min,
&_loc[CELL_MAX_HYP - 1].time.tm_sec,
&_loc[CELL_MAX_HYP - 1].latitude,
&_loc[CELL_MAX_HYP - 1].longitude,
&_loc[CELL_MAX_HYP - 1].altitude,
&_loc[CELL_MAX_HYP - 1].uncertainty,
&_loc[CELL_MAX_HYP - 1].speed,
&_loc[CELL_MAX_HYP - 1].direction,
&_loc[CELL_MAX_HYP - 1].verticalAcc,
&_loc[CELL_MAX_HYP - 1].svUsed) == 17) {
if (--a >= 0) {
debug_if(_debug_trace_on, "Position found at index %d\n", a);
memcpy(&_loc[a], &_loc[CELL_MAX_HYP - 1], sizeof(*_loc));
_loc[a].sensor = (b == 0) ? CELL_LAST : (b == 1) ? CELL_GNSS :
(b == 2) ? CELL_LOCATE : (b == 3) ? CELL_HYBRID : CELL_LAST;
_loc[a].time.tm_year -= 1900;
_loc[a].time.tm_mon -= 1;
_loc[a].time.tm_wday = 0;
_loc[a].time.tm_yday = 0;
// Uncertainty can appear as 4294967, which is
// (2^32 - 1) / 1000, or -1. Since it is confusing
// for the user to get a large positive number instead
// of 0 -1, set it to -1 in th1s case.
if (_loc[a].uncertainty == 4294967) {
_loc[a].uncertainty = -1;
}
_loc[a].validData = true;
_locRcvPos++;
}
// Response type 2, sensor used 2
//+UULOC: <sol>,<num>,<sensor_used>,<date>,<time>,<lat>,<long>,<alt>,<lat50>,<long50>,<major50>,<minor50>,<orientation50>,<confidence50>[,<lat95>,<long95>,<major95>,<minor95>,<orientation95>,<confidence95>]
} else if (sscanf(urcBuf, " %d,%d,%d,%d/%d/%d,%d:%d:%d.%*d,%f,%f,%d,%*f,%*f,%d,%*d,%*d,%*d",
&a, &_locExpPos, &b,
&_loc[CELL_MAX_HYP - 1].time.tm_mday,
&_loc[CELL_MAX_HYP - 1].time.tm_mon,
&_loc[CELL_MAX_HYP - 1].time.tm_year,
&_loc[CELL_MAX_HYP - 1].time.tm_hour,
&_loc[CELL_MAX_HYP - 1].time.tm_min,
&_loc[CELL_MAX_HYP - 1].time.tm_sec,
&_loc[CELL_MAX_HYP - 1].latitude,
&_loc[CELL_MAX_HYP - 1].longitude,
&_loc[CELL_MAX_HYP - 1].altitude,
&_loc[CELL_MAX_HYP - 1].uncertainty) == 13) {
if (--a >= 0) {
debug_if(_debug_trace_on, "Position found at index %d\n", a);
memcpy(&_loc[a], &_loc[CELL_MAX_HYP - 1], sizeof(*_loc));
_loc[a].sensor = (b == 0) ? CELL_LAST : (b == 1) ? CELL_GNSS :
(b == 2) ? CELL_LOCATE : (b == 3) ? CELL_HYBRID : CELL_LAST;
_loc[a].time.tm_year -= 1900;
_loc[a].time.tm_mon -= 1;
_loc[a].time.tm_wday = 0;
_loc[a].time.tm_yday = 0;
_loc[a].speed = 0;
_loc[a].direction = 0;
_loc[a].verticalAcc = 0;
_loc[a].svUsed = 0;
// Uncertainty can appear as 4294967, which is
// (2^32 - 1) / 1000, or -1. Since it is confusing
// for the user to get a large positive number instead
// of 0 -1, set it to -1 in th1s case.
if (_loc[a].uncertainty == 4294967) {
_loc[a].uncertainty = -1;
}
_loc[a].validData = true;
_locRcvPos++;
}
// Response type 2, sensor used 0
//+UULOC: <sol>,<num>,<sensor_used>,<date>,<time>,<lat>,<long>,<alt>,<uncertainty>
} else if (sscanf(urcBuf, " %d,%d,%d,%d/%d/%d,%d:%d:%d.%*d,%f,%f,%d,%d",
&a, &_locExpPos, &b,
&_loc[CELL_MAX_HYP - 1].time.tm_mday,
&_loc[CELL_MAX_HYP - 1].time.tm_mon,
&_loc[CELL_MAX_HYP - 1].time.tm_year,
&_loc[CELL_MAX_HYP - 1].time.tm_hour,
&_loc[CELL_MAX_HYP - 1].time.tm_min,
&_loc[CELL_MAX_HYP - 1].time.tm_sec,
&_loc[CELL_MAX_HYP - 1].latitude,
&_loc[CELL_MAX_HYP - 1].longitude,
&_loc[CELL_MAX_HYP - 1].altitude,
&_loc[CELL_MAX_HYP - 1].uncertainty) == 13) {
if (--a >= 0) {
debug_if(_debug_trace_on, "Position found at index %d\n", a);
memcpy(&_loc[a], &_loc[CELL_MAX_HYP - 1], sizeof(*_loc));
_loc[a].sensor = (b == 0) ? CELL_LAST : (b == 1) ? CELL_GNSS :
(b == 2) ? CELL_LOCATE : (b == 3) ? CELL_HYBRID : CELL_LAST;
_loc[a].time.tm_year -= 1900;
_loc[a].time.tm_mon -= 1;
_loc[a].time.tm_wday = 0;
_loc[a].time.tm_yday = 0;
_loc[a].speed = 0;
_loc[a].direction = 0;
_loc[a].verticalAcc = 0;
_loc[a].svUsed = 0;
// Uncertainty can appear as 4294967, which is
// (2^32 - 1) / 1000, or -1. Since it is confusing
// for the user to get a large positive number instead
// of 0 -1, set it to -1 in th1s case.
if (_loc[a].uncertainty == 4294967) {
_loc[a].uncertainty = -1;
}
_loc[a].validData = true;
_locRcvPos++;
}
}
}
}
/**********************************************************************
* PUBLIC METHODS: GENERAL
**********************************************************************/
// Constructor.
UbloxATCellularInterfaceExt::UbloxATCellularInterfaceExt(PinName tx,
PinName rx,
int baud,
bool debugOn,
osPriority priority):
UbloxATCellularInterface(tx, rx, baud, debugOn, priority)
{
// Zero HTTP stuff
memset(_httpProfiles, 0, sizeof(_httpProfiles));
for (unsigned int profile = 0; profile < sizeof(_httpProfiles) / sizeof(_httpProfiles[0]);
profile++) {
_httpProfiles[profile].modemHandle = HTTP_PROF_UNUSED;
}
// Zero FTP stuff
_ftpTimeout = TIMEOUT_BLOCKING;
_lastFtpOpCodeResult = FTP_OP_CODE_UNUSED;
_lastFtpResult = 0;
_lastFtpOpCodeData = FTP_OP_CODE_UNUSED;
_ftpBuf = NULL;
_ftpBufLen = 0;
_ftpError.eClass = 0;
_ftpError.eCode = 0;
// Zero Cell Locate stuff
_locRcvPos = 0;
_locExpPos = 0;
// URC handler for HTTP
_at->oob("+UUHTTPCR", callback(this, &UbloxATCellularInterfaceExt::UUHTTPCR_URC));
// URC handlers for FTP
_at->oob("+UUFTPCR", callback(this, &UbloxATCellularInterfaceExt::UUFTPCR_URC));
_at->oob("+UUFTPCD", callback(this, &UbloxATCellularInterfaceExt::UUFTPCD_URC));
// URC handlers for Cell Locate
_at->oob("+UULOCIND:", callback(this, &UbloxATCellularInterfaceExt::UULOCIND_URC));
_at->oob("+UULOC:", callback(this, &UbloxATCellularInterfaceExt::UULOC_URC));
}
// Destructor.
UbloxATCellularInterfaceExt::~UbloxATCellularInterfaceExt()
{
}
/**********************************************************************
* PUBLIC METHODS: HTTP
**********************************************************************/
// Find a free profile.
int UbloxATCellularInterfaceExt::httpAllocProfile()
{
int profile = HTTP_PROF_UNUSED;
LOCK();
// Find a free HTTP profile
profile = findProfile();
debug_if(_debug_trace_on, "httpFindProfile: profile is %d\n", profile);
if (profile != HTTP_PROF_UNUSED) {
_httpProfiles[profile].modemHandle = 1;
_httpProfiles[profile].timeout = TIMEOUT_BLOCKING;
_httpProfiles[profile].pending = false;
_httpProfiles[profile].cmd = -1;
_httpProfiles[profile].result = -1;
}
UNLOCK();
return profile;
}
// Free a profile.
bool UbloxATCellularInterfaceExt::httpFreeProfile(int profile)
{
bool success = false;
LOCK();
if (IS_PROFILE(profile)) {
debug_if(_debug_trace_on, "httpFreeProfile(%d)\n", profile);
_httpProfiles[profile].modemHandle = HTTP_PROF_UNUSED;
_httpProfiles[profile].timeout = TIMEOUT_BLOCKING;
_httpProfiles[profile].pending = false;
_httpProfiles[profile].cmd = -1;
_httpProfiles[profile].result = -1;
success = _at->send("AT+UHTTP=%d", profile) && _at->recv("OK");
}
UNLOCK();
return success;
}
// Set the blocking/timeout state of a profile.
bool UbloxATCellularInterfaceExt::httpSetTimeout(int profile, int timeout)
{
bool success = false;
LOCK();
debug_if(_debug_trace_on, "httpSetTimeout(%d, %d)\n", profile, timeout);
if (IS_PROFILE(profile)) {
_httpProfiles[profile].timeout = timeout;
success = true;
}
UNLOCK();
return success;
}
// Set a profile back to defaults.
bool UbloxATCellularInterfaceExt::httpResetProfile(int httpProfile)
{
bool success = false;
LOCK();
debug_if(_debug_trace_on, "httpResetProfile(%d)\n", httpProfile);
success = _at->send("AT+UHTTP=%d", httpProfile) && _at->recv("OK");
UNLOCK();
return success;
}
// Set HTTP parameters.
bool UbloxATCellularInterfaceExt::httpSetPar(int httpProfile,
HttpOpCode httpOpCode,
const char * httpInPar)
{
bool success = false;
int httpInParNum = 0;
SocketAddress address;
debug_if(_debug_trace_on, "httpSetPar(%d, %d, \"%s\")\n", httpProfile, httpOpCode, httpInPar);
if (IS_PROFILE(httpProfile)) {
LOCK();
switch(httpOpCode) {
case HTTP_IP_ADDRESS: // 0
if (gethostbyname(httpInPar, &address) == NSAPI_ERROR_OK) {
success = _at->send("AT+UHTTP=%d,%d,\"%s\"",
httpProfile, httpOpCode, address.get_ip_address()) &&
_at->recv("OK");
}
break;
case HTTP_SERVER_NAME: // 1
case HTTP_USER_NAME: // 2
case HTTP_PASSWORD: // 3
success = _at->send("AT+UHTTP=%d,%d,\"%s\"", httpProfile, httpOpCode, httpInPar) &&
_at->recv("OK");
break;
case HTTP_AUTH_TYPE: // 4
case HTTP_SERVER_PORT: // 5
httpInParNum = atoi(httpInPar);
success = _at->send("AT+UHTTP=%d,%d,%d", httpProfile, httpOpCode, httpInParNum) &&
_at->recv("OK");
break;
case HTTP_SECURE: // 6
httpInParNum = atoi(httpInPar);
success = _at->send("AT+UHTTP=%d,%d,%d", httpProfile, httpOpCode, httpInParNum) &&
_at->recv("OK");
break;
default:
debug_if(_debug_trace_on, "httpSetPar: unknown httpOpCode %d\n", httpOpCode);
break;
}
UNLOCK();
}
return success;
}
// Perform an HTTP command.
UbloxATCellularInterfaceExt::Error * UbloxATCellularInterfaceExt::httpCommand(int httpProfile,
HttpCmd httpCmd,
const char *httpPath,
const char *rspFile,
const char *sendStr,
int httpContentType,
const char *httpCustomPar,
char *buf, int len, int *read_size)
{
bool atSuccess = false;
bool success = false;
int at_timeout;
char defaultFilename[] = "http_last_response_x";
debug_if(_debug_trace_on, "%s\n", getHttpCmd(httpCmd));
if (IS_PROFILE(httpProfile)) {
LOCK();
at_timeout = _at_timeout; // Has to be inside LOCK()s
if (rspFile == NULL) {
sprintf(defaultFilename + sizeof (defaultFilename) - 2, "%1d", httpProfile);
rspFile = defaultFilename;
}
switch (httpCmd) {
case HTTP_HEAD:
atSuccess = _at->send("AT+UHTTPC=%d,%d,\"%s\",\"%s\"", httpProfile, httpCmd,
httpPath, rspFile) &&
_at->recv("OK");
break;
case HTTP_GET:
atSuccess = _at->send("AT+UHTTPC=%d,%d,\"%s\",\"%s\"", httpProfile, httpCmd,
httpPath, rspFile) &&
_at->recv("OK");
break;
case HTTP_DELETE:
atSuccess = _at->send("AT+UHTTPC=%d,%d,\"%s\",\"%s\"", httpProfile, httpCmd,
httpPath, rspFile) &&
_at->recv("OK");
break;
case HTTP_PUT:
// In this case the parameter sendStr is a filename
atSuccess = _at->send("AT+UHTTPC=%d,%d,\"%s\",\"%s\",\"%s\"", httpProfile, httpCmd,
httpPath, rspFile, sendStr) &&
_at->recv("OK");
break;
case HTTP_POST_FILE:
// In this case the parameter sendStr is a filename
if (httpContentType != 6) {
atSuccess = _at->send("AT+UHTTPC=%d,%d,\"%s\",\"%s\",\"%s\",%d",
httpProfile, httpCmd, httpPath, rspFile, sendStr,
httpContentType) &&
_at->recv("OK");
} else {
atSuccess = _at->send("AT+UHTTPC=%d,%d,\"%s\",\"%s\",\"%s\",%d,%s",
httpProfile, httpCmd, httpPath, rspFile, sendStr,
httpContentType,
httpCustomPar) &&
_at->recv("OK");
}
break;
case HTTP_POST_DATA:
// In this case the parameter sendStr is a string containing data
if (httpContentType != 6) {
atSuccess = _at->send("AT+UHTTPC=%d,%d,\"%s\",\"%s\",\"%s\",%d",
httpProfile, httpCmd, httpPath, rspFile, sendStr,
httpContentType) &&
_at->recv("OK");
} else {
atSuccess = _at->send("AT+UHTTPC=%d,%d,\"%s\",\"%s\",\"%s\",%d,%s",
httpProfile, httpCmd, httpPath, rspFile, sendStr,
httpContentType,
httpCustomPar) &&
_at->recv("OK");
}
break;
default:
debug_if(_debug_trace_on, "HTTP command not recognised\n");
break;
}
if (atSuccess) {
Timer timer;
int read_length = 0;
at_set_timeout(1000);
_httpProfiles[httpProfile].pending = true;
_httpProfiles[httpProfile].result = -1;
// Waiting for unsolicited result code
timer.start();
while (_httpProfiles[httpProfile].pending) {
if (_httpProfiles[httpProfile].result != -1) {
// Received unsolicited: starting its analysis
_httpProfiles[httpProfile].pending = false;
if (_httpProfiles[httpProfile].result == 1) {
// Leave a short delay to make sure the file has been written
wait_ms(100);
// HTTP command successfully executed
read_length = readFile(rspFile, buf, len);
if (read_length >= 0) {
success = true;
if(read_size != NULL) {
*read_size = *read_size + read_length;
}
}
} else {
// Retrieve the error class and code
if (_at->send("AT+UHTTPER=%d", httpProfile) &&
_at->recv("AT+UHTTPER=%*d,%d,%d",
&(_httpProfiles[httpProfile].httpError.eClass),
&(_httpProfiles[httpProfile].httpError.eCode)) &&
_at->recv("OK")) {
debug_if(_debug_trace_on, "HTTP error class %d, code %d\n",
_httpProfiles[httpProfile].httpError.eClass,
_httpProfiles[httpProfile].httpError.eCode);
}
}
} else if (!TIMEOUT(timer, _httpProfiles[httpProfile].timeout)) {
// Wait for URCs
_at->recv(UNNATURAL_STRING);
} else {
_httpProfiles[httpProfile].pending = false;
}
}
timer.stop();
at_set_timeout(at_timeout);
if (!success) {
debug_if(_debug_trace_on, "%s: ERROR\n", getHttpCmd(httpCmd));
}
}
UNLOCK();
}
return success ? NULL : &(_httpProfiles[httpProfile].httpError);
}
/**********************************************************************
* PUBLIC METHODS: FTP
**********************************************************************/
// Set the blocking/timeout for FTP.
bool UbloxATCellularInterfaceExt::ftpSetTimeout(int timeout)
{
LOCK();
debug_if(_debug_trace_on, "ftpSetTimeout(%d)\n", timeout);
_ftpTimeout = timeout;
UNLOCK();
return true;
}
// Reset the FTP configuration back to defaults.
// Note: not all modrmd support all parameters,
// hence the void return code.
void UbloxATCellularInterfaceExt::ftpResetPar()
{
LOCK();
debug_if(_debug_trace_on, "ftpResetPar()\n");
for (int x = 0; x < NUM_FTP_OP_CODES; x++) {
if (_at->send("AT+UFTP=%d", x)) {
_at->recv("OK");
}
}
UNLOCK();
}
// Set FTP parameters.
bool UbloxATCellularInterfaceExt::ftpSetPar(FtpOpCode ftpOpCode,
const char * ftpInPar)
{
bool success = false;
int ftpInParNum = 0;
LOCK();
debug_if(_debug_trace_on, "ftpSetPar(%d, %s)\n", ftpOpCode, ftpInPar);
switch (ftpOpCode) {
case FTP_IP_ADDRESS: // 0
case FTP_SERVER_NAME: // 1
case FTP_USER_NAME: // 2
case FTP_PASSWORD: // 3
case FTP_ACCOUNT: // 4
success = _at->send("AT+UFTP=%d,\"%s\"", ftpOpCode, ftpInPar) &&
_at->recv("OK");
break;
case FTP_INACTIVITY_TIMEOUT: // 5
case FTP_MODE: // 6
case FTP_SERVER_PORT: // 7
case FTP_SECURE: // 8
ftpInParNum = atoi(ftpInPar);
success = _at->send("AT+UFTP=%d,%d", ftpOpCode, ftpInParNum) &&
_at->recv("OK");
break;
default:
debug_if(_debug_trace_on, "ftpSetPar: unknown ftpOpCode %d\n", ftpOpCode);
break;
}
UNLOCK();
return success;
}
// Perform an FTP command.
UbloxATCellularInterfaceExt::Error * UbloxATCellularInterfaceExt::ftpCommand(FtpCmd ftpCmd,
const char* file1,
const char* file2,
char* buf, int len,
int offset)
{
bool atSuccess = false;
bool success = false;
int at_timeout;
LOCK();
at_timeout = _at_timeout; // Has to be inside LOCK()s
debug_if(_debug_trace_on, "%s\n", getFtpCmd(ftpCmd));
switch (ftpCmd) {
case FTP_LOGOUT:
case FTP_LOGIN:
atSuccess = _at->send("AT+UFTPC=%d", ftpCmd) && _at->recv("OK");
break;
case FTP_DELETE_FILE:
case FTP_CD:
case FTP_MKDIR:
case FTP_RMDIR:
case FTP_FOTA_FILE:
atSuccess = _at->send("AT+UFTPC=%d,\"%s\"", ftpCmd, file1) &&
_at->recv("OK");
break;
case FTP_RENAME_FILE:
atSuccess = _at->send("AT+UFTPC=%d,\"%s\",\"%s\"",
ftpCmd, file1, file2) &&
_at->recv("OK");
break;
case FTP_GET_FILE:
case FTP_PUT_FILE:
if (file2 == NULL) {
file2 = file1;
}
atSuccess = _at->send("AT+UFTPC=%d,\"%s\",\"%s\",%d",
ftpCmd, file1, file2, offset) &&
_at->recv("OK");
// Not all modules support continuing a GET/PUT (e.g.
// Sara-G350 00S-00 doesn't), so if we receive an error,
// try again without it
if (!atSuccess) {
atSuccess = _at->send("AT+UFTPC=%d,\"%s\",\"%s\"",
ftpCmd, file1, file2) &&
_at->recv("OK");
}
break;
case FTP_FILE_INFO:
case FTP_LS:
_ftpBuf = buf;
_ftpBufLen = len;
// Add a terminator in case nothing comes back
if (_ftpBufLen > 0) {
*_ftpBuf = 0;
}
if (file1 == NULL) {
atSuccess = _at->send("AT+UFTPC=%d", ftpCmd) &&
_at->recv("OK");
} else {
atSuccess = _at->send("AT+UFTPC=%d,\"%s\"", ftpCmd, file1) &&
_at->recv("OK");
}
break;
default:
debug_if(_debug_trace_on, "FTP command not recognised/supported\n");
break;
}
// Wait for the result to arrive back
if (atSuccess) {
Timer timer;
at_set_timeout(1000);
_lastFtpOpCodeData = FTP_OP_CODE_UNUSED;
_lastFtpOpCodeResult = FTP_OP_CODE_UNUSED;
_lastFtpResult = -1; // just for safety
// Waiting for result to arrive
timer.start();
while ((_lastFtpOpCodeResult == FTP_OP_CODE_UNUSED) &&
!TIMEOUT(timer, _ftpTimeout)) {
_at->recv(UNNATURAL_STRING);
}
timer.stop();
if ((_lastFtpOpCodeResult == ftpCmd) && (_lastFtpResult == 1)) {
// Got a result for our FTP op code and it is good
success = true;
} else {
// Retrieve the error class and code
if (_at->send("AT+UFTPER") &&
_at->recv("+UFTPER:%d,%d", &(_ftpError.eClass), &(_ftpError.eCode)) &&
_at->recv("OK")) {
debug_if(_debug_trace_on, "FTP Error class %d, code %d\n",
_ftpError.eClass, _ftpError.eCode);
}
}
at_set_timeout(at_timeout);
if (!success) {
debug_if(_debug_trace_on, "%s: ERROR\n", getFtpCmd(ftpCmd));
}
}
// Set these back to nothing to stop the URC splatting
_ftpBuf = NULL;
_ftpBufLen = 0;
UNLOCK();
return success ? NULL : &_ftpError;
}
/**********************************************************************
* PUBLIC METHODS: Cell Locate
**********************************************************************/
// Configure CellLocate TCP Aiding server.
bool UbloxATCellularInterfaceExt::cellLocSrvTcp(const char* token,
const char* server_1,
const char* server_2,
int days, int period,
int resolution)
{
bool success = false;
LOCK();
if ((_dev_info.dev == DEV_LISA_U2_03S) || (_dev_info.dev == DEV_SARA_U2)){
success = _at->send("AT+UGSRV=\"%s\",\"%s\",\"%s\",%d,%d,%d",
server_1, server_2, token, days, period, resolution) &&
_at->recv("OK");
}
UNLOCK();
return success;
}
// Configure CellLocate UDP Aiding server.
bool UbloxATCellularInterfaceExt::cellLocSrvUdp(const char* server_1,
int port, int latency,
int mode)
{
bool success = false;
LOCK();
if (_dev_info.dev != DEV_TOBY_L2) {
success = _at->send("AT+UGAOP=\"%s\",%d,%d,%d", server_1, port, latency, mode) &&
_at->recv("OK");
}
UNLOCK();
return success;
}
// Configure Cell Locate location sensor.
bool UbloxATCellularInterfaceExt::cellLocConfig(int scanMode)
{
bool success;
LOCK();
success = _at->send("AT+ULOCCELL=%d", scanMode) &&
_at->recv("OK");
UNLOCK();
return success;
}
// Request CellLocate.
bool UbloxATCellularInterfaceExt::cellLocRequest(CellSensType sensor,
int timeout,
int accuracy,
CellRespType type,
int hypothesis)
{
bool success = false;
if ((hypothesis <= CELL_MAX_HYP) &&
!((hypothesis > 1) && (type != CELL_MULTIHYP))) {
LOCK();
_locRcvPos = 0;
_locExpPos = 0;
for (int i = 0; i < hypothesis; i++) {
_loc[i].validData = false;
}
// Switch on the URC but don't error on it as some
// modules (e.g. Sara-G350) don't support it
if (_at->send("AT+ULOCIND=1")) {
_at->recv("OK");
}
// Switch on Cell Locate
success = _at->send("AT+ULOC=2,%d,%d,%d,%d,%d", sensor, type, timeout, accuracy, hypothesis) &&
_at->recv("OK");
// Answers are picked up by reacting to +ULOC
UNLOCK();
}
return success;
}
// Get a position record.
bool UbloxATCellularInterfaceExt::cellLocGetData(CellLocData *data, int index)
{
bool success = false;
if (_loc[index].validData) {
LOCK();
memcpy(data, &_loc[index], sizeof(*_loc));
success = true;
UNLOCK();
}
return success;
}
// Get number of position records received.
int UbloxATCellularInterfaceExt::cellLocGetRes()
{
int at_timeout;
LOCK();
at_timeout = _at_timeout; // Has to be inside LOCK()s
at_set_timeout(1000);
// Wait for URCs
_at->recv(UNNATURAL_STRING);
at_set_timeout(at_timeout);
UNLOCK();
return _locRcvPos;
}
// Get number of positions records expected to be received.
int UbloxATCellularInterfaceExt::cellLocGetExpRes()
{
int numRecords = 0;
LOCK();
_at->recv("OK");
if (_locRcvPos > 0) {
numRecords = _locExpPos;
}
UNLOCK();
return numRecords;
}
// End of file