Fix for HTTP return status code 1.0 in http_tls
Dependencies: ublox-at-cellular-interface
Revision 0:0b75e22c9231, committed 2017-06-05
- Comitter:
- RobMeades
- Date:
- Mon Jun 05 12:58:04 2017 +0000
- Child:
- 1:26a67ab07275
- Commit message:
- Initial revision.
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/unit_tests/cell-locate/main.cpp Mon Jun 05 12:58:04 2017 +0000
@@ -0,0 +1,250 @@
+#include "mbed.h"
+#include "greentea-client/test_env.h"
+#include "unity.h"
+#include "utest.h"
+#include "UbloxATCellularInterfaceExt.h"
+#include "UDPSocket.h"
+#include "FEATURE_COMMON_PAL/nanostack-libservice/mbed-client-libservice/common_functions.h"
+#include "mbed_trace.h"
+#define TRACE_GROUP "TEST"
+
+using namespace utest::v1;
+
+// ----------------------------------------------------------------
+// COMPILE-TIME MACROS
+// ----------------------------------------------------------------
+
+// These macros can be overridden with an mbed_app.json file and
+// contents of the following form:
+//
+//{
+// "config": {
+// "apn": {
+// "value": "\"my_apn\""
+// },
+// "run-tcp-server-test": {
+// "value": 1
+// },
+// "mga-token": {
+// "value": "\"my_token\""
+// }
+//}
+
+// The credentials of the SIM in the board.
+#ifndef MBED_CONF_APP_DEFAULT_PIN
+// Note: this is the PIN for the SIM with ICCID
+// 8944501104169548380.
+# define MBED_CONF_APP_DEFAULT_PIN "5134"
+#endif
+
+// Network credentials.
+#ifndef MBED_CONF_APP_APN
+# define MBED_CONF_APP_APN NULL
+#endif
+#ifndef MBED_CONF_APP_USERNAME
+# define MBED_CONF_APP_USERNAME NULL
+#endif
+#ifndef MBED_CONF_APP_PASSWORD
+# define MBED_CONF_APP_PASSWORD NULL
+#endif
+
+#ifndef MBED_CONF_APP_RUN_CELL_LOCATE_TCP_SERVER_TEST
+# define MBED_CONF_APP_RUN_CELL_LOCATE_TCP_SERVER_TEST 0
+#endif
+
+// The authentication token for TCP access to the MGA server.
+#if MBED_CONF_APP_RUN_CELL_LOCATE_TCP_SERVER_TEST
+# ifndef MBED_CONF_APP_CELL_LOCATE_MGA_TOKEN
+# error "You must have a token for MGA server access to run Cell Locate with a TCP server"
+# endif
+#endif
+
+// The type of response requested
+#ifndef MBED_CONF_APP_RESP_TYPE
+# define MBED_CONF_APP_RESP_TYPE 1 // CELL_DETAILED
+#endif
+
+// The maximum number of hypotheses requested
+#ifndef MBED_CONF_APP_CELL_LOCATE_MAX_NUM_HYPOTHESIS
+# if MBED_CONF_APP_RESP_TYPE == 2 // CELL_MULTIHYP
+# define MBED_CONF_APP_CELL_LOCATE_MAX_NUM_HYPOTHESIS 16
+# else
+# define MBED_CONF_APP_CELL_LOCATE_MAX_NUM_HYPOTHESIS 1
+# endif
+#endif
+
+#ifndef MBED_CONF_APP_CELL_LOCATE_MGA_TOKEN
+# define MBED_CONF_APP_CELL_LOCATE_MGA_TOKEN "0"
+#endif
+
+// ----------------------------------------------------------------
+// PRIVATE VARIABLES
+// ----------------------------------------------------------------
+
+// Lock for debug prints
+static Mutex mtx;
+
+// An instance of the cellular interface
+static UbloxATCellularInterfaceExt *pDriver =
+ new UbloxATCellularInterfaceExt(MDMTXD, MDMRXD,
+ MBED_CONF_UBLOX_CELL_BAUD_RATE,
+ true);
+
+// ----------------------------------------------------------------
+// PRIVATE FUNCTIONS
+// ----------------------------------------------------------------
+
+// Locks for debug prints
+static void lock()
+{
+ mtx.lock();
+}
+
+static void unlock()
+{
+ mtx.unlock();
+}
+
+static void printCellLocateData(UbloxATCellularInterfaceExt::CellLocData *pData)
+{
+ char timeString[25];
+
+ tr_debug("Cell Locate data:");
+ if (strftime(timeString, sizeof(timeString), "%a %b %d %H:%M:%S %Y", (const tm *) &(pData->time)) > 0) {
+ tr_debug(" time: %s", timeString);
+ }
+ tr_debug(" longitude: %.6f", pData->longitude);
+ tr_debug(" latitude: %.6f", pData->latitude);
+ tr_debug(" altitude: %d metres", pData->altitude);
+ tr_debug(" uncertainty: %d metres", pData->uncertainty);
+ tr_debug(" speed: %d metres/second", pData->speed);
+ tr_debug(" vertical accuracy: %d metres/second", pData->speed);
+ switch (pData->sensor) {
+ case UbloxATCellularInterfaceExt::CELL_LAST:
+ tr_debug(" sensor type: last");
+ break;
+ case UbloxATCellularInterfaceExt::CELL_GNSS:
+ tr_debug(" sensor type: GNSS");
+ break;
+ case UbloxATCellularInterfaceExt::CELL_LOCATE:
+ tr_debug(" sensor type: Cell Locate");
+ break;
+ case UbloxATCellularInterfaceExt::CELL_HYBRID:
+ tr_debug(" sensor type: hybrid");
+ break;
+ default:
+ tr_debug(" sensor type: unknown");
+ break;
+ }
+ tr_debug(" satellites used: %d", pData->svUsed);
+}
+
+// ----------------------------------------------------------------
+// TESTS
+// ----------------------------------------------------------------
+
+// Test Cell Locate talking to a UDP server
+void test_udp_server() {
+ int numRes = 0;
+ UbloxATCellularInterfaceExt::CellLocData data;
+
+ memset(&data, 0, sizeof(data));
+ TEST_ASSERT(pDriver->connect(MBED_CONF_APP_DEFAULT_PIN, MBED_CONF_APP_APN,
+ MBED_CONF_APP_USERNAME, MBED_CONF_APP_PASSWORD) == 0);
+
+ TEST_ASSERT(pDriver->cellLocSrvUdp());
+ TEST_ASSERT(pDriver->cellLocConfig(1));
+ TEST_ASSERT(pDriver->cellLocRequest(UbloxATCellularInterfaceExt::CELL_HYBRID, 10, 100,
+ (UbloxATCellularInterfaceExt::CellRespType) MBED_CONF_APP_RESP_TYPE,
+ MBED_CONF_APP_CELL_LOCATE_MAX_NUM_HYPOTHESIS));
+
+ for (int x = 0; (numRes == 0) && (x < 10); x++) {
+ numRes = pDriver->cellLocGetRes();
+ }
+
+ TEST_ASSERT(numRes > 0);
+ TEST_ASSERT(pDriver->cellLocGetData(&data));
+
+ TEST_ASSERT(data.validData);
+
+ printCellLocateData(&data);
+
+ TEST_ASSERT(pDriver->disconnect() == 0);
+ // Wait for printfs to leave the building or the test result string gets messed up
+ wait_ms(500);
+}
+
+// Test Cell Locate talking to a TCP server
+void test_tcp_server() {
+ int numRes = 0;
+ UbloxATCellularInterfaceExt::CellLocData data;
+
+ memset(&data, 0, sizeof(data));
+ TEST_ASSERT(pDriver->connect(MBED_CONF_APP_DEFAULT_PIN, MBED_CONF_APP_APN,
+ MBED_CONF_APP_USERNAME, MBED_CONF_APP_PASSWORD) == 0);
+
+ TEST_ASSERT(pDriver->cellLocSrvTcp(MBED_CONF_APP_CELL_LOCATE_MGA_TOKEN));
+ TEST_ASSERT(pDriver->cellLocConfig(1));
+ TEST_ASSERT(pDriver->cellLocRequest(UbloxATCellularInterfaceExt::CELL_HYBRID, 10, 100,
+ (UbloxATCellularInterfaceExt::CellRespType) MBED_CONF_APP_RESP_TYPE,
+ MBED_CONF_APP_CELL_LOCATE_MAX_NUM_HYPOTHESIS));
+
+ for (int x = 0; (numRes == 0) && (x < 10); x++) {
+ numRes = pDriver->cellLocGetRes();
+ }
+
+ TEST_ASSERT(numRes > 0);
+ TEST_ASSERT(pDriver->cellLocGetData(&data));
+
+ TEST_ASSERT(data.validData);
+
+ printCellLocateData(&data);
+
+ TEST_ASSERT(pDriver->disconnect() == 0);
+ // Wait for printfs to leave the building or the test result string gets messed up
+ wait_ms(500);
+}
+
+// Tidy up after testing so as not to screw with the test output strings
+void test_tidy_up() {
+ pDriver->deinit();
+}
+
+// ----------------------------------------------------------------
+// TEST ENVIRONMENT
+// ----------------------------------------------------------------
+
+// Setup the test environment
+utest::v1::status_t test_setup(const size_t number_of_cases) {
+ // Setup Greentea with a timeout
+ GREENTEA_SETUP(540, "default_auto");
+ return verbose_test_setup_handler(number_of_cases);
+}
+
+// Test cases
+Case cases[] = {
+ Case("Cell Locate with UDP server", test_udp_server),
+#if MBED_CONF_APP_RUN_CELL_LOCATE_TCP_SERVER_TEST
+ Case("Cell Locate with TCP server", test_tcp_server),
+#endif
+ Case("Tidy up", test_tidy_up)
+};
+
+Specification specification(test_setup, cases);
+
+// ----------------------------------------------------------------
+// MAIN
+// ----------------------------------------------------------------
+
+int main() {
+ mbed_trace_init();
+
+ mbed_trace_mutex_wait_function_set(lock);
+ mbed_trace_mutex_release_function_set(unlock);
+
+ // Run tests
+ return !Harness::run(specification);
+}
+
+// End Of File
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/unit_tests/cell-locate/template_mbed_app.txt Mon Jun 05 12:58:04 2017 +0000
@@ -0,0 +1,33 @@
+{
+ "config": {
+ "buffer-size": { "value": 0 },
+ "platform": { "help": "Options: UBLOX, MTS_DRAGONFLY",
+ "value": "UBLOX"},
+ "cell-locate-mga-token": {
+ "help": "Your Cell Locate MGA token, required for TCP testing. A tokane can be obtained from https://www.u-blox.com/en/assistnow-service-registration-form",
+ "value": "\"mymgatoken\""
+ },
+ "run-cell-locate-tcp-server-test": {
+ "help": "Set to true to run tests over TCP as well as over UDP",
+ "value": 1
+ }
+ },
+ "target_overrides": {
+ "*": {
+ "lwip.ipv4-enabled": true,
+ "lwip.ipv6-enabled": false,
+ "lwip.ethernet-enabled": false,
+ "lwip.ppp-enabled": true,
+ "lwip.tcp-enabled": true,
+ "target.features_add": ["LWIP", "COMMON_PAL"],
+ "platform.stdio-convert-newlines": true,
+ "platform.stdio-baud-rate": 9600,
+ "platform.default-serial-baud-rate": 115200,
+ "lwip.debug-enabled": false,
+ "lwip.enable-ppp-trace": false,
+ "lwip.use-mbed-trace": false,
+ "mbed-trace.enable": 1
+ }
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/unit_tests/ftp/main.cpp Mon Jun 05 12:58:04 2017 +0000
@@ -0,0 +1,529 @@
+#include "mbed.h"
+#include "greentea-client/test_env.h"
+#include "unity.h"
+#include "utest.h"
+#include "UbloxATCellularInterfaceExt.h"
+#include "UDPSocket.h"
+#include "FEATURE_COMMON_PAL/nanostack-libservice/mbed-client-libservice/common_functions.h"
+#include "mbed_trace.h"
+#define TRACE_GROUP "TEST"
+
+using namespace utest::v1;
+
+// ----------------------------------------------------------------
+// COMPILE-TIME MACROS
+// ----------------------------------------------------------------
+
+// These macros can be overridden with an mbed_app.json file and
+// contents of the following form:
+//
+//{
+// "config": {
+// "apn": {
+// "value": "\"my_apn\""
+// },
+// "ftp-server": {
+// "value": "\"test.rebex.net\""
+// },
+// "ftp-username": {
+// "value": "\"demo\""
+// },
+// "ftp-password": {
+// "value": "\"password\""
+// },
+// "ftp-use-passive": {
+// "value": true
+// },
+// "ftp-server-supports-write": {
+// "value": false
+// },
+// "ftp-filename": {
+// "value": "\"readme.txt\""
+// },
+// "ftp-dirname": {
+// "value": "\"pub\""
+// }
+//}
+
+// The credentials of the SIM in the board.
+#ifndef MBED_CONF_APP_DEFAULT_PIN
+// Note: this is the PIN for the SIM with ICCID
+// 8944501104169548380.
+# define MBED_CONF_APP_DEFAULT_PIN "5134"
+#endif
+
+// Network credentials.
+#ifndef MBED_CONF_APP_APN
+# define MBED_CONF_APP_APN NULL
+#endif
+#ifndef MBED_CONF_APP_USERNAME
+# define MBED_CONF_APP_USERNAME NULL
+#endif
+#ifndef MBED_CONF_APP_PASSWORD
+# define MBED_CONF_APP_PASSWORD NULL
+#endif
+
+// FTP server name
+#ifndef MBED_CONF_APP_FTP_SERVER
+# error "Must define an FTP server name to run these tests"
+#endif
+
+// User name on the FTP server
+#ifndef MBED_CONF_APP_FTP_USERNAME
+# define MBED_CONF_APP_FTP_SERVER_USERNAME ""
+#endif
+
+// Password on the FTP server
+#ifndef MBED_CONF_APP_FTP_PASSWORD
+# define MBED_CONF_APP_FTP_SERVER_PASSWORD ""
+#endif
+
+// Whether to use SFTP or not
+#ifndef MBED_CONF_APP_FTP_SECURE
+# define MBED_CONF_APP_FTP_SECURE false
+#endif
+
+// Port to use on the remote server
+#ifndef MBED_CONF_APP_FTP_SERVER_PORT
+# if MBED_CONF_APP_FTP_SECURE
+# define MBED_CONF_APP_FTP_SERVER_PORT 22
+# else
+# define MBED_CONF_APP_FTP_SERVER_PORT 21
+# endif
+#endif
+
+// Whether to use passive or active mode
+// default to true as many servers/networks
+// require this
+#ifndef MBED_CONF_APP_FTP_USE_PASSIVE
+# define MBED_CONF_APP_FTP_USE_PASSIVE true
+#endif
+
+// Whether the server supports FTP write operations
+#ifndef MBED_CONF_APP_FTP_SERVER_SUPPORTS_WRITE
+# define MBED_CONF_APP_FTP_SERVER_SUPPORTS_WRITE false
+#endif
+
+#if MBED_CONF_APP_FTP_SERVER_SUPPORTS_WRITE
+// The name of the file to PUT, GET and then delete
+# ifndef MBED_CONF_APP_FTP_FILENAME
+# define MBED_CONF_APP_FTP_FILENAME "test_file_delete_me"
+# endif
+// The name of the directory to create, CD to and then remove
+# ifndef MBED_CONF_APP_FTP_DIRNAME
+# define MBED_CONF_APP_FTP_DIRNAME "test_dir_delete_me"
+# endif
+#else
+// The name of the file to GET
+# ifndef MBED_CONF_APP_FTP_FILENAME
+# error "Must define the name of a file you know exists on the FTP server"
+# endif
+// The name of the directory to CD to
+# ifndef MBED_CONF_APP_FTP_DIRNAME
+# error "Must define the name of a directory you know exists on the FTP server"
+# endif
+#endif
+
+// The size of file when testing PUT/GET
+#ifndef MBED_CONF_APP_FTP_FILE_SIZE
+# define MBED_CONF_APP_FTP_FILE_SIZE 42000
+#endif
+
+// ----------------------------------------------------------------
+// PRIVATE VARIABLES
+// ----------------------------------------------------------------
+
+// Lock for debug prints
+static Mutex mtx;
+
+// An instance of the cellular interface
+static UbloxATCellularInterfaceExt *pDriver =
+ new UbloxATCellularInterfaceExt(MDMTXD, MDMRXD,
+ MBED_CONF_UBLOX_CELL_BAUD_RATE,
+ true);
+// A buffer for general use
+static char buf[MBED_CONF_APP_FTP_FILE_SIZE];
+
+// ----------------------------------------------------------------
+// PRIVATE FUNCTIONS
+// ----------------------------------------------------------------
+
+// Locks for debug prints
+static void lock()
+{
+ mtx.lock();
+}
+
+static void unlock()
+{
+ mtx.unlock();
+}
+
+
+// Write a file to the module's file system with known contents
+void createFile(const char * filename) {
+
+ for (unsigned int x = 0; x < sizeof (buf); x++) {
+ buf[x] = (char) x;
+ }
+
+ TEST_ASSERT(pDriver->writeFile(filename, buf, sizeof (buf)) == sizeof (buf));
+ tr_debug("%d bytes written to file \"%s\"", sizeof (buf), filename);
+}
+
+// Read a file back from the module's file system and check the contents
+void checkFile(const char * filename) {
+ memset(buf, 0, sizeof (buf));
+
+ int x = pDriver->readFile(filename, buf, sizeof (buf));
+ tr_debug ("File is %d bytes big", x);
+ TEST_ASSERT(x == sizeof (buf));
+
+ tr_debug("%d bytes read from file \"%s\"", sizeof (buf), filename);
+
+ for (unsigned int x = 0; x < sizeof (buf); x++) {
+ TEST_ASSERT(buf[x] == (char) x);
+ }
+}
+
+// ----------------------------------------------------------------
+// TESTS
+// ----------------------------------------------------------------
+
+// Test the setting up of parameters, connection and login to an FTP session
+void test_ftp_login() {
+ SocketAddress address;
+ char portString[10];
+
+ sprintf(portString, "%d", MBED_CONF_APP_FTP_SERVER_PORT);
+
+ TEST_ASSERT(pDriver->init(MBED_CONF_APP_DEFAULT_PIN));
+
+ // Reset parameters to default to begin with
+ TEST_ASSERT(pDriver->ftpResetPar());
+
+ // Set a timeout for FTP commands
+ TEST_ASSERT(pDriver->ftpSetTimeout(60000));
+
+ // Set up the FTP server parameters
+ TEST_ASSERT(pDriver->ftpSetPar(UbloxATCellularInterfaceExt::FTP_SERVER_NAME,
+ MBED_CONF_APP_FTP_SERVER));
+ TEST_ASSERT(pDriver->ftpSetPar(UbloxATCellularInterfaceExt::FTP_SERVER_PORT,
+ portString));
+ TEST_ASSERT(pDriver->ftpSetPar(UbloxATCellularInterfaceExt::FTP_USER_NAME,
+ MBED_CONF_APP_FTP_USERNAME));
+ TEST_ASSERT(pDriver->ftpSetPar(UbloxATCellularInterfaceExt::FTP_PASSWORD,
+ MBED_CONF_APP_FTP_PASSWORD));
+#ifdef MBED_CONF_APP_FTP_ACCOUNT
+ TEST_ASSERT(pDriver->ftpSetPar(UbloxATCellularInterfaceExt::FTP_ACCOUNT,
+ MBED_CONF_APP_FTP_ACCOUNT));
+#endif
+#if MBED_CONF_APP_FTP_SECURE
+ TEST_ASSERT(pDriver->ftpSetPar(UbloxATCellularInterfaceExt::FTP_SECURE, "1"));
+#endif
+#if MBED_CONF_APP_FTP_USE_PASSIVE
+ TEST_ASSERT(pDriver->ftpSetPar(UbloxATCellularInterfaceExt::FTP_MODE, "1"));
+#endif
+
+ // Now connect to the network
+ TEST_ASSERT(pDriver->connect(MBED_CONF_APP_DEFAULT_PIN, MBED_CONF_APP_APN,
+ MBED_CONF_APP_USERNAME, MBED_CONF_APP_PASSWORD) == 0);
+
+ // Get the server IP address, purely to make sure it's there
+ TEST_ASSERT(pDriver->gethostbyname(MBED_CONF_APP_FTP_SERVER, &address) == 0);
+ tr_debug ("Using FTP \"%s\", which is at %s", MBED_CONF_APP_FTP_SERVER,
+ address.get_ip_address());
+
+ // Log into the FTP server
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_LOGIN) == NULL);
+}
+
+// Test FTP directory listing
+void test_ftp_dir() {
+ // Get a directory listing
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_LS,
+ NULL, NULL, 0, buf, sizeof (buf)) == NULL);
+ tr_debug("Listing:\n%s", buf);
+
+ // The file we will GET should appear in the directory listing
+ TEST_ASSERT(strstr(buf, MBED_CONF_APP_FTP_FILENAME) > NULL);
+ // As should the directory name we will change to
+ TEST_ASSERT(strstr(buf, MBED_CONF_APP_FTP_DIRNAME) > NULL);
+}
+
+// Test FTP file information
+void test_ftp_fileinfo() {
+ // Get the info
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_FILE_INFO,
+ MBED_CONF_APP_FTP_FILENAME, NULL, 0,
+ buf, sizeof (buf)) == NULL);
+ tr_debug("File info:\n%s", buf);
+
+ // The file info string should at least include the file name
+ TEST_ASSERT(strstr(buf, MBED_CONF_APP_FTP_FILENAME) > NULL);
+}
+
+#if MBED_CONF_APP_FTP_SERVER_SUPPORTS_WRITE
+
+// In case a previous test failed half way, do some cleaning up first
+// Note: don't check return values as these operations will fail
+// if there's nothing to clean up
+void test_ftp_write_cleanup() {
+ pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_DELETE_FILE,
+ MBED_CONF_APP_FTP_FILENAME);
+ pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_DELETE_FILE,
+ MBED_CONF_APP_FTP_FILENAME "_2");
+ pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_RMDIR,
+ MBED_CONF_APP_FTP_DIRNAME);
+ pDriver->delFile(MBED_CONF_APP_FTP_FILENAME);
+ pDriver->delFile(MBED_CONF_APP_FTP_FILENAME "_1");
+}
+
+// Test FTP put and then get
+void test_ftp_put_get() {
+ // Create the file
+ createFile(MBED_CONF_APP_FTP_FILENAME);
+
+ // Put the file
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_PUT_FILE,
+ MBED_CONF_APP_FTP_FILENAME) == NULL);
+
+ // Get the file
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_GET_FILE,
+ MBED_CONF_APP_FTP_FILENAME,
+ MBED_CONF_APP_FTP_FILENAME "_1") == NULL);
+
+ // Check that it is the same as we sent
+ checkFile(MBED_CONF_APP_FTP_FILENAME "_1");
+}
+
+// Test FTP rename file
+void test_ftp_rename() {
+ // Get a directory listing
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_LS,
+ NULL, NULL, 0, buf, sizeof (buf)) == NULL);
+ tr_debug("Listing:\n%s", buf);
+
+ // The file we are renaming to should not appear
+ TEST_ASSERT(strstr(buf, MBED_CONF_APP_FTP_FILENAME "_2") == NULL);
+
+ // Rename the file
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_RENAME_FILE,
+ MBED_CONF_APP_FTP_FILENAME,
+ MBED_CONF_APP_FTP_FILENAME "_2") == NULL);
+
+ // Get a directory listing
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_LS,
+ NULL, NULL, 0, buf, sizeof (buf)) == NULL);
+ tr_debug("Listing:\n%s", buf);
+
+ // The new file should now exist
+ TEST_ASSERT(strstr(buf, MBED_CONF_APP_FTP_FILENAME "_2") > NULL);
+
+}
+
+// Test FTP delete file
+void test_ftp_delete() {
+ // Get a directory listing
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_LS,
+ NULL, NULL, 0, buf, sizeof (buf)) == NULL);
+ tr_debug("Listing:\n%s", buf);
+
+ // The file we are to delete should appear in the list
+ TEST_ASSERT(strstr(buf, MBED_CONF_APP_FTP_FILENAME "_2") > NULL);
+
+ // Delete the file
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_DELETE_FILE,
+ MBED_CONF_APP_FTP_FILENAME "_2") == NULL);
+
+ // Get a directory listing
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_LS,
+ NULL, NULL, 0, buf, sizeof (buf)) == NULL);
+ tr_debug("Listing:\n%s", buf);
+
+ // The file we deleted should no longer appear in the list
+ TEST_ASSERT(strstr(buf, MBED_CONF_APP_FTP_FILENAME "_2") == NULL);
+}
+
+// Test FTP MKDIR
+void test_ftp_mkdir() {
+ // Get a directory listing
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_LS,
+ NULL, NULL, 0, buf, sizeof (buf)) == NULL);
+ tr_debug("Listing:\n%s", buf);
+
+ // The directory we are to create should not appear in the list
+ TEST_ASSERT(strstr(buf, MBED_CONF_APP_FTP_DIRNAME) == NULL);
+
+ // Create the directory
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_MKDIR,
+ MBED_CONF_APP_FTP_DIRNAME) == NULL);
+
+ // Get a directory listing
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_LS,
+ NULL, NULL, 0, buf, sizeof (buf)) == NULL);
+ tr_debug("Listing:\n%s", buf);
+
+ // The directory we created should now appear in the list
+ TEST_ASSERT(strstr(buf, MBED_CONF_APP_FTP_DIRNAME) > NULL);
+}
+
+// Test FTP RMDIR
+void test_ftp_rmdir() {
+ // Get a directory listing
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_LS,
+ NULL, NULL, 0, buf, sizeof (buf)) == NULL);
+ tr_debug("Listing:\n%s", buf);
+
+ // The directory we are to remove should appear in the list
+ TEST_ASSERT(strstr(buf, MBED_CONF_APP_FTP_DIRNAME) > NULL);
+
+ // Remove the directory
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_RMDIR,
+ MBED_CONF_APP_FTP_DIRNAME) == NULL);
+
+ // Get a directory listing
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_LS,
+ NULL, NULL, 0, buf, sizeof (buf)) == NULL);
+ tr_debug("Listing:\n%s", buf);
+
+ // The directory we removed should no longer appear in the list
+ TEST_ASSERT(strstr(buf, MBED_CONF_APP_FTP_DIRNAME) == NULL);
+}
+
+#endif // MBED_CONF_APP_FTP_SERVER_SUPPORTS_WRITE
+
+// Test FTP get
+void test_ftp_get() {
+ // Make sure that the 'get' filename we're going to use
+ // isn't already here (but don't assert on this one
+ // as, if the file isn't there, we will get an error)
+ pDriver->delFile(MBED_CONF_APP_FTP_FILENAME);
+
+ // Get the file
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_GET_FILE,
+ MBED_CONF_APP_FTP_FILENAME) == NULL);
+
+ // Check that it has arrived
+ TEST_ASSERT(pDriver->fileSize(MBED_CONF_APP_FTP_FILENAME) > 0);
+}
+
+// Test FTP change directory
+void test_ftp_cd() {
+ // Get a directory listing
+ *buf = 0;
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_LS,
+ NULL, NULL, 0, buf, sizeof (buf)) == NULL);
+
+ tr_debug("Listing:\n%s", buf);
+
+ // The listing should include the directory name we are going to move to
+ TEST_ASSERT(strstr(buf, MBED_CONF_APP_FTP_DIRNAME) > NULL);
+
+ // Change directories
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_CD,
+ MBED_CONF_APP_FTP_DIRNAME) == NULL);
+ // Get a directory listing
+ *buf = 0;
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_LS,
+ NULL, NULL, 0, buf, sizeof (buf)) == NULL);
+ tr_debug("Listing:\n%s", buf);
+
+ // The listing should no longer include the directory name we have moved
+ TEST_ASSERT(strstr(buf, MBED_CONF_APP_FTP_DIRNAME) == NULL);
+
+ // Go back to where we were
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_CD, "..")
+ == NULL);
+
+ // Get a directory listing
+ *buf = 0;
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_LS,
+ NULL, NULL, 0, buf, sizeof (buf)) == NULL);
+ tr_debug("Listing:\n%s", buf);
+
+ // The listing should include the directory name we went to once more
+ TEST_ASSERT(strstr(buf, MBED_CONF_APP_FTP_DIRNAME) > NULL);
+}
+
+#ifdef MBED_CONF_APP_FTP_FOTA_FILENAME
+// Test FTP FOTA
+// TODO: test not tested as I don't have a module that supports the FTP FOTA operation
+void test_ftp_fota() {
+ *buf = 0;
+ // Do FOTA on a file
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_FOTA_FILE,
+ MBED_CONF_APP_FTP_FOTA_FILENAME, NULL,
+ 0, buf, sizeof (buf)) == NULL);
+ tr_debug("MD5 sum: %s\n", buf);
+
+ // Check that the 128 bit MD5 sum is now there
+ TEST_ASSERT(strlen (buf) == 32);
+}
+#endif
+
+// Test logout and disconnect from an FTP session
+void test_ftp_logout() {
+ // Log out from the FTP server
+ TEST_ASSERT(pDriver->ftpCommand(UbloxATCellularInterfaceExt::FTP_LOGOUT) == NULL);
+
+ TEST_ASSERT(pDriver->disconnect() == 0);
+
+ // Wait for printfs to leave the building or the test result string gets messed up
+ wait_ms(500);
+}
+
+// ----------------------------------------------------------------
+// TEST ENVIRONMENT
+// ----------------------------------------------------------------
+
+// Setup the test environment
+utest::v1::status_t test_setup(const size_t number_of_cases) {
+ // Setup Greentea with a timeout
+ GREENTEA_SETUP(540, "default_auto");
+ return verbose_test_setup_handler(number_of_cases);
+}
+
+// Test cases
+Case cases[] = {
+ Case("FTP log in", test_ftp_login),
+#if MBED_CONF_APP_FTP_SERVER_SUPPORTS_WRITE
+ Case("Clean-up", test_ftp_write_cleanup),
+ Case("FTP put and get", test_ftp_put_get),
+ Case("FTP file info", test_ftp_fileinfo),
+ Case("FTP rename", test_ftp_rename),
+ Case("FTP make directory", test_ftp_mkdir),
+ Case("FTP directory list", test_ftp_dir),
+ Case("FTP delete", test_ftp_delete),
+ Case("FTP change directory", test_ftp_cd),
+ Case("FTP delete directory", test_ftp_rmdir),
+#else
+ Case("FTP directory list", test_ftp_dir),
+ Case("FTP file info", test_ftp_fileinfo),
+ Case("FTP get", test_ftp_get),
+ Case("FTP change directory", test_ftp_cd),
+#endif
+#ifdef MBED_CONF_APP_FTP_FOTA_FILENAME
+ Case("FTP FOTA", test_ftp_fota),
+#endif
+ Case("FTP log out", test_ftp_logout)
+};
+
+Specification specification(test_setup, cases);
+
+// ----------------------------------------------------------------
+// MAIN
+// ----------------------------------------------------------------
+
+int main() {
+ mbed_trace_init();
+
+ mbed_trace_mutex_wait_function_set(lock);
+ mbed_trace_mutex_release_function_set(unlock);
+
+ // Run tests
+ return !Harness::run(specification);
+}
+
+// End Of File
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/unit_tests/ftp/template_mbed_app.txt Mon Jun 05 12:58:04 2017 +0000
@@ -0,0 +1,53 @@
+{
+ "config": {
+ "buffer-size": { "value": 0 },
+ "platform": { "help": "Options: UBLOX, MTS_DRAGONFLY",
+ "value": "UBLOX"},
+ "ftp-server": {
+ "help": "An FTP server to use when running the FTP tests",
+ "value": "\"test.rebex.net\""
+ },
+ "ftp-username": {
+ "help": "The user name for the FTP server account",
+ "value": "\"demo\""
+ },
+ "ftp-password": {
+ "help": "The password for the FTP server account",
+ "value": "\"password\""
+ },
+ "ftp-use-passive": {
+ "help": "Set to true to use passive mode, otherwise false (defaults to true, since this is needed for most cases)",
+ "value": true
+ },
+ "ftp-server-supports-write": {
+ "help": "Set to true if the FTP server supports PUT, rename, MKDIR and delete, otherwise set to false",
+ "value": false
+ },
+ "ftp-filename": {
+ "help": "A filename to use during FTP tests. This file must already exist on the server if the server does not support write",
+ "value": "\"readme.txt\""
+ },
+ "ftp-dirname": {
+ "help": "A directory name to yse during FTP tests. This directory must already exist on the server if the server does not support write",
+ "value": "\"pub\""
+ }
+ },
+ "target_overrides": {
+ "*": {
+ "lwip.ipv4-enabled": true,
+ "lwip.ipv6-enabled": false,
+ "lwip.ethernet-enabled": false,
+ "lwip.ppp-enabled": true,
+ "lwip.tcp-enabled": true,
+ "target.features_add": ["LWIP", "COMMON_PAL"],
+ "platform.stdio-convert-newlines": true,
+ "platform.stdio-baud-rate": 9600,
+ "platform.default-serial-baud-rate": 115200,
+ "lwip.debug-enabled": false,
+ "lwip.enable-ppp-trace": false,
+ "lwip.use-mbed-trace": false,
+ "mbed-trace.enable": 1
+ }
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/unit_tests/http/main.cpp Mon Jun 05 12:58:04 2017 +0000
@@ -0,0 +1,335 @@
+#include "mbed.h"
+#include "greentea-client/test_env.h"
+#include "unity.h"
+#include "utest.h"
+#include "UbloxATCellularInterfaceExt.h"
+#include "UDPSocket.h"
+#include "FEATURE_COMMON_PAL/nanostack-libservice/mbed-client-libservice/common_functions.h"
+#include "mbed_trace.h"
+#define TRACE_GROUP "TEST"
+
+using namespace utest::v1;
+
+// ----------------------------------------------------------------
+// COMPILE-TIME MACROS
+// ----------------------------------------------------------------
+
+// These macros can be overridden with an mbed_app.json file and
+// contents of the following form:
+//
+//{
+// "config": {
+// "apn": {
+// "value": "\"my_apn\""
+// }
+//}
+
+// The credentials of the SIM in the board.
+#ifndef MBED_CONF_APP_DEFAULT_PIN
+// Note: this is the PIN for the SIM with ICCID
+// 8944501104169548380.
+# define MBED_CONF_APP_DEFAULT_PIN "5134"
+#endif
+
+// Network credentials.
+#ifndef MBED_CONF_APP_APN
+# define MBED_CONF_APP_APN NULL
+#endif
+#ifndef MBED_CONF_APP_USERNAME
+# define MBED_CONF_APP_USERNAME NULL
+#endif
+#ifndef MBED_CONF_APP_PASSWORD
+# define MBED_CONF_APP_PASSWORD NULL
+#endif
+
+// The time to wait for a HTTP command to complete
+#define HTTP_TIMEOUT 10000
+
+// The HTTP echo server, as described in the
+// first answer here:
+// http://stackoverflow.com/questions/5725430/http-test-server-that-accepts-get-post-calls
+// !!! IMPORTANT: this test relies on that server behaving in the same way forever !!!
+#define HTTP_ECHO_SERVER "httpbin.org"
+
+// The size of the test file
+#define TEST_FILE_SIZE 100
+
+// The maximum number of HTTP profiles
+#define MAX_PROFILES 4
+
+// ----------------------------------------------------------------
+// PRIVATE VARIABLES
+// ----------------------------------------------------------------
+
+// Lock for debug prints
+static Mutex mtx;
+
+// An instance of the cellular interface
+static UbloxATCellularInterfaceExt *pDriver =
+ new UbloxATCellularInterfaceExt(MDMTXD, MDMRXD,
+ MBED_CONF_UBLOX_CELL_BAUD_RATE,
+ true);
+// A few buffers for general use
+static char buf[1024];
+static char buf1[sizeof(buf)];
+
+// ----------------------------------------------------------------
+// PRIVATE FUNCTIONS
+// ----------------------------------------------------------------
+
+// Locks for debug prints
+static void lock()
+{
+ mtx.lock();
+}
+
+static void unlock()
+{
+ mtx.unlock();
+}
+
+// ----------------------------------------------------------------
+// TESTS
+// ----------------------------------------------------------------
+
+// Test HTTP commands
+void test_http_cmd() {
+ int profile;
+ char * pData;
+
+ TEST_ASSERT(pDriver->connect(MBED_CONF_APP_DEFAULT_PIN, MBED_CONF_APP_APN,
+ MBED_CONF_APP_USERNAME, MBED_CONF_APP_PASSWORD) == 0);
+
+ profile = pDriver->httpAllocProfile();
+ TEST_ASSERT(profile >= 0);
+
+ pDriver->httpSetTimeout(profile, HTTP_TIMEOUT);
+
+ // Set up the server to talk to
+ TEST_ASSERT(pDriver->httpSetPar(profile, UbloxATCellularInterfaceExt::HTTP_SERVER_NAME, HTTP_ECHO_SERVER));
+
+ // Check HTTP head request
+ memset(buf, 0, sizeof (buf));
+ TEST_ASSERT(pDriver->httpCommand(profile, UbloxATCellularInterfaceExt::HTTP_HEAD,
+ "/headers",
+ NULL, NULL, 0, NULL,
+ buf, sizeof (buf)) == NULL);
+ tr_debug("Received: %s", buf);
+ TEST_ASSERT(strstr(buf, "Content-Length:") != NULL);
+
+ // Check HTTP get request
+ memset(buf, 0, sizeof (buf));
+ TEST_ASSERT(pDriver->httpCommand(profile, UbloxATCellularInterfaceExt::HTTP_GET,
+ "/get",
+ NULL, NULL, 0, NULL,
+ buf, sizeof (buf)) == NULL);
+ tr_debug("Received: %s", buf);
+ TEST_ASSERT(strstr(buf, "\"http://httpbin.org/get\"") != NULL);
+
+ // Check HTTP delete request
+ memset(buf, 0, sizeof (buf));
+ TEST_ASSERT(pDriver->httpCommand(profile, UbloxATCellularInterfaceExt::HTTP_DELETE,
+ "/delete",
+ NULL, NULL, 0, NULL,
+ buf, sizeof (buf)) == NULL);
+ tr_debug("Received: %s", buf);
+ TEST_ASSERT(strstr(buf, "\"http://httpbin.org/delete\"") != NULL);
+
+ // Check HTTP put request (this will fail as the echo server doesn't support it)
+ memset(buf, 0, sizeof (buf));
+ TEST_ASSERT(pDriver->httpCommand(profile, UbloxATCellularInterfaceExt::HTTP_PUT,
+ "/put",
+ NULL, NULL, 0, NULL,
+ buf, sizeof (buf)) != NULL);
+
+ // Check HTTP post request with data
+ memset(buf, 0, sizeof (buf));
+ TEST_ASSERT(pDriver->httpCommand(profile, UbloxATCellularInterfaceExt::HTTP_POST_DATA,
+ "/post",
+ NULL, NULL, 0, NULL,
+ buf, sizeof (buf)) == NULL);
+ tr_debug("Received: %s", buf);
+ TEST_ASSERT(strstr(buf, "\"http://httpbin.org/post\"") != NULL);
+
+ // Check HTTP post request with a file, also checking that writing the response
+ // to a named file works
+ for (int x = 0; x < TEST_FILE_SIZE; x++) {
+ buf[x] = (x % 10) + 0x30;
+ }
+ pDriver->delFile("post_test.txt");
+ TEST_ASSERT(pDriver->writeFile("post_test.txt", buf, TEST_FILE_SIZE) == TEST_FILE_SIZE);
+
+ // This may fail if rsp.txt doesn't happen to be sitting around from a previous run
+ // so don't check the return value
+ pDriver->delFile("rsp.txt");
+
+ memset(buf, 0, sizeof (buf));
+ TEST_ASSERT(pDriver->httpCommand(profile, UbloxATCellularInterfaceExt::HTTP_POST_FILE,
+ "/post",
+ "rsp.txt", "post_test.txt",
+ UbloxATCellularInterfaceExt::HTTP_CONTENT_TEXT, NULL,
+ buf, sizeof (buf)) == NULL);
+ tr_debug("Received: %s", buf);
+ // Find the data in the response and check it
+ pData = strstr(buf, "\"data\": \"");
+ TEST_ASSERT(pData != NULL);
+ pData += 9;
+ for (int x = 0; x < TEST_FILE_SIZE; x++) {
+ TEST_ASSERT(*(pData + x) == (x % 10) + 0x30);
+ }
+
+ // Also check that rsp.txt exists and is the same as buf
+ pDriver->readFile("rsp.txt", buf1, sizeof (buf1));
+ memcmp(buf1, buf, sizeof (buf1));
+ TEST_ASSERT(pDriver->delFile("rsp.txt"));
+ TEST_ASSERT(!pDriver->delFile("rsp.txt")); // Should fail
+
+ TEST_ASSERT(pDriver->httpFreeProfile(profile));
+ TEST_ASSERT(pDriver->disconnect() == 0);
+ // Wait for printfs to leave the building or the test result string gets messed up
+ wait_ms(500);
+}
+
+// Test HTTP with TLS
+void test_http_tls() {
+ int profile;
+ SocketAddress address;
+
+ TEST_ASSERT(pDriver->connect(MBED_CONF_APP_DEFAULT_PIN, MBED_CONF_APP_APN,
+ MBED_CONF_APP_USERNAME, MBED_CONF_APP_PASSWORD) == 0);
+
+ profile = pDriver->httpAllocProfile();
+ TEST_ASSERT(profile >= 0);
+
+ pDriver->httpSetTimeout(profile, HTTP_TIMEOUT);
+
+ // Set up the server to talk to and TLS, using the IP address this time just for variety
+ TEST_ASSERT(pDriver->gethostbyname("amazon.com", &address) == 0);
+ TEST_ASSERT(pDriver->httpSetPar(profile, UbloxATCellularInterfaceExt::HTTP_IP_ADDRESS, address.get_ip_address()));
+ TEST_ASSERT(pDriver->httpSetPar(profile, UbloxATCellularInterfaceExt::HTTP_SECURE, "1"));
+
+ // Check HTTP get request
+ memset(buf, 0, sizeof (buf));
+ TEST_ASSERT(pDriver->httpCommand(profile, UbloxATCellularInterfaceExt::HTTP_GET,
+ "/",
+ NULL, NULL, 0, NULL,
+ buf, sizeof (buf)) == NULL);
+ tr_debug("Received: %s", buf);
+ // This is what amazon.com returns if TLS is set
+ TEST_ASSERT(strstr(buf, "302 MovedTemporarily") != NULL);
+
+ // Reset the profile and check that this now fails
+ TEST_ASSERT(pDriver->httpResetProfile(profile));
+ TEST_ASSERT(pDriver->httpSetPar(profile, UbloxATCellularInterfaceExt::HTTP_IP_ADDRESS, address.get_ip_address()));
+ memset(buf, 0, sizeof (buf));
+ TEST_ASSERT(pDriver->httpCommand(profile, UbloxATCellularInterfaceExt::HTTP_GET,
+ "/",
+ NULL, NULL, 0, NULL,
+ buf, sizeof (buf)) == NULL);
+ tr_debug("Received: %s", buf);
+ // This is what amazon.com returns if TLS is NOT set
+ TEST_ASSERT(strstr(buf, "301 Moved Permanently") != NULL);
+
+ TEST_ASSERT(pDriver->httpFreeProfile(profile));
+ TEST_ASSERT(pDriver->disconnect() == 0);
+ // Wait for printfs to leave the building or the test result string gets messed up
+ wait_ms(500);
+}
+
+// Allocate max profiles
+void test_alloc_profiles() {
+ int profiles[MAX_PROFILES];
+
+ TEST_ASSERT(pDriver->connect(MBED_CONF_APP_DEFAULT_PIN, MBED_CONF_APP_APN,
+ MBED_CONF_APP_USERNAME, MBED_CONF_APP_PASSWORD) == 0);
+
+ // Allocate first profile and use it
+ profiles[0] = pDriver->httpAllocProfile();
+ TEST_ASSERT(profiles[0] >= 0);
+ TEST_ASSERT(pDriver->httpSetPar(profiles[0], UbloxATCellularInterfaceExt::HTTP_SERVER_NAME, "developer.mbed.org"));
+
+ // Check HTTP get request
+ memset(buf, 0, sizeof (buf));
+ TEST_ASSERT(pDriver->httpCommand(profiles[0], UbloxATCellularInterfaceExt::HTTP_GET,
+ "/media/uploads/mbed_official/hello.txt",
+ NULL, NULL, 0, NULL,
+ buf, sizeof (buf)) == NULL);
+ tr_debug("Received: %s", buf);
+ TEST_ASSERT(strstr(buf, "Hello world!") != NULL);
+
+ // Check that we stop being able to get profiles at the max number
+ for (int x = 1; x < sizeof (profiles) / sizeof (profiles[0]); x++) {
+ profiles[x] = pDriver->httpAllocProfile();
+ TEST_ASSERT(profiles[0] >= 0);
+ }
+ TEST_ASSERT(pDriver->httpAllocProfile() < 0);
+
+ // Now use the last one and check that it doesn't affect the first one
+ TEST_ASSERT(pDriver->httpSetPar(profiles[sizeof (profiles) / sizeof (profiles[0]) - 1],
+ UbloxATCellularInterfaceExt::HTTP_SERVER_NAME, HTTP_ECHO_SERVER));
+
+ // Check HTTP head request on last profile
+ memset(buf, 0, sizeof (buf));
+ TEST_ASSERT(pDriver->httpCommand(profiles[sizeof (profiles) / sizeof (profiles[0]) - 1],
+ UbloxATCellularInterfaceExt::HTTP_HEAD,
+ "/headers",
+ NULL, NULL, 0, NULL,
+ buf, sizeof (buf)) == NULL);
+ tr_debug("Received: %s", buf);
+ TEST_ASSERT(strstr(buf, "Content-Length:") != NULL);
+
+ // Check HTTP get request on first profile once more
+ memset(buf, 0, sizeof (buf));
+ TEST_ASSERT(pDriver->httpCommand(profiles[0], UbloxATCellularInterfaceExt::HTTP_GET,
+ "/media/uploads/mbed_official/hello.txt",
+ NULL, NULL, 0, NULL,
+ buf, sizeof (buf)) == NULL);
+ tr_debug("Received: %s", buf);
+ TEST_ASSERT(strstr(buf, "Hello world!") != NULL);
+
+ // Free the profiles again
+ for (int x = 0; x < sizeof (profiles) / sizeof (profiles[0]); x++) {
+ TEST_ASSERT(pDriver->httpFreeProfile(profiles[x]));
+ }
+
+ TEST_ASSERT(pDriver->disconnect() == 0);
+ // Wait for printfs to leave the building or the test result string gets messed up
+ wait_ms(500);
+}
+
+// ----------------------------------------------------------------
+// TEST ENVIRONMENT
+// ----------------------------------------------------------------
+
+// Setup the test environment
+utest::v1::status_t test_setup(const size_t number_of_cases) {
+ // Setup Greentea with a timeout
+ GREENTEA_SETUP(540, "default_auto");
+ return verbose_test_setup_handler(number_of_cases);
+}
+
+// Test cases
+Case cases[] = {
+ Case("HTTP commands", test_http_cmd),
+ Case("HTTP with TLS", test_http_tls),
+ Case("Alloc max profiles", test_alloc_profiles)
+};
+
+Specification specification(test_setup, cases);
+
+// ----------------------------------------------------------------
+// MAIN
+// ----------------------------------------------------------------
+
+int main() {
+ mbed_trace_init();
+
+ mbed_trace_mutex_wait_function_set(lock);
+ mbed_trace_mutex_release_function_set(unlock);
+
+ // Run tests
+ return !Harness::run(specification);
+}
+
+// End Of File
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/UbloxATCellularInterfaceExt.cpp Mon Jun 05 12:58:04 2017 +0000
@@ -0,0 +1,984 @@
+/* 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"
+#if defined(FEATURE_COMMON_PAL)
+#include "mbed_trace.h"
+#define TRACE_GROUP "UCAD"
+#else
+#define debug_if(_debug_trace_on, ...) (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
+
+/**********************************************************************
+ * 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) {
+ // +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_mon -= 1;
+ _loc[0].time.tm_wday = 0;
+ _loc[0].time.tm_yday = 0;
+ _loc[0].validData = true;
+ _locExpPos=1;
+ _locRcvPos++;
+ // +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_mon -= 1;
+ _loc[a].time.tm_wday = 0;
+ _loc[a].time.tm_yday = 0;
+ _loc[a].validData = true;
+ _locRcvPos++;
+ }
+ //+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_mon -= 1;
+ _loc[a].time.tm_wday = 0;
+ _loc[a].time.tm_yday = 0;
+ _loc[a].validData = true;
+ _locRcvPos++;
+ }
+ }
+ }
+}
+
+/**********************************************************************
+ * PUBLIC METHODS: GENERAL
+ **********************************************************************/
+
+// Constructor.
+UbloxATCellularInterfaceExt::UbloxATCellularInterfaceExt(PinName tx,
+ PinName rx,
+ int baud,
+ bool debugOn):
+ UbloxATCellularInterface(tx, rx, baud, debugOn)
+{
+ // 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)
+{
+ 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;
+
+ 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) {
+ // HTTP command successfully executed
+ if (readFile(rspFile, buf, len) >= 0) {
+ success = true;
+ }
+ } 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.
+bool UbloxATCellularInterfaceExt::ftpResetPar()
+{
+ bool success = true;
+ LOCK();
+
+ debug_if(_debug_trace_on, "ftpResetPar()\n");
+ for (int x = 0; success && (x < NUM_FTP_OP_CODES); x++) {
+ success = _at->send("AT+UFTP=%d", x) && _at->recv("OK");
+ }
+
+ UNLOCK();
+ return success;
+}
+
+// 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,
+ int offset,
+ char* buf, int len)
+{
+ 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");
+ 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
+ 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 the URC
+ }
+
+ 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
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/UbloxATCellularInterfaceExt.h Mon Jun 05 12:58:04 2017 +0000
@@ -0,0 +1,605 @@
+/* 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 _UBLOX_AT_CELLULAR_INTERFACE_EXT_
+#define _UBLOX_AT_CELLULAR_INTERFACE_EXT_
+
+#include "ublox_modem_driver/UbloxAtCellularInterface.h"
+#include "UbloxCellularDriverGen.h"
+
+/**UbloxATCellularInterfaceExt class.
+ *
+ * This interface extends the UbloxATCellularInterface to
+ * include other features that use the IP stack on board the
+ * cellular modem: HTTP, FTP and Cell Locate.
+ *
+ * Note: the UbloxCellularGeneric class is required because
+ * reading a large HTTP response is performed via a modem
+ * file system call and the UbloxCellularGeneric class is
+ * where modem file system support is provided.
+ */
+class UbloxATCellularInterfaceExt : public UbloxATCellularInterface, public UbloxCellularDriverGen {
+
+public:
+ /** Constructor.
+ *
+ * @param tx the UART TX data pin to which the modem is attached.
+ * @param rx the UART RX data pin to which the modem is attached.
+ * @param baud the UART baud rate.
+ * @param debugOn true to switch AT interface debug on, otherwise false.
+ */
+ UbloxATCellularInterfaceExt(PinName tx = MDMTXD,
+ PinName rx = MDMRXD,
+ int baud = MBED_CONF_UBLOX_CELL_BAUD_RATE,
+ bool debugOn = false);
+
+ /* Destructor.
+ */
+ virtual ~UbloxATCellularInterfaceExt();
+
+ /**********************************************************************
+ * PUBLIC: General
+ **********************************************************************/
+
+ /** Infinite timeout.
+ */
+ #define TIMEOUT_BLOCKING -1
+
+ /** A struct containing an HTTP or FTP error class and code
+ */
+ typedef struct {
+ int eClass;
+ int eCode;
+ } Error;
+
+ /**********************************************************************
+ * PUBLIC: HTTP
+ **********************************************************************/
+
+ /** HTTP profile unused marker.
+ */
+ #define HTTP_PROF_UNUSED -1
+
+ /** HTTP configuration parameters (reference to HTTP control +UHTTP).
+ */
+ typedef enum {
+ HTTP_IP_ADDRESS = 0,
+ HTTP_SERVER_NAME = 1,
+ HTTP_USER_NAME = 2,
+ HTTP_PASSWORD = 3,
+ HTTP_AUTH_TYPE = 4,
+ HTTP_SERVER_PORT = 5,
+ HTTP_SECURE = 6
+ } HttpOpCode;
+
+ /** Type of HTTP Command.
+ */
+ typedef enum {
+ HTTP_HEAD = 0,
+ HTTP_GET = 1,
+ HTTP_DELETE = 2,
+ HTTP_PUT = 3,
+ HTTP_POST_FILE = 4,
+ HTTP_POST_DATA = 5
+ } HttpCmd;
+
+ /** HTTP content types.
+ */
+ typedef enum {
+ HTTP_CONTENT_URLENCODED = 0,
+ HTTP_CONTENT_TEXT = 1,
+ HTTP_CONTENT_OCTET_STREAM = 2,
+ HTTP_CONTENT_FORM_DATA = 3,
+ HTTP_CONTENT_JSON = 4,
+ HTTP_CONTENT_XML = 5,
+ HTTP_CONTENT_USER_DEFINED = 6
+ } HttpContentType;
+
+ /** Find a free HTTP profile.
+ *
+ * A profile will be blocking when first allocated.
+ *
+ * @return the profile or negative if none are available.
+ */
+ int httpAllocProfile();
+
+ /** Free the HTTP profile.
+ *
+ * @param profile the HTTP profile handle.
+ * @return true if successful, otherwise false.
+ */
+ bool httpFreeProfile(int profile);
+
+ /** Set the timeout for this profile.
+ *
+ * @param profile the HTTP profile handle.
+ * @param timeout -1 blocking, else non-blocking timeout in milliseconds.
+ * @return true if successful, otherwise false.
+ */
+ bool httpSetTimeout(int profile, int timeout);
+
+ /** Reset a HTTP profile back to defaults.
+ *
+ * This may be called if the state of a HTTP profile
+ * during parameter setting or exchange of HTTP commands
+ * has become confusing/unknown.
+ *
+ * @param httpProfile the HTTP profile to be reset.
+ * @return true if successful, false otherwise.
+ */
+ bool httpResetProfile(int httpProfile);
+
+ /** Set HTTP parameters.
+ *
+ * This should be called as many times as is necessary
+ * to set all the possible parameters (HttpOpCode).
+ *
+ * See section 28.1 of u-blox-ATCommands_Manual(UBX-13002752).pdf
+ * for full details. By example:
+ *
+ * httpOpCode httpInPar
+ * HTTP_IP_ADDRESS "145.33.18.10" (the target HTTP server IP address)
+ * HTTP_SERVER_NAME "www.myhttpserver.com" (the target HTTP server name)
+ * HTTP_USER_NAME "my_username"
+ * HTTP_PASSWORD "my_password"
+ * HTTP_AUTH_TYPE "0" for no authentication, "1" for username/password
+ * authentication (the default is 0)
+ * HTTP_SERVER_PORT "81" (default is port 80)
+ * HTTP_SECURE "0" for no security, "1" for TLS (the default is 0)
+ *
+ * @param httpProfile the HTTP profile identifier.
+ * @param httpOpCode the HTTP operation code.
+ * @param httpInPar the HTTP input parameter.
+ * @return true if successful, false otherwise.
+ */
+ bool httpSetPar(int httpProfile, HttpOpCode httpOpCode, const char * httpInPar);
+
+ /** Perform a HTTP command.
+ *
+ * See section 28.3 of u-blox-ATCommands_Manual(UBX-13002752).pdf
+ * for full details. By example, it works like this:
+ *
+ * httpCmd httpPath rspFile sendStr httpContentType httpCustomPar
+ * HEAD "path/file.html" NULL NULL 0 NULL
+ * GET "path/file.html" NULL NULL 0 NULL
+ * DELETE "path/file.html" NULL NULL 0 NULL
+ * PUT "path/file.html" NULL "myfile.txt" 0 to 6 Note 1
+ * POST_FILE "path/file.html" NULL "myfile.txt" 0 to 6 Note 1
+ * POST "path/file.html" NULL "hello there!" 0 to 6 Note 1
+ *
+ * Note 1: httpCustomPar is only applicable when httpContentType = HTTP_CONTENT_USER_DEFINED.
+ *
+ * The server to which this command is directed must have previously been
+ * set with a call to httpSetPar(). If the server requires TLS (i.e. "HTTPS"),
+ * then set that up with httpSetPar() also (HTTP_SECURE).
+ *
+ * rspFile may be left as NULL as the server response will be returned in buf.
+ * Alternatively, a rspFile may be given (e.g. "myresponse.txt") and this can
+ * later be read from the modem file system using readFile().
+ *
+ * @param httpProfile the HTTP profile identifier.
+ * @param httpCmd the HTTP command.
+ * @param httpPath the path of resource on the HTTP server.
+ * @param rspFile the local modem file where the server
+ * response will be stored, use NULL for
+ * don't care.
+ * @param sendStr the filename or string to be sent
+ * to the HTTP server with the command request.
+ * @param httpContentType the HTTP Content-Type identifier.
+ * @param httpCustomPar the parameter for a user defined HTTP Content-Type.
+ * @param buf the buffer to read into.
+ * @param len the size of the buffer to read into.
+ * @return NULL if successful, otherwise a pointer to
+ * a Error struct containing the error class and error
+ * code, see section Appendix A.B of
+ * u-blox-ATCommands_Manual(UBX-13002752).pdf for details.
+ */
+ Error * httpCommand(int httpProfile, HttpCmd httpCmd, const char* httpPath,
+ const char* rspFile, const char* sendStr,
+ int httpContentType, const char* httpCustomPar,
+ char* buf, int len);
+
+ /**********************************************************************
+ * PUBLIC: FTP
+ **********************************************************************/
+
+ /** FTP configuration parameters (reference to FTP control +UFTP).
+ */
+ typedef enum {
+ FTP_IP_ADDRESS = 0,
+ FTP_SERVER_NAME = 1,
+ FTP_USER_NAME = 2,
+ FTP_PASSWORD = 3,
+ FTP_ACCOUNT = 4,
+ FTP_INACTIVITY_TIMEOUT = 5,
+ FTP_MODE = 6,
+ FTP_SERVER_PORT = 7,
+ FTP_SECURE = 8,
+ NUM_FTP_OP_CODES
+ } FtpOpCode;
+
+ /** Type of FTP Command.
+ */
+ typedef enum {
+ FTP_LOGOUT = 0,
+ FTP_LOGIN = 1,
+ FTP_DELETE_FILE = 2,
+ FTP_RENAME_FILE = 3,
+ FTP_GET_FILE = 4,
+ FTP_PUT_FILE = 5,
+ FTP_GET_DIRECT = 6,
+ FTP_PUT_DIRECT = 7,
+ FTP_CD = 8,
+ FTP_MKDIR = 10,
+ FTP_RMDIR = 11,
+ FTP_FILE_INFO = 13,
+ FTP_LS = 14,
+ FTP_FOTA_FILE = 100
+ } FtpCmd;
+
+ /** Set the timeout for FTP operations.
+ *
+ * @param timeout -1 blocking, else non-blocking timeout in milliseconds.
+ * @return true if successful, otherwise false.
+ */
+ bool ftpSetTimeout(int timeout);
+
+ /** Reset the FTP configuration back to defaults.
+ *
+ * @return true if successful, false otherwise.
+ */
+ bool ftpResetPar();
+
+ /** Set FTP parameters.
+ *
+ * This should be called as many times as is necessary
+ * to set all the possible parameters (FtpOpCode).
+ *
+ * See section 27.1 of u-blox-ATCommands_Manual(UBX-13002752).pdf
+ * for full details. By example:
+ *
+ * ftpOpCode ftpInPar
+ * FTP_IP_ADDRESS "145.33.18.10" (the target FTP server IP address)
+ * FTP_SERVER_NAME "www.ftpserver.com" (the target FTP server name)
+ * FTP_USER_NAME "my_username"
+ * FTP_PASSWORD "my_password"
+ * FTP_ACCOUNT "my_account" (not required by most FTP servers)
+ * FTP_INACTIVITY_TIMEOUT "60" (the default is 0, which means no timeout)
+ * FTP_MODE "0" for active, "1" for passive (the default is 0)
+ * FTP_SERVER_PORT "25" (default is port 21)
+ * FTP_SECURE "0" for no security, "1" for SFTP (the default is 0)
+ *
+ * @param ftpOpCode the FTP operation code.
+ * @param ftpInPar the FTP input parameter.
+ * @return true if successful, false otherwise.
+ */
+ bool ftpSetPar(FtpOpCode ftpOpCode, const char * ftpInPar);
+
+ /** Perform an FTP command.
+ *
+ * Connect() must have been called previously to establish a data
+ * connection.
+ *
+ * See section 27.2 of u-blox-ATCommands_Manual(UBX-13002752).pdf
+ * for full details. By example, it works like this:
+ *
+ * ftpCmd file1 file2 offset buf len
+ * FTP_LOGOUT N/A N/A N/A N/A N/A
+ * FTP_LOGIN N/A N/A N/A N/A N/A
+ * FTP_DELETE_FILE "the_file" N/A N/A N/A N/A
+ * FTP_RENAME_FILE "old_name" "new_name" N/A N/A N/A
+ * FTP_GET_FILE "the_file" Note 1 0 - 65535 N/A N/A (Notes 2)
+ * FTP_PUT_FILE "the_file" Note 1 0 - 65535 N/A N/A (Note 2)
+ * FTP_CD "dir1\dir2" N/A N/A N/A N/A
+ * FTP_MKDIR "newdir" N/A N/A N/A N/A
+ * FTP_RMDIR "dir" N/A N/A N/A N/A
+ * FTP_FILE_INFO "the_path" N/A N/A Note 3
+ * FTP_LS "the_path" N/A N/A Note 3
+ * FTP_FOTA_FILE "the_file" N/A N/A Note 4
+ *
+ * Note 1: for this case, file2 is the name that the file should be
+ * given when it arrives (in the modem file system for a GET, at the FTP
+ * server for a PUT); if set to NULL then file1 is used.
+ * Note 2: the file will placed into the modem file system for the
+ * GET case (and can be read with readFile()), or must already be in the
+ * modem file system, (can be written using writeFile()) for the PUT case.
+ * Note 3: buf should point to the location where the file info
+ * or directory listing is to be stored and len should be the maximum
+ * length that can be stored.
+ * Note 4: a hex string representing the MD5 sum of the FOTA file will be
+ * stored at buf; len must be at least 32 as an MD5 sum is 16 bytes.
+ * FTP_FOTA_FILE is not supported on SARA-U2.
+ * Note 5: FTP_GET_DIRECT and FTP_PUT_DIRECT are not supported by
+ * this driver.
+ *
+ * @param ftpCmd the FTP command.
+ * @param file1 the first file name if required (NULL otherwise).
+ * @param file2 the second file name if required (NULL otherwise).
+ * @param offset the offset (in bytes) at which to begin the get
+ * or put operation within the file.
+ * @param buf pointer to a buffer, required for FTP_DIRECT mode
+ * and FTP_LS only.
+ * @param len the size of buf.
+ * @return NULL if successful, otherwise a pointer to
+ * a Error struct containing the error class and error
+ * code, see section Appendix A.B of
+ * u-blox-ATCommands_Manual(UBX-13002752).pdf for details.
+ */
+ Error *ftpCommand(FtpCmd ftpCmd, const char* file1 = NULL, const char* file2 = NULL,
+ int offset = 0, char* buf = NULL, int len = 0);
+
+ /**********************************************************************
+ * PUBLIC: Cell Locate
+ **********************************************************************/
+
+ /** Which form of Cell Locate sensing to use.
+ */
+ typedef enum {
+ CELL_LAST,
+ CELL_GNSS,
+ CELL_LOCATE,
+ CELL_HYBRID
+ } CellSensType;
+
+ /** Types of Cell Locate response.
+ */
+ typedef enum {
+ CELL_DETAILED = 1,
+ CELL_MULTIHYP = 2
+ } CellRespType;
+
+ /** Cell Locate data.
+ */
+ typedef struct {
+ volatile bool validData; //!< Flag for indicating if data is valid.
+ volatile struct tm time; //!< GPS Timestamp.
+ volatile float longitude; //!< Estimated longitude, in degrees.
+ volatile float latitude; //!< Estimated latitude, in degrees.
+ volatile int altitude; //!< Estimated altitude, in meters^2.
+ volatile int uncertainty; //!< Maximum possible error, in meters.
+ volatile int speed; //!< Speed over ground m/s^2.
+ volatile int direction; //!< Course over ground in degrees.
+ volatile int verticalAcc; //!< Vertical accuracy, in meters^2.
+ volatile CellSensType sensor; //!< Sensor used for last calculation.
+ volatile int svUsed; //!< number of satellite used.
+ } CellLocData;
+
+ /** Configure the Cell Locate TCP aiding server.
+ *
+ * Connect() must have been called previously to establish
+ * a data connection.
+ *
+ * @param server_1 host name of the primary MGA server.
+ * @param server_2 host name of the secondary MGA server.
+ * @param token authentication token for MGA server access.
+ * @param days the number of days into the future the off-line
+ * data for the u-blox 7.
+ * @param period the number of weeks into the future the off-line
+ * data for u-blox M8.
+ * @param resolution resolution of off-line data for u-blox M8: 1 every
+ * day, 0 every other day.
+ * @return true if the request is successful, otherwise false.
+ */
+ bool cellLocSrvTcp(const char* token, const char* server_1 = "cell-live1.services.u-blox.com",
+ const char* server_2 = "cell-live2.services.u-blox.com",
+ int days = 14, int period = 4, int resolution = 1);
+
+ /** Configure Cell Locate UDP aiding server.
+ *
+ * Connect() must have been called previously to establish
+ * a data connection.
+ *
+ * @param server_1 host name of the primary MGA server.
+ * @param port server port.
+ * @param latency expected network latency in seconds from 0 to 10000 milliseconds.
+ * @param mode Assist Now management, mode of operation:
+ * 0 data downloaded at GNSS power up,
+ * 1 automatically kept alive, manual download.
+ * @return true if the request is successful, otherwise false.
+ */
+ bool cellLocSrvUdp(const char* server_1 = "cell-live1.services.u-blox.com",
+ int port = 46434, int latency = 1000, int mode = 0);
+
+ /** Configure Cell Locate location sensor.
+ *
+ * @param scanMode network scan mode: 0 normal, 1 deep scan.
+ * @return true if the request is successful, otherwise false.
+ */
+ bool cellLocConfig(int scanMode);
+
+ /** Request a one-shot Cell Locate.
+ *
+ * This function is non-blocking, the result is retrieved using cellLocGetxxx.
+ *
+ * Note: during the location process, unsolicited result codes will be returned
+ * by the modem indicating progress and potentially flagging interesting errors.
+ * In order to see these errors, instantiate this class with debugOn set to true.
+ *
+ * @param sensor sensor selection.
+ * @param timeout timeout period in seconds (1 - 999).
+ * @param accuracy target accuracy in meters (1 - 999999).
+ * @param type detailed or multi-hypothesis.
+ * @param hypothesis maximum desired number of responses from CellLocate (up to 16),
+ * must be 1 if type is CELL_DETAILED.
+ * @return true if the request is successful, otherwise false.
+ */
+ bool cellLocRequest(CellSensType sensor, int timeout, int accuracy,
+ CellRespType type = CELL_DETAILED, int hypothesis = 1);
+
+ /** Get a position record.
+ *
+ * @param data pointer to a CellLocData structure where the location will be put.
+ * @param index of the position to retrieve.
+ * @return true if data has been retrieved and copied, false otherwise.
+ */
+ bool cellLocGetData(CellLocData *data, int index = 0);
+
+ /** Get the number of position records received.
+ *
+ * @return number of position records received.
+ */
+ int cellLocGetRes();
+
+ /** Get the number of position records expected to be received.
+ *
+ * @return number of position records expected to be received.
+ */
+ int cellLocGetExpRes();
+
+protected:
+
+ /**********************************************************************
+ * PROTECTED: HTTP
+ **********************************************************************/
+
+ /** Check for timeout.
+ */
+ #define TIMEOUT(t, ms) ((ms != TIMEOUT_BLOCKING) && (ms < t.read_ms()))
+
+ /** Check for a valid profile.
+ */
+ #define IS_PROFILE(p) (((p) >= 0) && (((unsigned int) p) < (sizeof(_httpProfiles)/sizeof(_httpProfiles[0]))) \
+ && (_httpProfiles[p].modemHandle != HTTP_PROF_UNUSED))
+
+ /** Management structure for HTTP profiles.
+ *
+ * It is possible to have up to 4 different HTTP profiles (LISA-C200, LISA-U200 and SARA-G350) having:
+ *
+ * @param handle the current HTTP profile is in handling state or not (default value is HTTP_ERROR).
+ * @param timeout the timeout for the current HTTP command.
+ * @param pending the status for the current HTTP command (in processing state or not).
+ * @param cmd the code for the current HTTP command.
+ * @param result the result for the current HTTP command once processed.
+ */
+ typedef struct {
+ int modemHandle;
+ int timeout;
+ volatile bool pending;
+ volatile int cmd;
+ volatile int result;
+ Error httpError;
+ } HttpProfCtrl;
+
+ /** The HTTP profile storage.
+ */
+ HttpProfCtrl _httpProfiles[4];
+
+ /** Callback to capture the response to an HTTP command.
+ */
+ void UUHTTPCR_URC();
+
+ /** Find a profile with a given handle. If no handle is given, find the next
+ * free profile.
+ *
+ * @param modemHandle the handle of the profile to find.
+ * @return the profile handle or negative if not found/created.
+ */
+ int findProfile(int modemHandle = HTTP_PROF_UNUSED);
+
+ /** Helper function to get a HTTP command as a text string, useful
+ * for debug purposes.
+ *
+ * @param httpCmdCode the HTTP command.
+ * @return HTTP command in string format.
+ */
+ const char* getHttpCmd(HttpCmd httpCmd);
+
+ /**********************************************************************
+ * PROTECTED: FTP
+ **********************************************************************/
+
+ /** Unused FTP op code marker.
+ */
+ #define FTP_OP_CODE_UNUSED -1
+
+ /** The FTP timeout in milliseconds.
+ */
+ int _ftpTimeout;
+
+ /** A place to store the FTP op code for the last result.
+ */
+ volatile int _lastFtpOpCodeResult;
+
+ /** A place to store the last FTP result.
+ */
+ volatile int _lastFtpResult;
+
+ /** A place to store the last FTP op code for data response.
+ */
+ volatile int _lastFtpOpCodeData;
+
+ /** A place to store data returns from an FTP operation.
+ */
+ char * _ftpBuf;
+
+ /** The length of FTP data that can be stored (at _ftpBuf).
+ */
+ int _ftpBufLen;
+
+ /** storage for the last FTP error
+ */
+ Error _ftpError;
+
+ /** Callback to capture the result of an FTP command.
+ */
+ void UUFTPCR_URC();
+
+ /** Callback to capture data returned from an FTP command.
+ */
+ void UUFTPCD_URC();
+
+ /** Helper function to get an FTP command as a text string, useful
+ * for debug purposes.
+ *
+ * @param ftpCmdCode the FTP command.
+ * @return FTP command in string format.
+ */
+ const char * getFtpCmd(FtpCmd ftpCmd);
+
+ /**********************************************************************
+ * PROTECTED: Cell Locate
+ **********************************************************************/
+
+ /** The maximum number of hypotheses
+ */
+ #define CELL_MAX_HYP (16 + 1)
+
+ /** Received positions.
+ */
+ volatile int _locRcvPos;
+
+ /** Expected positions.
+ */
+ volatile int _locExpPos;
+
+ /** The Cell Locate data.
+ */
+ CellLocData _loc[CELL_MAX_HYP];
+
+ /** Buffer for the URC to work with
+ */
+ char urcBuf[128];
+
+ /** Callback to capture +UULOCIND.
+ */
+ void UULOCIND_URC();
+
+ /** Callback to capture +UULOC.
+ */
+ void UULOC_URC();
+};
+
+#endif // _UBLOX_AT_CELLULAR_INTERFACE_EXT_
+