Cellular library for MTS Socket Modem Arduino Shield devices from Multi-Tech Systems

Dependents:   mtsas mtsas mtsas mtsas

Cellular/EasyIP.h

Committer:
Vanger
Date:
2014-08-01
Revision:
45:b9ead235ab7c
Parent:
44:bf07f3d85822
Child:
49:1fc51c53cebf

File content as of revision 45:b9ead235ab7c:

#ifndef SMC_H
#define SMC_H

#include <string>
#include <vector>

#include "MTSBufferedIO.h"
#include "Cellular.h"

namespace mts
{
    /** This is a class for communicating without a Multi-Tech Systems SocketModem iCell. Instead, 
    * it uses the hayes command set to directly implement the same UIP interface to the radio.
    * (See the UIP class and documentation, "UIP.h")
    * This class supports four main types of cellular radio interactions including:
    * configuration and status AT-command processing, SMS processing, TCP Socket data connections,
    * and UDP data connections. It should be noted that the radio can not process commands or
    * SMS messages while having an open data connection at the same time. The concurrent
    * capability may be added in a future release. This class also inherits from IPStack
    * providing a common set of commands for communication devices that have an onboard
    * IP Stack. It is also integrated with the standard mbed Sockets package and can therefore
    * be used seamlessly with clients and services built on top of this interface already within
    * the mbed library.
    * The default baud rate for the cellular radio is 115200 bps.
    *
    * @code
    * #include "mbed.h"
    * #include "mtsas.h"
    * #include "TCPSocketConnection.h"
    *
    * int main(){
    *   //Modify to match your apn if you are using an HSPA radio with a SIM card
    *   const char APN[] = "";
    *
    *   //Sets the log level to INFO, which is about midway on priority levels
    *   //Possible levels: FATAL, ERROR, WARNING, INFO, DEBUG, TRACE, NONE
    *   MTSLog::setLogLevel(MTSLog::TRACE_LEVEL);
    *
    *   // STMicro Nucelo F401RE
    *   // The supported jumper configurations of the MTSAS do not line up with
    *   // the pin mapping of the Nucleo F401RE. Therefore, the MTSAS serial TX
    *   // pin (JP8 Pin 2) must be manually jumped to Serial1 RX (Shield pin D2)
    *   // and the MTSAS serial RX pin (JP9 Pin 2) pin must be manually jumped to
    *   // Serial1 TX (Shield pin D8).
    *   // Uncomment the following line to use the STMicro Nuceleo F401RE
    *   MTSSerialFlowControl* io = new MTSSerialFlowControl(D8, D2, D3, D6);
    *
    *   // Freescale KL46Z
    *   // To configure the pins for the Freescale KL46Z board, use configuration B
    *   // for the SocketModem.
    *   // Uncomment the following line to use the Freescale KL46Z board
    *   // MTSSerialFlowControl* io = new MTSSerialFlowControl(D2, D9, D3, D6);
    *
    *   // Freescale KL64F
    *   // To configure the pins for the Freescale KL46Z board, use configuration A
    *   // for the SocketModem.
    *   // Uncomment te following line to use the Freescale KL46F board
    *   // MTSSerialFlowControl* io = new MTSSerialFlowControl(D1, D0, D3, D6);
    *
    *   // Sets the baudrate for communicating with the radio
    *   io->baud(115200);
    *
    *   // Sets up the interfacing with the radio through the MTS library
    *   Cellular* radio = CellularFactory::create(io);
    *   radio->configureSignals(D4, D7, RESET);
    *   Transport::setTransport(radio);
    *
    *   // Sets the APN on the device (if necessary)
    *   for (int i = 0; i < 10; i++) {
    *       if (i >= 10) {
    *           logError("Failed to set APN to %s", APN);
    *       }
    *       if (radio->setApn(APN) == MTS_SUCCESS) {
    *           logInfo("Successfully set APN to %s", APN);
    *           break;
    *       } else {
    *           wait(1);
    *       }
    *   }
    *
    *   //Establish PPP link
    *   for (int i = 0; i < 10; i++) {
    *       if (i >= 10) {
    *       logError("Failed to establish PPP link");
    *       }
    *       if (radio->connect() == true) {
    *           logInfo("Successfully established PPP link");
    *           break;
    *       } else {
    *           wait(1);
    *       }
    *   }
    *
    *   //Ping google.com (optional)
    *   for (int i = 0; i < 10; i++) {
    *       if (i >= 10) {
    *           logError("Failed to ping www.google.com");
    *       }
    *       if (radio->ping("www.google.com") == true) {
    *           logInfo("Successfully pinged www.google.com");
    *           break;
    *       } else {
    *           wait(1);
    *       }
    *   }
    *
    *   //Used for packet verification from server's data response
    *   const char PATTERN_LINE1[] = "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()[]{}|";
    *   const char PATTERN[] = "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()[]{}|\r\n"
    *                          "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()[]{}/\r\n"
    *                          "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()[]{}-\r\n"
    *                          "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()[]{}\\\r\n"
    *                          "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()[]{}|\r\n"
    *                          "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()[]{}/\r\n"
    *                          "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()[]{}-\r\n"
    *                          "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()[]{}\\\r\n"
    *                          "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()[]{}*\r\n";
    *   
    *   const char MENU_LINE1[] = "send ascii pattern until keypress";
    *   const char MENU[] = "1       send ascii pattern until keypress"
    *                       "2       send ascii pattern (numbered)"
    *                       "3       send pattern and close socket"
    *                       "4       send [ETX] and wait for keypress"
    *                       "5       send [DLE] and wait for keypress"
    *                       "6       send all hex values (00-FF)"
    *                       "q       quit"
    *                       ">:";
    *   
    *   const char TCP_TEST_SERVER[] = "204.26.122.5";
    *   const int TCP_TEST_PORT = 7000;
    *   
    *   //Creates TCP socket pointer instance
    *   TCPSocketConnection* sock = new TCPSocketConnection();
    *   //Turns off read_blocking and sets socket timeout to 2s
    *   sock->set_blocking(false, 2);
    *   
    *   Timer tmr; //Used for timeouts
    *   int bytesRead = 0; //Number of bytes read
    *   const int readSize = 1024; //Size of buffer
    *   char buffer[readSize] = {0}; //Read buffer
    *   string result; //Result as a string
    *   
    *   //Open TCP socket
    *   for (int i = 0; i < 5; i++) {
    *       if (i >= 5) {
    *           logError("Failed to open socket");
    *       }
    *       if (! sock->connect(TCP_TEST_SERVER, TCP_TEST_PORT)) {
    *           logInfo("Opened TCP server");
    *           break;
    *       } else {
    *           wait(1);
    *       }
    *   }
    *   
    *   //Waiting for menu from remote server
    *   logInfo("Receiving Menu");
    *   tmr.reset();
    *   tmr.start();
    *   do {
    *       bytesRead = sock->receive(buffer, readSize);
    *       if (bytesRead > 0) {
    *           result.append(buffer, bytesRead);
    *       }
    *       logInfo("Total Bytes Read: %d", result.size());
    *       if(result.find(MENU_LINE1) != std::string::npos) {
    *           break;
    *       }
    *   } while(tmr.read() <= 40);
    *   
    *   wait(5);
    *
    *   logInfo("Received: [%d] [%s]", result.size(), result.c_str());
    *
    *   //Checking that menu was successfully received
    *   size_t pos = result.find(MENU_LINE1);
    *   if(pos != string::npos) {
    *       logInfo("Found Menu 1st Line");
    *   } else {
    *       logError("Failed To Find Menu 1st Line");
    *       sock->close();
    *       return 0;
    *   }
    *
    *   result.clear();
    *
    *   //Sends a response of '2' back to choose option 2 from the menu
    *   logInfo("Writing To Socket: 2");
    *   if(sock->send("2\r\n", 3) == 3) {
    *       logInfo("Successfully Wrote '2'");
    *   } else {
    *       logError("Failed To Write '2'");
    *       sock->close();
    *       return 0;
    *   }
    *   logInfo("Expecting 'how many ? >:'");
    *   tmr.reset();
    *   tmr.start();
    *   do {
    *       bytesRead = sock->receive(buffer, readSize);
    *       if (bytesRead > 0) {
    *           result.append(buffer, bytesRead);
    *       }
    *       logInfo("Total Bytes Read: %d", result.size());
    *       if(result.find("how many") != std::string::npos) {
    *           break;
    *       }
    *   } while(tmr.read() <= 40);
    *
    *   logInfo("Received: [%d] [%s]", result.size(), result.c_str());
    *
    *   //Sends 2 to have the server send the pattern twice
    *   if(result.find("how many") != std::string::npos) {
    *       logInfo("Successfully Found 'how many'");
    *       logInfo("Writing To Socket: 2");
    *       if(sock->send("2\r\n", 3) == 3) {
    *           logInfo("Successfully wrote '2'");
    *       } else {
    *           logError("Failed to write '2'");
    *           sock->close();
    *           return 0;
    *       }
    *   } else {
    *       logError("didn't receive 'how many'");
    *       sock->close();
    *       return 0;
    *   }
    *
    *   result.clear();
    *
    *   //Receives data from request sent to server
    *   logInfo("Receiving Data");
    *   tmr.reset();
    *   tmr.start();
    *   do {
    *       bytesRead = sock->receive(buffer, readSize);
    *       if (bytesRead > 0) {
    *           result.append(buffer, bytesRead);
    *       }
    *       logInfo("Total Bytes Read: %d", result.size());
    *       if(result.size() >= 1645) {
    *           break;
    *       }
    *   } while(tmr.read() <= 40);
    *
    *   logInfo("Received Data: [%d] [%s]", result.size(), result.c_str());
    *
    *   //Compares received data with expected data
    *   pos = result.find(PATTERN_LINE1);
    *   if(pos != string::npos) {
    *       int patternSize = sizeof(PATTERN) - 1;
    *       const char* ptr = &result.data()[pos];
    *       bool match = true;
    *       for(int i = 0; i < patternSize; i++) {
    *           if(PATTERN[i] != ptr[i]) {
    *               logError("1st Pattern Doesn't Match At [%d]", i);
    *               logError("Pattern [%02X]  Buffer [%02X]", PATTERN[i], ptr[i]);
    *               match = false;
    *               break;
    *           }
    *       }
    *       if(match) {
    *           logInfo("Found 1st Pattern");
    *       } else {
    *           logError("Failed To Find 1st Pattern");
    *           sock->close();
    *           return 0;
    *       }
    *
    *       pos = result.find(PATTERN_LINE1, pos + patternSize);
    *       if(pos != std::string::npos) {
    *           ptr = &result.data()[pos];
    *           match = true;
    *           for(int i = 0; i < patternSize; i++) {
    *               if(PATTERN[i] != ptr[i]) {
    *                   logError("2nd Pattern Doesn't Match At [%d]", i);
    *                   logError("Pattern [%02X]  Buffer [%02X]", PATTERN[i], ptr[i]);
    *                   match = false;
    *                   break;
    *               }
    *           }
    *           if(match) {
    *               logInfo("Found 2nd Pattern");
    *           } else {
    *               logError("Failed To Find 2nd Pattern");
    *               sock->close();
    *               return 0;
    *           }
    *       }
    *   } else {
    *       logError("Failed To Find Pattern 1st Line");
    *       sock->close();
    *       return 0;
    *   }
    *
    *   //Clears the result, and closes the socket connection.
    *   result.clear();
    *   sock->close();
    *   
    *   //Disconnect ppp link
    *   radio->disconnect();
    *   
    *   logInfo("End of example code");
    *   return 0;
    * }
    * @endcode
    */
class EasyIP : public Cellular
{
public:
    /** This static function is used to create or get a reference to a
    * Cellular object. Cellular uses the singleton pattern, which means
    * that you can only have one existing at a time. The first time you
    * call getInstance this method creates a new uninitialized Cellular
    * object and returns it. All future calls to this method will return
    * a reference to the instance created during the first call. Note that
    * you must call init on the returned instance before mnaking any other
    * calls. If using this class's bindings to any of the Socket package
    * classes like TCPSocketConnection, you must call this method and the
    * init method on the returned object first, before even creating the
    * other objects.
    *
    * @returns a reference to the single Cellular obect that has been created.
    */
    EasyIP(Radio type);
    
