u-blox USB modems (GSM and CDMA)

Dependencies:   CellularUSBModem

Dependents:   C027_CANInterfaceComm C027_ModemTransparentUSBCDC_revb UbloxModemHTTPClientTest C027_HTTPClientTest ... more

Legacy Networking Libray

This is an mbed 2 networking library. For an mbed OS 5 compatible library, please see:

Import libraryC027Interface

Socket interface for C027Interface. Implements the NetworkSocketAPI

Revision:
5:60b48a013e86
Child:
6:5a1583d0e6cd
diff -r 6e9d98f8c237 -r 60b48a013e86 UbloxModem.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UbloxModem.cpp	Mon Dec 16 09:00:36 2013 +0000
@@ -0,0 +1,471 @@
+/* UbloxModem.cpp */
+/* Copyright (C) 2012 mbed.org, MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#define __DEBUG__ 3
+#ifndef __MODULE__
+#define __MODULE__ "UbloxModem.cpp"
+#endif
+
+#include "core/fwk.h"
+#include "sms/GSMSMSInterface.h"
+#include "sms/CDMASMSInterface.h"
+
+#include "UbloxModem.h"
+
+UbloxModem::UbloxModem(IOStream* atStream, IOStream* pppStream) :
+   m_at(atStream),                          // Construct ATCommandsInterface with the AT serial channel
+   m_CdmaSms(&m_at),                         // Construct SMSInterface with the ATCommandsInterface
+   m_GsmSms(&m_at),                          // Construct SMSInterface with the ATCommandsInterface
+   m_ussd(&m_at),                           // Construct USSDInterface with the ATCommandsInterface
+   m_linkMonitor(&m_at),                    // Construct LinkMonitor with the ATCommandsInterface
+   m_ppp(pppStream ? pppStream : atStream),    // Construct PPPIPInterface with the PPP serial channel
+   m_ipInit(false),                         // PPIPInterface connection is initially down
+   m_smsInit(false),                        // SMSInterface starts un-initialised
+   m_ussdInit(false),                       // USSDInterface starts un-initialised 
+   m_linkMonitorInit(false),                // LinkMonitor subsystem starts un-initialised
+   m_atOpen(false),                          // ATCommandsInterface starts in a closed state
+   m_onePort(pppStream == NULL),
+   m_gsm(true)
+{
+}
+
+class CREGProcessor : public IATCommandsProcessor
+{
+public:
+  CREGProcessor(bool gsm) : status(STATUS_REGISTERING)
+  {
+    m_gsm = gsm;
+  }
+  enum REGISTERING_STATUS { STATUS_REGISTERING, STATUS_OK, STATUS_FAILED };
+  REGISTERING_STATUS getStatus()
+  {
+    return status;
+  }
+  const char* getAtCommand()
+  {
+      return m_gsm ? "AT+CREG?" : "AT+CSS?";
+  }
+private:
+  virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line)
+  {
+    int r;
+    if (m_gsm)
+    {
+        if( sscanf(line, "+CREG: %*d,%d", &r) == 1 )
+        {
+          status = (r == 1 || r == 5) ? STATUS_OK : 
+                   (r == 0 || r == 2) ? STATUS_REGISTERING :
+          //       (r == 3)           ? STATUS_FAILED :
+                                        STATUS_FAILED;
+        }
+    }
+    else
+    {
+        char bc[3] = "";
+        if(sscanf(line, "%*s %*c,%2s,%*d",bc)==1)
+        {
+            status = (strcmp("Z", bc) == 0) ? STATUS_REGISTERING : STATUS_OK;
+        }
+    }
+    return OK;
+  }
+  virtual int onNewEntryPrompt(ATCommandsInterface* pInst)
+  {
+    return OK;
+  }
+  volatile REGISTERING_STATUS status;
+  bool m_gsm;
+};
+
+int UbloxModem::connect(const char* apn, const char* user, const char* password)
+{
+  if( !m_ipInit )
+  {
+    m_ipInit = true;
+    m_ppp.init();
+  }
+  m_ppp.setup(user, password, m_gsm ? DEFAULT_MSISDN_GSM : DEFAULT_MSISDN_CDMA);
+
+  int ret = init();
+  if(ret)
+  {
+    return ret;
+  }
+
+  if (m_onePort)
+  {
+     m_smsInit = false; //SMS status reset
+     m_ussdInit = false; //USSD status reset
+     m_linkMonitorInit = false; //Link monitor status reset
+  }
+
+  ATCommandsInterface::ATResult result;
+
+  if(apn != NULL)
+  {
+    char cmd[48];
+    int tries = 30;
+    sprintf(cmd, "AT+CGDCONT=1,\"IP\",\"%s\"", apn);
+    do //Try 30 times because for some reasons it can fail *a lot* with the K3772-Z dongle
+    {
+      ret = m_at.executeSimple(cmd, &result);
+      DBG("Result of command: Err code=%d", ret);
+      if(ret)
+      {
+        Thread::wait(500);
+      }
+    } while(ret && --tries);
+    DBG("ATResult: AT return=%d (code %d)", result.result, result.code);
+    DBG("APN set to %s", apn);
+  }
+
+  //Connect
+  DBG("Connecting");
+  if (m_onePort)
+  {
+    m_at.close(); // Closing AT parser
+    m_atOpen = false; //Will need to be reinitialized afterwards
+  }
+  
+  DBG("Connecting PPP");
+
+  ret = m_ppp.connect();
+  DBG("Result of connect: Err code=%d", ret);
+  return ret;
+}
+
+
+int UbloxModem::disconnect()
+{
+  DBG("Disconnecting from PPP");
+  int ret = m_ppp.disconnect();
+  if(ret)
+  {
+    ERR("Disconnect returned %d, still trying to disconnect", ret);
+  }
+
+  //Ugly but leave dongle time to recover
+  Thread::wait(500);
+
+  if (m_onePort)
+  {
+    //ATCommandsInterface::ATResult result;
+    DBG("Starting AT thread");
+    ret = m_at.open();
+    if(ret)
+    {
+      return ret;
+    }
+  }
+
+  DBG("Trying to hangup");
+
+  if (m_onePort)
+  {
+    //Reinit AT parser
+    ret = m_at.init(false);
+    DBG("Result of command: Err code=%d\n", ret);
+    if(ret)
+    {
+      m_at.close(); // Closing AT parser
+      DBG("AT Parser closed, could not complete disconnection");
+      return NET_TIMEOUT;
+    }
+  
+  }
+  return OK;
+}
+
+int UbloxModem::sendSM(const char* number, const char* message)
+{
+  int ret = init();
+  if(ret)
+  {
+    return ret;
+  }
+
+  ISMSInterface* sms;
+  if (m_gsm)  sms = &m_GsmSms;
+  else        sms = &m_CdmaSms;
+  if(!m_smsInit)
+  {
+    ret = sms->init();
+    if(ret)
+    {
+      return ret;
+    }
+    m_smsInit = true;
+  }
+
+  ret = sms->send(number, message);
+  if(ret)
+  {
+    return ret;
+  }
+
+  return OK;
+}
+
+int UbloxModem::getSM(char* number, char* message, size_t maxLength)
+{
+  int ret = init();
+  if(ret)
+  {
+    return ret;
+  }
+
+  ISMSInterface* sms;
+  if (m_gsm)  sms = &m_GsmSms;
+  else        sms = &m_CdmaSms;
+  if(!m_smsInit)
+  {
+    ret = sms->init();
+    if(ret)
+    {
+      return ret;
+    }
+    m_smsInit = true;
+  }
+
+  ret = sms->get(number, message, maxLength);
+  if(ret)
+  {
+    return ret;
+  }
+
+  return OK;
+}
+
+int UbloxModem::getSMCount(size_t* pCount)
+{
+  int ret = init();
+  if(ret)
+  {
+    return ret;
+  }
+
+  ISMSInterface* sms;
+  if (m_gsm)  sms = &m_GsmSms;
+  else        sms = &m_CdmaSms;
+  if(!m_smsInit)
+  {
+    ret = sms->init();
+    if(ret)
+    {
+      return ret;
+    }
+    m_smsInit = true;
+  }
+
+  ret = sms->getCount(pCount);
+  if(ret)
+  {
+    return ret;
+  }
+
+  return OK;
+}
+
+ATCommandsInterface* UbloxModem::getATCommandsInterface()
+{
+  return &m_at;
+}
+
+int UbloxModem::init()
+{
+  if(m_atOpen)
+  {
+    return OK;
+  }
+  
+  DBG("Starting AT thread if needed");
+  int ret = m_at.open();
+  if(ret)
+  {
+    return ret;
+  }
+  
+  DBG("Sending initialisation commands");
+  ret = m_at.init(false);
+  if(ret)
+  {
+    return ret;
+  }
+
+  ATCommandsInterface::ATResult result;
+  CREGProcessor cregProcessor(m_gsm);
+  //Wait for network registration
+  do
+  {
+    DBG("Waiting for network registration");
+    ret = m_at.execute(cregProcessor.getAtCommand(), &cregProcessor, &result);
+    DBG("Result of command: Err code=%d\n", ret);
+    DBG("ATResult: AT return=%d (code %d)\n", result.result, result.code);
+    if(cregProcessor.getStatus() == CREGProcessor::STATUS_REGISTERING)
+    {
+      Thread::wait(3000);
+    }
+  } while(cregProcessor.getStatus() == CREGProcessor::STATUS_REGISTERING);
+  if(cregProcessor.getStatus() == CREGProcessor::STATUS_FAILED)
+  {
+    ERR("Registration denied");
+    return NET_AUTH;
+  }
+ 
+  m_atOpen = true;
+
+  return OK;
+}
+
+int UbloxModem::cleanup()
+{
+  if(m_ppp.isConnected())
+  {
+    WARN("Data connection is still open"); //Try to encourage good behaviour from the user
+    m_ppp.disconnect(); 
+  }
+  
+  m_smsInit = false;
+  m_ussdInit = false;
+  m_linkMonitorInit = false;
+  //We don't reset m_ipInit as PPPIPInterface::init() only needs to be called once
+  
+  if(m_atOpen)
+  {
+    m_at.close();
+    m_atOpen = false;
+  }
+  
+  return OK;
+}
+
+int UbloxModem::sendUSSD(const char* command, char* result, size_t maxLength)
+{
+  int ret = init();
+  if(ret)
+  {
+    return ret;
+  }
+
+  if(!m_ussdInit)
+  {
+    ret = m_ussd.init();
+    if(ret)
+    {
+      return ret;
+    }
+    m_ussdInit = true;
+  }
+
+  ret = m_ussd.send(command, result, maxLength);
+  if(ret)
+  {
+    return ret;
+  }
+
+  return OK;
+}
+
+int UbloxModem::getLinkState(int* pRssi, LinkMonitor::REGISTRATION_STATE* pRegistrationState, LinkMonitor::BEARER* pBearer)
+{
+  int ret = init();
+  if(ret)
+  {
+    return ret;
+  }
+  
+  if(!m_linkMonitorInit)
+  {
+    ret = m_linkMonitor.init();
+    if(ret)
+    {
+      return ret;
+    }
+    m_linkMonitorInit = true;
+  }
+
+  ret = m_linkMonitor.getState(pRssi, pRegistrationState, pBearer);
+  if(ret)
+  {
+    return ret;
+  }
+
+  return OK;
+}
+
+#include "USBHost.h"
+#include "UbloxGSMModemInitializer.h"
+#include "UbloxCDMAModemInitializer.h"
+
+UbloxUSBModem::UbloxUSBModem() :
+   UbloxModem(&m_atStream, &m_pppStream),
+   m_dongle(),                              // Construct WANDongle: USB interface with two serial channels to the modem (USBSerialStream objects)
+   m_atStream(m_dongle.getSerial(1)),       // AT commands are sent down one serial channel.
+   m_pppStream(m_dongle.getSerial(0)),      // PPP connections are managed via another serial channel.
+   m_dongleConnected(false)                 // Dongle is initially not ready for anything
+{
+    USBHost* host = USBHost::getHostInst();
+    m_dongle.addInitializer(new UbloxGSMModemInitializer(host));
+    m_dongle.addInitializer(new UbloxCDMAModemInitializer(host));
+}
+
+int UbloxUSBModem::init()
+{
+  if( !m_dongleConnected )
+  {
+    m_dongleConnected = true;
+    while( !m_dongle.connected() )
+    {
+      m_dongle.tryConnect();
+      Thread::wait(10);
+    }
+    if(m_dongle.getDongleType() == WAN_DONGLE_TYPE_UBLOX_LISAU200)
+    {
+      INFO("Using a u-blox LISA-U200 3G/WCDMA Modem");
+    }
+    else if(m_dongle.getDongleType() == WAN_DONGLE_TYPE_UBLOX_LISAC200)
+    {
+      INFO("Using a u-blox LISA-C200 CDMA Modem");
+      m_gsm = false;
+      m_onePort = true;
+    }
+    else
+    {
+      WARN("Using an Unknown Dongle");
+    }
+  }
+  return UbloxModem::init();
+}
+
+int UbloxUSBModem::cleanup()
+{
+  UbloxModem::cleanup();
+  m_dongle.disconnect();
+  m_dongleConnected = false;
+  return OK;
+}
+
+UbloxSerModem::UbloxSerModem() :
+   UbloxModem(&m_atStream, NULL),
+   m_Serial(P0_15,P0_16),
+   m_atStream(m_Serial)
+{
+  m_Serial.baud(115200);
+}
+