Implementation of the CellularInterface for u-blox C030 boards with N2xx modems. Note: requires the N211 module firmware to be at least 06.57 A01.02.

Dependents:   example-ublox-cellular-interface HelloMQTT example-ublox-cellular-interface_r410M example-ublox-mbed-client ... more

Revision:
1:8ea78dce6b36
Child:
4:2bf3875a13f1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UbloxATCellularInterfaceN2xx.cpp	Mon Jun 26 13:49:08 2017 +0100
@@ -0,0 +1,814 @@
+/* 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 "UbloxATCellularInterfaceN2xx.h"
+#include "mbed_poll.h"
+#include "nsapi.h"
+#include "APN_db.h"
+#ifdef FEATURE_COMMON_PAL
+#include "mbed_trace.h"
+#define TRACE_GROUP "UACI"
+#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
+
+/**********************************************************************
+ * PRIVATE METHODS
+ **********************************************************************/
+
+ bool UbloxATCellularInterfaceN2xx::SendAT(const char *cmd)
+ {
+     tr_debug("SendAT helper()");
+     return _at->send(cmd) && _at->recv("OK");     
+ } 
+ 
+// Event thread for asynchronous received data handling.
+void UbloxATCellularInterfaceN2xx::handle_event(){
+    pollfh fhs;
+    int count;
+    int at_timeout;
+
+    fhs.fh = _fh;
+    fhs.events = POLLIN;
+
+    while (true) {
+        count = poll(&fhs, 1, 1000);
+        if (count > 0 && (fhs.revents & POLLIN)) {
+            LOCK();
+            at_timeout = _at_timeout;
+            at_set_timeout(10); // Avoid blocking but also make sure we don't
+                                // time out if we get ahead of the serial port
+            _at->debug_on(false); // Debug here screws with the test output
+            // Let the URCs run
+            _at->recv(UNNATURAL_STRING);
+            _at->debug_on(_debug_trace_on);
+            at_set_timeout(at_timeout);
+            UNLOCK();
+        }
+    }
+}
+
+// Find or create a socket from the list.
+UbloxATCellularInterfaceN2xx::SockCtrl * UbloxATCellularInterfaceN2xx::find_socket(int modem_handle)
+{
+    UbloxATCellularInterfaceN2xx::SockCtrl *socket = NULL;
+
+    for (unsigned int x = 0; (socket == NULL) && (x < sizeof(_sockets) / sizeof(_sockets[0])); x++) {
+        if (_sockets[x].modem_handle == modem_handle) {
+            socket = &(_sockets[x]);
+        }
+    }
+
+    return socket;
+}
+
+// Clear out the storage for a socket
+void UbloxATCellularInterfaceN2xx::clear_socket(UbloxATCellularInterfaceN2xx::SockCtrl * socket)
+{
+    if (socket != NULL) {
+        socket->modem_handle = SOCKET_UNUSED;
+        socket->pending     = 0;
+        socket->callback    = NULL;
+        socket->data        = NULL;
+    }
+}
+
+// Check that a socket pointer is valid
+bool UbloxATCellularInterfaceN2xx::check_socket(SockCtrl * socket)
+{
+    bool success = false;
+
+    if (socket != NULL) {
+        for (unsigned int x = 0; !success && (x < sizeof(_sockets) / sizeof(_sockets[0])); x++) {
+            if (socket == &(_sockets[x])) {
+                success = true;
+            }
+        }
+    }
+
+    return success;
+}
+
+// Callback for Socket Read From URC.
+void UbloxATCellularInterfaceN2xx::NSONMI_URC()
+{
+    int a;
+    int b;
+    char buf[32];
+    SockCtrl *socket;
+    
+    // Note: not calling _at->recv() from here as we're
+    // already in an _at->recv()
+    // +UUSORF: <socket>,<length>
+    if (read_at_to_newline(buf, sizeof (buf)) > 0) {
+        tr_debug("NSONMI URC");
+        if (sscanf(buf, ":%d,%d", &a, &b) == 2) {         
+            socket = find_socket(a);        
+            if (socket != NULL) {
+                socket->pending += b;
+                tr_debug("Socket 0x%08x: modem handle %d has %d byte(s) pending",
+                         (unsigned int) socket, a, socket->pending);
+                if (socket->callback != NULL) {
+                    tr_debug("***** Calling callback...");
+                    socket->callback(socket->data);
+                    tr_debug("***** Callback finished");
+                } else {
+                    tr_debug("No callback found for socket.");
+                }
+            } else {
+                tr_debug("Can't find socket with modem handle %d", a);
+            }
+        }
+    }
+}
+
+/**********************************************************************
+ * PROTECTED METHODS: GENERAL
+ **********************************************************************/
+
+// Get the next set of credentials, based on IMSI.
+void UbloxATCellularInterfaceN2xx::get_next_credentials(const char * config)
+{
+    if (config) {
+        _apn    = _APN_GET(config);
+        _uname  = _APN_GET(config);
+        _pwd    = _APN_GET(config);
+    }
+
+    _apn    = _apn     ?  _apn    : "";
+    _uname  = _uname   ?  _uname  : "";
+    _pwd    = _pwd     ?  _pwd    : "";
+}
+
+/**********************************************************************
+ * PROTECTED METHODS: NETWORK INTERFACE and SOCKETS
+ **********************************************************************/
+
+// Gain access to us.
+NetworkStack *UbloxATCellularInterfaceN2xx::get_stack()
+{
+    return this;
+}
+
+// Create a socket.
+nsapi_error_t UbloxATCellularInterfaceN2xx::socket_open(nsapi_socket_t *handle,
+                                                    nsapi_protocol_t proto)
+{
+    nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
+    int modem_handle =0;
+    SockCtrl *socket;
+  
+    if (proto != NSAPI_UDP) {
+        return NSAPI_ERROR_UNSUPPORTED;
+    }
+   
+    LOCK();
+
+    // Find a free socket
+    socket = find_socket();
+    if (socket != NULL) {  
+        tr_debug("socket_open(%d)", proto);    
+        if (_at->send("AT+NSOCR=DGRAM,17,%d", _localListenPort) &&            
+            _at->recv("%d\n", &modem_handle) && 
+            _at->recv("OK")) {
+                tr_debug("Socket 0x%8x: handle %d was created", (unsigned int) socket, modem_handle);
+                clear_socket(socket);
+                socket->modem_handle = modem_handle;
+                *handle = (nsapi_socket_t) socket;
+                nsapi_error = NSAPI_ERROR_OK;
+        } else {
+            tr_error("Couldn't open socket using AT command");
+        }
+    } else {
+        tr_error("Can't find a socket to use");
+        nsapi_error = NSAPI_ERROR_NO_MEMORY;
+    }
+
+    UNLOCK();
+    return nsapi_error;
+}
+
+
+// Close a socket.
+nsapi_error_t UbloxATCellularInterfaceN2xx::socket_close(nsapi_socket_t handle)
+{
+    nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
+    SockCtrl *socket = (SockCtrl *) handle;
+    LOCK();
+
+    tr_debug("socket_close(0x%08x)", (unsigned int) handle);
+
+    MBED_ASSERT (check_socket(socket));
+
+    if (_at->send("AT+NSOCL=%d", socket->modem_handle) &&
+        _at->recv("OK")) {
+        clear_socket(socket);
+        nsapi_error = NSAPI_ERROR_OK;
+    } else {
+        tr_error("Failed to close socket %d", socket->modem_handle);
+    }
+
+    UNLOCK();
+    return nsapi_error;
+}
+
+// Bind a local port to a socket.
+nsapi_error_t UbloxATCellularInterfaceN2xx::socket_bind(nsapi_socket_t handle,
+                                                    const SocketAddress &address)
+{
+    return NSAPI_ERROR_UNSUPPORTED;
+}
+
+// Connect to a socket
+nsapi_error_t UbloxATCellularInterfaceN2xx::socket_connect(nsapi_socket_t handle,
+                                                       const SocketAddress &address)
+{
+return NSAPI_ERROR_UNSUPPORTED;  
+}
+
+// Send to a socket.
+nsapi_size_or_error_t UbloxATCellularInterfaceN2xx::socket_send(nsapi_socket_t handle,
+                                                            const void *data,
+                                                            nsapi_size_t size)
+{
+    return NSAPI_ERROR_UNSUPPORTED;  
+}
+
+// Send to an IP address.
+nsapi_size_or_error_t UbloxATCellularInterfaceN2xx::socket_sendto(nsapi_socket_t handle,
+                                                              const SocketAddress &address,
+                                                              const void *data,
+                                                              nsapi_size_t size)
+{
+    nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR;
+    bool success = true;
+    const char *buf = (const char *) data;
+    nsapi_size_t blk = MAX_WRITE_SIZE;
+    nsapi_size_t count = size;
+    SockCtrl *socket = (SockCtrl *) handle;
+
+    tr_debug("socket_sendto(0x%8x, %s(:%d), 0x%08x, %d)", (unsigned int) handle,
+             address.get_ip_address(), address.get_port(), (unsigned int) data, size);
+
+    MBED_ASSERT (check_socket(socket));
+
+    tr_debug("Max Write Size for SendTo: %d", MAX_WRITE_SIZE);
+    if (size > MAX_WRITE_SIZE) {
+        tr_warn("WARNING: packet length %d is too big for one UDP packet (max %d), will be fragmented.", size, MAX_WRITE_SIZE);
+    }
+
+    while ((count > 0) && success) {
+        if (count < blk) {
+            blk = count;
+        }
+        
+        // call the AT Helper function to send the bytes
+        tr_debug("Sending %d bytes....", blk);
+        int sent = sendto(socket, address, buf, blk);
+        if (sent < 0) {
+            tr_error("Something went wrong! %d", sent);
+            return NSAPI_ERROR_DEVICE_ERROR;
+        }
+        
+        buf += sent;
+        count -= sent;
+    }
+
+    if (success) {
+        nsapi_error_size = size - count;
+        if (_debug_trace_on) {
+            tr_debug("socket_sendto: %d \"%*.*s\"", size, size, size, (char *) data);
+        }
+    }
+
+    return nsapi_error_size;
+}
+
+nsapi_size_or_error_t UbloxATCellularInterfaceN2xx::sendto(SockCtrl *socket, const SocketAddress &address, const char *buf, int size) {
+    nsapi_size_or_error_t sent = NSAPI_ERROR_DEVICE_ERROR;
+    int id;
+    
+    tr_debug("Malloc %d * 2 + 1 bytes", size);
+    char *str = (char *) malloc((size * 2) + 1);
+    if (str == NULL) {
+        tr_error("Nope, could allocate it!");
+        return NSAPI_ERROR_NO_MEMORY;
+    }
+    
+    tr_debug("Converting byte array to hex string");
+    bin_to_hex(buf, size, str);
+    tr_debug("Got hex string");
+    
+    LOCK();
+        
+    // AT+NSOSTF= socket, remote_addr, remote_port, length, data
+    tr_debug("Going to send this:-");
+    tr_debug("AT+NSOSTF=%d,%s,%d,0x0,%d,%s", socket->modem_handle, address.get_ip_address(), address.get_port(), size, str);
+    tr_debug("Writing AT command...");
+    if (_at->send("AT+NSOSTF=%d,%s,%d,0x0,%d,%s", socket->modem_handle, address.get_ip_address(), address.get_port(), size, str)) {
+        tr_debug("Reading back the Sent Size...");
+        if (_at->recv("%d,%d\n", &id, &sent) && _at->recv("OK")) {            
+            tr_debug("Received %d bytes on socket %d", sent, id);
+        } else {
+            tr_error("Didn't get the Sent size or OK");
+        }
+    } else {
+        tr_error("Didn't send the AT command!");
+    }
+
+    UNLOCK();  
+    free(str);
+    
+    return sent;
+}
+
+void UbloxATCellularInterfaceN2xx::bin_to_hex(const char *buff, unsigned int length, char *output)
+{
+    char binHex[] = "0123456789ABCDEF";
+    
+    *output = '\0';
+
+    for (; length > 0; --length)
+    {
+        unsigned char byte = *buff++;
+
+        *output++ = binHex[(byte >> 4) & 0x0F];
+        *output++ = binHex[byte & 0x0F];
+    }
+    
+    *output++ = '\0';
+}
+
+// Receive from a socket, TCP style.
+nsapi_size_or_error_t UbloxATCellularInterfaceN2xx::socket_recv(nsapi_socket_t handle,
+                                                            void *data,
+                                                            nsapi_size_t size)
+{
+    return NSAPI_ERROR_UNSUPPORTED;
+}
+
+// Receive a packet over a UDP socket.
+nsapi_size_or_error_t UbloxATCellularInterfaceN2xx::socket_recvfrom(nsapi_socket_t handle,
+                                                                SocketAddress *address,
+                                                                void *data,
+                                                                nsapi_size_t size)
+{
+    nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR;
+    bool success = true;
+    char *buf = (char *) data;
+    nsapi_size_t read_blk;
+    nsapi_size_t count = 0;
+    char ipAddress[NSAPI_IP_SIZE];
+    
+    char * tmpBuf = NULL;
+
+    Timer timer;
+    SockCtrl *socket = (SockCtrl *) handle;
+    int at_timeout = _at_timeout;
+
+    tr_debug("socket_recvfrom(0x%08x, 0x%08x, %d)", (unsigned int) handle,
+             (unsigned int) data, size);
+
+    MBED_ASSERT (check_socket(socket));
+
+    timer.start();
+
+    while (success && (size > 0)) {
+        LOCK();
+        at_set_timeout(1000);
+
+        read_blk = socket->pending;
+        if (read_blk > MAX_READ_SIZE) {
+            read_blk = MAX_READ_SIZE;
+        }
+        if (read_blk > 0) {
+            memset (ipAddress, 0, sizeof (ipAddress)); // Ensure terminator
+
+            tmpBuf = (char *) malloc(read_blk);
+            if (tmpBuf == NULL) {
+                return NSAPI_ERROR_NO_MEMORY;
+            }
+            
+            // call the AT helper function to get the bytes
+            nsapi_error_size = receivefrom(socket->modem_handle, address, read_blk, tmpBuf);
+                
+            if (nsapi_error_size >= 0) {
+                memcpy(buf, tmpBuf, nsapi_error_size);
+
+                socket->pending -= read_blk;
+                tr_debug("Socket 0x%08x: modem handle %d now has only %d byte(s) pending",
+                         (unsigned int) socket, socket->modem_handle, socket->pending);
+
+                count += nsapi_error_size;
+                buf += nsapi_error_size;
+                size = 0; // A UDP packet arrives in one piece, so this means DONE.
+            } else {
+              // Should never fail to read when there is pending data
+                success = false;
+            }
+        } else if (timer.read_ms() < SOCKET_TIMEOUT) {
+            // Wait for URCs
+            _at->recv(UNNATURAL_STRING);
+        } else {
+            // Timeout with nothing received
+            nsapi_error_size = NSAPI_ERROR_WOULD_BLOCK;
+            success = false;
+        }
+
+        at_set_timeout(at_timeout);
+        UNLOCK();
+    }
+    timer.stop();
+
+    if (success) {
+        nsapi_error_size = count;
+    }
+
+    tr_debug("socket_recvfrom: %d \"%*.*s\"", count, count, count, buf - count);
+
+    return nsapi_error_size;
+}
+
+nsapi_size_or_error_t UbloxATCellularInterfaceN2xx::receivefrom(int modem_handle, SocketAddress *address, int length, char *buf) {
+    char ipAddress[NSAPI_IP_SIZE];
+    nsapi_size_or_error_t size;
+    
+    memset (ipAddress, 0, sizeof (ipAddress)); // Ensure terminator
+    
+    if (length > MAX_READ_SIZE) {
+        return NSAPI_ERROR_UNSUPPORTED;
+    }
+    
+    char * tmpBuf = (char *) malloc(length*2);
+    if (tmpBuf == NULL)
+        return NSAPI_ERROR_NO_MEMORY;
+    
+    LOCK();      
+    
+    // Ask for x bytes from Socket 
+    if (_at->send("AT+NSORF=%d,%d", modem_handle, length)) {
+        unsigned int id, port;
+        
+        // ReadFrom header, to get length - if no data then this will time out
+        if (_at->recv("%d,%15[^,],%d,%d,", &id, ipAddress, &port, &size)) {
+            tr_debug("Socket RecvFrom: #%d: %d", id, size);
+                
+            address->set_ip_address(ipAddress);
+            address->set_port(port);
+            
+            // now read hex data
+            if (_at->read(tmpBuf, size*2) == size*2) {
+                
+                // convert to bytes
+                hex_to_bin(tmpBuf, buf, size);
+             
+                // read the "remaining" value
+                char remaining[4];                
+                if (!_at->recv(",%3[^\n]\n", remaining)) {
+                    size = NSAPI_ERROR_DEVICE_ERROR;
+                }
+            }
+        }
+        
+        // we should get the OK (even if there is no data to read)
+        if (!_at->recv("OK")) {
+            size = NSAPI_ERROR_DEVICE_ERROR;
+        }
+    }
+    
+    UNLOCK();
+    
+    free(tmpBuf);
+    
+    return size;
+}
+
+char UbloxATCellularInterfaceN2xx::hex_char(char c)
+{
+    if ('0' <= c && c <= '9') return (unsigned char)(c - '0');
+    if ('A' <= c && c <= 'F') return (unsigned char)(c - 'A' + 10);
+    if ('a' <= c && c <= 'f') return (unsigned char)(c - 'a' + 10);
+    return 0xFF;
+}
+
+int UbloxATCellularInterfaceN2xx::hex_to_bin(const char* s, char * buff, int length)
+{
+    int result;
+    if (!s || !buff || length <= 0) return -1;
+
+    for (result = 0; *s; ++result)
+    {
+        unsigned char msn = hex_char(*s++);
+        if (msn == 0xFF) return -1;
+        unsigned char lsn = hex_char(*s++);
+        if (lsn == 0xFF) return -1;
+        unsigned char bin = (msn << 4) + lsn;
+
+        if (length-- <= 0) return -1;
+        *buff++ = bin;
+    }
+    return result;
+}
+
+// Attach an event callback to a socket, required for asynchronous
+// data reception
+void UbloxATCellularInterfaceN2xx::socket_attach(nsapi_socket_t handle,
+                                             void (*callback)(void *),
+                                             void *data)
+{
+    SockCtrl *socket = (SockCtrl *) handle;
+
+    MBED_ASSERT (check_socket(socket));
+
+    socket->callback = callback;
+    socket->data = data;
+}
+
+// Unsupported TCP server functions.
+nsapi_error_t UbloxATCellularInterfaceN2xx::socket_listen(nsapi_socket_t handle,
+                                                      int backlog)
+{
+    return NSAPI_ERROR_UNSUPPORTED;
+}
+nsapi_error_t UbloxATCellularInterfaceN2xx::socket_accept(nsapi_socket_t server,
+                                                      nsapi_socket_t *handle,
+                                                      SocketAddress *address)
+{
+    return NSAPI_ERROR_UNSUPPORTED;
+}
+
+// Unsupported option functions.
+nsapi_error_t UbloxATCellularInterfaceN2xx::setsockopt(nsapi_socket_t handle,
+                                                   int level, int optname,
+                                                   const void *optval,
+                                                   unsigned optlen)
+{
+    return NSAPI_ERROR_UNSUPPORTED;
+}
+nsapi_error_t UbloxATCellularInterfaceN2xx::getsockopt(nsapi_socket_t handle,
+                                                   int level, int optname,
+                                                   void *optval,
+                                                   unsigned *optlen)
+{
+    return NSAPI_ERROR_UNSUPPORTED;
+}
+
+/**********************************************************************
+ * PUBLIC METHODS
+ **********************************************************************/
+
+// Constructor.
+UbloxATCellularInterfaceN2xx::UbloxATCellularInterfaceN2xx(PinName tx,
+                                                   PinName rx,
+                                                   int baud,
+                                                   bool debug_on)
+{
+    _sim_pin_check_change_pending = false;
+    _sim_pin_check_change_pending_enabled_value = false;
+    _sim_pin_change_pending = false;
+    _sim_pin_change_pending_new_pin_value = NULL;
+    _apn = NULL;
+    _uname = NULL;
+    _pwd = NULL;
+    _connection_status_cb = NULL;
+    
+    _localListenPort = 10000;
+    
+    tr_debug("UbloxATCellularInterfaceN2xx Constructor");
+
+    // Initialise sockets storage
+    memset(_sockets, 0, sizeof(_sockets));
+    for (unsigned int socket = 0; socket < sizeof(_sockets) / sizeof(_sockets[0]); socket++) {
+        _sockets[socket].modem_handle = SOCKET_UNUSED;
+        _sockets[socket].callback = NULL;
+        _sockets[socket].data = NULL;
+    }
+
+    // The authentication to use
+    _auth = NSAPI_SECURITY_UNKNOWN;
+
+    // Nullify the temporary IP address storage
+    _ip = NULL;
+
+    // Initialise the base class, which starts the AT parser
+    baseClassInit(tx, rx, baud, debug_on);
+
+    // Start the event handler thread for Rx data
+    event_thread.start(callback(this, &UbloxATCellularInterfaceN2xx::handle_event));
+
+    // URC handlers for sockets
+    _at->oob("+NSONMI", callback(this, &UbloxATCellularInterfaceN2xx::NSONMI_URC));
+}
+
+// Destructor.
+UbloxATCellularInterfaceN2xx::~UbloxATCellularInterfaceN2xx()
+{
+    // Free _ip if it was ever allocated
+    free(_ip);
+}
+
+// Set the authentication scheme.
+void UbloxATCellularInterfaceN2xx::set_authentication(nsapi_security_t auth)
+{
+    _auth = auth;
+}
+
+// Set APN, user name and password.
+void  UbloxATCellularInterfaceN2xx::set_credentials(const char *apn,
+                                                const char *uname,
+                                                const char *pwd)
+{
+    _apn = apn;
+    _uname = uname;
+    _pwd = pwd;
+}
+
+// Set PIN.
+void UbloxATCellularInterfaceN2xx::set_sim_pin(const char *pin) {
+    set_pin(pin);
+}
+
+// Get the IP address of a host.
+nsapi_error_t UbloxATCellularInterfaceN2xx::gethostbyname(const char *host,
+                                                      SocketAddress *address,
+                                                      nsapi_version_t version)
+{
+    nsapi_error_t nsapiError = NSAPI_ERROR_DEVICE_ERROR;
+    tr_debug("GetHostByName: host= %s", host);
+    if (address->set_ip_address(host)) {
+        tr_debug("OK");
+        nsapiError = NSAPI_ERROR_OK;
+    } else {
+        tr_debug("Failed");
+        nsapiError = NSAPI_ERROR_UNSUPPORTED;
+    }
+
+    return nsapiError;
+}
+
+// Make a cellular connection
+nsapi_error_t UbloxATCellularInterfaceN2xx::connect(const char *sim_pin,
+                                                const char *apn,
+                                                const char *uname,
+                                                const char *pwd)
+{
+    nsapi_error_t nsapi_error;
+
+    if (sim_pin != NULL) {
+        _pin = sim_pin;
+    }
+
+    if (apn != NULL) {
+        _apn = apn;
+    }
+
+    if ((uname != NULL) && (pwd != NULL)) {
+        _uname = uname;
+        _pwd = pwd;
+    } else {
+        _uname = NULL;
+        _pwd = NULL;
+    }
+
+    tr_debug("SIM, APN, UName & pwd set, now calling connect()");
+    nsapi_error = connect();
+
+    return nsapi_error;
+}
+
+// Make a cellular connection using the IP stack on board the cellular modem
+nsapi_error_t UbloxATCellularInterfaceN2xx::connect()
+{
+    nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
+    bool registered = false;
+
+    // Set up modem and then register with the network
+    if (init()) {
+        
+        tr_debug("Trying to register...");
+        nsapi_error = NSAPI_ERROR_NO_CONNECTION;    
+        for (int retries = 0; !registered && (retries < 3); retries++) {
+            if (nwk_registration()) {
+                registered = true;
+            }
+        }
+    }
+
+    // Attempt to establish a connection
+    if (registered) {
+        nsapi_error = NSAPI_ERROR_OK;
+    } else {
+        tr_debug("Failed to register.");
+    }
+
+    return nsapi_error;
+}
+
+// User initiated disconnect.
+nsapi_error_t UbloxATCellularInterfaceN2xx::disconnect()
+{
+    nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
+
+    if (nwk_deregistration()) {
+        nsapi_error = NSAPI_ERROR_OK;
+        
+        if (_connection_status_cb) {
+            _connection_status_cb(NSAPI_ERROR_CONNECTION_LOST);
+        }
+    }
+
+    return nsapi_error;
+}
+
+// Enable or disable SIM PIN check lock.
+nsapi_error_t UbloxATCellularInterfaceN2xx::set_sim_pin_check(bool set,
+                                                          bool immediate,
+                                                          const char *sim_pin)
+{
+    return NSAPI_ERROR_UNSUPPORTED;
+}
+
+// Change the PIN code for the SIM card.
+nsapi_error_t UbloxATCellularInterfaceN2xx::set_new_sim_pin(const char *new_pin,
+                                                        bool immediate,
+                                                        const char *old_pin)
+{
+    return NSAPI_ERROR_UNSUPPORTED;
+}
+
+// Determine if the connection is up.
+bool UbloxATCellularInterfaceN2xx::is_connected()
+{
+    return get_ip_address() != NULL;
+}
+
+// Get the IP address of the on-board modem IP stack.
+const char * UbloxATCellularInterfaceN2xx::get_ip_address()
+{
+    SocketAddress address;
+    int id;
+    LOCK();
+
+    if (_ip == NULL)
+    {
+        // Temporary storage for an IP address string with terminator
+        _ip = (char *) malloc(NSAPI_IP_SIZE);
+    }
+    
+    if (_ip != NULL) {                        
+        memset(_ip, 0, NSAPI_IP_SIZE); // Ensure a terminator
+        // +CGPADDR - returns a list of IP Addresses per context, just pick the first one as SARA-N2xx only allows 1 context.
+        if (!_at->send("AT+CGPADDR") ||
+            !_at->recv("+CGPADDR:%d,%15[^\n]\n", &id, _ip) ||
+            ! _at->recv("OK") ||
+            !address.set_ip_address(_ip) ||
+            !address) {            
+                free (_ip);
+                _ip = NULL;
+        }
+    }
+
+    UNLOCK();
+    return _ip;
+}
+
+// Get the local network mask.
+const char *UbloxATCellularInterfaceN2xx::get_netmask()
+{
+    // Not implemented.
+    return NULL;
+}
+
+// Get the local gateways.
+const char *UbloxATCellularInterfaceN2xx::get_gateway()
+{
+    return get_ip_address();
+}
+
+void UbloxATCellularInterfaceN2xx::set_LocalListenPort(int port) {
+    _localListenPort = port;
+}
+
+// Callback in case the connection is lost.
+void UbloxATCellularInterfaceN2xx::connection_status_cb(Callback<void(nsapi_error_t)> cb)
+{
+    _connection_status_cb = cb;
+}
+
+// End of file
+