    /** Destructs a Cellular object and frees all related resources.
    */
    ~EasyIP();
    
    /** Initializes the MTS IO buffer
    */
    virtual bool init(MTSBufferedIO* io);
    
    /** PPP connect command.
    * Connects the radio to the cellular network.
    *
    * @returns true if PPP connection to the network succeeded,
    * false if the PPP connection failed.
    */
    virtual bool connect();
    
    /** PPP disconnect command.
    * Disconnects from the PPP network, and will also close active socket
    * connection if open. 
    */
    virtual void disconnect();
    
    /** Checks if the radio is connected to the cell network.
    * Checks antenna signal, cell tower registration, and context activation
    * before finally pinging (4 pings, 32 bytes each) to confirm PPP connection
    * to network. Will return true if there is an open socket connection as well.
    *
    * @returns true if there is a PPP connection to the cell network, false
    * if there is no PPP connection to the cell network.
    */
    virtual bool isConnected();
    
    /** Resets the radio/modem.
    * Disconnects all active PPP and socket connections to do so.
    */
    virtual void reset();

    // TCP and UDP Socket related commands
    // For behavior of the following methods refer to IPStack.h documentation
    virtual bool bind(unsigned int port);
    virtual bool open(const std::string& address, unsigned int port, Mode mode);
    virtual bool isOpen();
    virtual bool close();
    virtual int read(char* data, int max, int timeout = -1);    
    virtual int write(const char* data, int length, int timeout = -1);
    virtual unsigned int readable();
    virtual unsigned int writeable();
    virtual bool ping(const std::string& address = "8.8.8.8"); //Google DNS server used as default ping address
    virtual std::string getDeviceIP();
    virtual bool setDeviceIP(std::string address = "DHCP");
    
    /** Sets the APN
    * 
    * @param apn c-string of the APN to use
    *
    * @returns MTS_SUCCESS if the APN was set, or is not needed, else it
    * returns the result of the AT command sent to the radio to set the APN
    */
    virtual Code setApn(const std::string& apn);
    
    /** A method for configuring command ehco capability on the radio. This command
    * sets whether sent characters are echoed back from the radio, in which case you
    * will receive back every command you send.
    *
    * @param state if true echo will be turned off, otherwise it will be turned on.
    * @returns the standard AT Code enumeration.
    */
    virtual Code echo(bool state);

    /** This method can be used to trade socket functionality for performance.
    * Can disable checking socket closed messages from the data socket, and thus the socket
    * will only be visibly closed to the local side if the radio is explicitly checked, or
    * the socket is closed by the local side through the use of physical pin manipulation.
    *
    * Uses the Hayes escape sequence (1 second pause, "+++", 1 second pause) to exit the socket
    * connection to check if a received "NO CARRIER" string is from the radio indicating the socket
    * has been closed, or is merely part of the data stream. Should not occur very often, however, if 
    * data carrying the string "NO CARRIER" is going to be transmitted frequently, then the socket should
    * be set closeable and physical-socket-closing-means be used instead to reduce the large amount of
    * overhead switching from checking the validity of the "NO CARRIER" message being and indication of
    * the socket connection being closed.
    *
    * @param enabled set to true if you want the socket closeable, otherwise false. The default
    * is true.
    * @returns the standard AT Code enumeration.
    */
    virtual Code setSocketCloseable(bool enabled = true);
    
private:
    /** Function that sends +++ to the radio to exit data mode
    * returns true if it successfully exits from online mode, else
    * it returns false. Used due to the fact that AT commands
    * cannot be sent while in data mode.
    *
    * @returns true if the radio dropped from data mode to commande mode
    * or is already in command mode (socket is still open in the background),
    * and false if the radio failed to switch to command mode.
    */
    virtual bool sendEscapeCommand(); 
    
    /** Switches to command mode, queries the status of the socket connection,
    * and then returns back to the active socket connection (if still open)
    *
    * @returns true if a socket is currently open, otherwise it returns false
    */
    virtual bool socketCheck();
};

}

#endif /* SMC_H */