3G Shield library, port for mbed. (from http://3gsa.org/ ) see: https://developer.mbed.org/users/phsfan/notebook/phsshield/

Dependents:   PHSShield_test PHSShield_connectTCP PHSShield_Twitter PHSShield_httpGET

Revision:
0:ca471c239d5f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/a3gs.cpp	Tue Mar 10 01:12:17 2015 +0000
@@ -0,0 +1,1898 @@
+//***********************************************************
+//	a3gs.cpp -- IEM 3G Sheild for Arduino Control Library
+//
+//	History:
+//		R1.0 2012/10/06  1st Release for IEM 3G Shiled(Ver1.0)
+//		R1.1 2012/10/14  2nd Release(Operate by few memories)
+//						 (modify sendSMS(),onSMSReceived(),httpGET(),httpPOST(),tweet(),connectTCP())
+//		R1.2 2012/10/28  Bug fix (httpGET(), httpPOST(), tweet(), availableSMS() and redSMS())
+//		R1.3 2013/01/01  Support "https" in httpGET() and httpPost() [if gw3g version is above 1.1]
+//		R1.4 2013/04/16  Bug fix (discardUntil() has never return)
+//		R2.0 2013/07/12  Change interface of TCP/IP and discardUntil()
+//						 some bugs fix(httpPOST, httpGET, read and getResult)
+//		R2.1 2013/08/17  Buf fix (discardUntil() has never return)
+//		R2.2 2013/10/27  Bug fix (read(res, reslength))
+//		R2.3 2014/07/06  Support binary data in read() and add new version read() function, bug fix write()
+//		R3.0 2014/08/30  Support for gw3g R2.0 and add follow functions:
+//						   setAirplaneMode(), put(), get()
+//
+//	Author:
+//		3G Shield Alliance and Atushi Daikoku
+//
+//	Notes:
+//		Lower compatible with Arduino GSM/GPRS Shield library.
+//		Notices as bellow:
+//		- Use SoftwareSerial library (RxD is D4, TxD is D5).
+//		- Use Interrupt 0(D2) for SMS arrival notification.
+//		- Use D6 and D7 are used for Power control.
+//		If You use Leonard, Mega or ADK(Mega2560) then you must change IEM_RXD_PIN and IEM_TXD_PIN for suitable.
+//***********************************************************
+/* Modified by 2015 phsfan
+ *  for ABIT PHS Shield on mbed
+ */
+
+//#define	DEBUG				1		// Define if you want to debug
+
+#include "mbed.h"
+#include "a3gs.h"
+
+// Macros
+#ifdef DEBUG
+#  define DEBUG_PRINT(m,v)	{ printf("%s:%s\r\n", m, v); }
+#  define DEBUG_PRINT_NUM(m,v)	{ printf("%s:%d\r\n", m, v); }
+#  define DBG(...) printf("" __VA_ARGS__);
+#else
+#  define DEBUG_PRINT(m,v)	// do nothing
+#  define DEBUG_PRINT_NUM(m,v)	// do nothing
+#  define DBG(...)
+#endif
+	// Constants for pins
+#define	IEM_INT_PIN			2		// D2(INT0)
+#define	IEM_RXD_PIN			4		// D4(For example,If you use Leonardo or Mega etc. then change to D10..)
+#define	IEM_TXD_PIN			5		// D5(For example,If you use Leonardo or Mega etc. then change to D11..)
+#define	IEM_POWER_PIN		6		// D6
+#define	IEM_REGULATOR_PIN	7		// D7
+	// Constants for getTime2()
+#define	SECS_PER_MIN 		(60UL)
+#define	SECS_PER_HOUR		(3600UL)
+#define	SECS_PER_DAY		(SECS_PER_HOUR * 24UL)
+#define	DAYS_PER_WEEK		(7UL)
+#define	SECS_YR_2000		(946684800UL)		// the time at the start of y2k
+	// Leap year calulator expects year argument as years offset from 1970
+#define	LEAP_YEAR(Y)		(((1970+Y)>0) && !((1970+Y)%4) && (((1970+Y)%100) || !((1970+Y)%400)))
+	// Command execution timeout values
+#define	TIMEOUT_LOCAL		5000	// Timeout value of local functions [mS]
+#define	TIMEOUT_NETWORK		35000	// Timeout value of communication functions [mS]
+#define	TIMEOUT_GPS			180000	// Timeout value og GPS locationing [mS]
+	// Misc.
+#define	ISDIGIT(c)			((c) >= '0' && (c) <= '9')
+	// Interrupt number for SMS
+#define	INTNO_SMS			0		// Use INT0
+	// IEM Version
+#define	MIN_IEM_VERSION		2.0		// A necessary minimum IEM version of this library
+
+// Global variables
+static char gWorkBuffer[256];		// Buffer for working(Mega..)
+static void (*SMShandler)(void);								// ISR for SMS Notification
+
+
+A3GS::A3GS(PinName tx, PinName rx, PinName intin, PinName power, PinName reg, int baud) :
+	iemSerial(tx, rx), _intin(intin), _power(power), _reg(reg)
+{
+	iemSerial.baud(baud);
+	_intin.mode(PullDown);
+	_power = 0;
+	_reg = 0;
+}
+
+//***************************
+//	begin()
+//
+//	@description
+//		Begin to use 3G shield function
+//	@return value
+//		0 .. OK(at least, Packet Switch or Circuit Switch is availabled)
+//		otherwise .. NG
+//	@param
+//		pin : Not used
+//	@note
+//		
+//***************************
+int A3GS::begin(char* pin)
+{
+	char	version[a3gsMAX_VERSION_LENGTH+1];
+//--
+	DEBUG_PRINT(">begin()", "default");
+
+	// Initilalize global variables and detach INT0
+	_status = IDLE;
+	SMShandler = NULL;
+	_intin.fall(NULL);
+
+	// Get iem version and check it
+	if (getVersion(version) != 0)
+		return 1;	// NG -- Can't get version
+
+	if (atof(version) < MIN_IEM_VERSION) {
+		DEBUG_PRINT(">getVersion()", "IEM version is old");
+		return 2;	// NG -- IEM Version is old
+	}
+
+	return a3gsSUCCESS;  // OK
+}
+
+//***************************
+//	begin()
+//
+//	@description
+//		Begin to use 3G shield function
+//	@return value
+//		0 .. OK(at least, Packet Switch or Circuit Switch is availabled)
+//		otherwise .. NG
+//	@param
+//		pin : Not used
+//		baudrate : baudrate to begin (2400/4800/9600/19200/38400/57600/115200)
+//	@note
+//		This function use to specify non-default baudrate.
+//***************************
+int A3GS::begin(char* pin, uint32_t baudrate)
+{
+	char	version[a3gsMAX_VERSION_LENGTH+1];
+//--
+	DEBUG_PRINT_NUM(">begin()", baudrate);
+
+	iemSerial.baud(baudrate);
+
+	// Initilalize global variables and detach INT0
+	_status = IDLE;
+	SMShandler = NULL;
+	_intin.fall(NULL);
+
+	// Get iem version and check it
+	if (getVersion(version) != 0)
+		return 1;	// NG -- Can't get version
+
+	if (atof(version) < MIN_IEM_VERSION) {
+		DEBUG_PRINT(">getVersion()", "IEM version is old");
+		return 2;	// NG -- IEM Version is old
+	}
+
+	return a3gsSUCCESS;  // OK
+}
+
+//***************************
+//	end()
+//
+//	@description
+//		End to use 3G shield function
+//	@return value
+//		0 .. always
+//	@param
+//		none
+//	@note
+//		re-enable serial communication, call a3gs.begin()
+//***************************
+int A3GS::end(void)
+{
+	// Clear interrupt handler
+	if (SMShandler != NULL) {
+		_intin.fall(NULL);
+		SMShandler = NULL;
+	}
+
+	return a3gsSUCCESS;	// OK
+}
+
+//***************************
+//	restart()
+//
+//	@description
+//		Restart 3G shield and clear SMS handler
+//	@return value
+//		0 .. always
+//	@param
+//		pin : Not used
+//	@note
+//		Reboot time is about 40 Sec.
+//***************************
+int A3GS::restart(char* pin)
+{
+	// Clear interrupt handler
+	if (SMShandler != NULL) {
+		_intin.fall(NULL);
+		SMShandler = NULL;
+	}
+
+	sendCommand("$YE");		// Send "Reset IEM" Command
+
+	return a3gsSUCCESS;	// OK
+}
+
+//***************************
+//	start()
+//
+//	@description
+//		Power on 3G shield and wait for ready to use
+//	@return value
+//		0 .. always
+//	@param
+//		pin : Not used
+//	@note
+//		Start up time is about 40 Sec.
+//***************************
+int A3GS::start(char* pin)
+{
+	_reg = 1;	// Regulator turn on
+	wait_ms(1000);		// Wait a moment
+	_power = 0;
+	wait_ms(2000);		// Make low STATUS_PB_WALK -> Turn on IEM
+	_power = 1;
+
+	DEBUG_PRINT(">start()", "Turn on and wait for a moment..");
+
+	wait_ms(35000);	// Wait for ready IEM
+//	wait_ms(1000);	// Wait for ready IEM
+
+	DEBUG_PRINT(">start()", "IEM is available now.");
+
+	return a3gsSUCCESS;	// OK
+}
+
+//***************************
+//	shutdown()
+//
+//	@description
+//		Power off 3G shield
+//	@return value
+//		0 .. always
+//	@param
+//		none
+//	@note
+//		Shutdown time is about 15 Sec.
+//***************************
+int A3GS::shutdown(void)
+{
+	sendCommand("$YE");		// Send "Reset IEM" Command
+
+	wait_ms(15000);	// Wait a moment
+
+	_reg = 0;	// Regulator turn off
+
+	return a3gsSUCCESS;	// OK
+}
+
+//***************************
+//	getService
+//
+//	@description
+//		Get network service status
+//	@return value
+//		0 .. Succeeded, otherwise .. Failed
+//	@param
+//		status .. [OUT] SRV* (see a3gs.h)
+//	@note
+//		
+//***************************
+int A3GS::getServices(int& status)
+{
+	char responses[12];		// Format "$YS=OK 9" or "$YS=NG errno"
+	int  length = sizeof(responses);
+//--
+	sendCommand("$YS");	// Send "Get Services" command
+
+	if (getResult(responses, &length, TIMEOUT_LOCAL))
+		return 1;	// NG -- maybe timeout
+
+	DEBUG_PRINT(">getServices()", responses);
+
+	// parse response
+	switch (responses[7]) {
+		case '0' :		// NO_SRV
+			status = a3gsSRV_NO;
+			break;
+		case '1' :		// PS_ONLY
+			status = a3gsSRV_PS;
+			break;
+		case '2' :		// CS_ONLY
+			status = a3gsSRV_CS;
+			break;
+		case '3' :		// BOTH
+			status = a3gsSRV_BOTH;
+			break;
+		default : 		// bug
+			return 1;		// NG -- Can't get service
+	}
+
+	return a3gsSUCCESS;		// OK
+}
+
+//***************************
+//	getIMEI()
+//
+//	@description
+//		Get IEM's IMEI
+//	@return value
+//		0 .. Succeeded
+//		otherwise .. Failed
+//	@param
+//		imei : [OUT] IMEI (a3gsIMEI_SIZE bytes space requred)
+//	@note
+//		
+//***************************
+int A3GS::getIMEI(char* imei)
+{
+	char responses[a3gsIMEI_SIZE+10];  // Format "$YI=OK 99..99" or "$YI=NG errno"
+	int  length = sizeof(responses);
+//--
+	sendCommand("$YI");		// Send "Get IMEI" command
+
+	if (getResult(responses, &length, TIMEOUT_LOCAL))
+		return 1;  // NG -- maybe timeout
+
+	DEBUG_PRINT(">getIMEI()", responses);
+
+	// parse response
+	if (strncmp(responses, "$YI=OK", 6))
+		return 1;	// NG -- Can't get IMEI
+
+	strncpy(imei, responses+7, a3gsIMEI_SIZE);
+	imei[a3gsIMEI_SIZE] = '\0';
+
+	return a3gsSUCCESS;	// OK
+}
+
+//***************************
+//	setBaudrate
+//
+//	@description
+//		Set UART baudrate
+//	@return value
+//		0 .. OK
+//		1 .. NG
+//	@param
+//		baudrate : baudrate to set (2400/4800/9600/19200/38400/57600/115200)
+//	@note
+//		New baudrate will be validated after reset IEM
+//***************************
+int A3GS::setBaudrate(uint32_t baudrate)
+{
+	char command[13];
+	char responses[12];  // Format "$YB=OK" or "$YB=NG errno"
+	int  length = sizeof(responses);
+//--
+	switch (baudrate) {
+		case 2400 :
+		case 4800 :
+		case 9600 :
+		case 19200 :
+		case 38400 :
+		case 57600 :
+		case 115200 :
+			break;
+		default :
+			return 1;	// NG -- Bad parameter
+	}
+
+	// Make command and send it
+	sprintf(command, "$YB %lu", baudrate);
+	sendCommand(command);	// Send "Set Baudrate" command
+
+	if (getResult(responses, &length, TIMEOUT_LOCAL))
+		return 1;  // NG -- maybe timeout
+
+	DEBUG_PRINT(">setBaudrate()", responses);
+
+	// parse response
+	if (strncmp(responses, "$YB=OK", 6))
+		return 1;	// NG -- Can't set baudrate
+
+	return a3gsSUCCESS;	// OK
+}
+
+//***************************
+//	setLED
+//
+//	@description
+//		Set LED On/Off
+//	@return value
+//		0 .. OK
+//		1 .. NG
+//	@param
+//		sw : true is On, false is Off
+//	@note
+//		There is LED on 3G shield board(named LED1)
+//***************************
+int A3GS::setLED(bool sw)
+{
+	char command[10];
+	char responses[12];  // Format "$YL=OK" or "$YL=NG errno"
+	int  length = sizeof(responses);
+//--
+	// Make command and send it
+	sprintf(command, "$YL %d", (sw ? 1 : 0));
+	sendCommand(command);	// Send "Set LED" command
+
+	if (getResult(responses, &length, TIMEOUT_LOCAL))
+		return 1;  // NG -- maybe timeout
+
+	DEBUG_PRINT(">setLED()", responses);
+
+	// parse response
+	if (strncmp(responses, "$YL=OK", 6))
+		return 1;	// NG -- Can't set led
+
+	return a3gsSUCCESS;	// OK
+}
+
+//***************************
+//	setAirplaneMode
+//
+//	@description
+//		Set airplane mode
+//	@return value
+//		0 .. OK
+//		1 .. NG
+//	@param
+//		sw : true is On(Airplane mode), false is Off(Normal mode)
+//	@note
+//		R3.0 add
+//***************************
+int A3GS::setAirplaneMode(bool sw)
+{
+	char command[10];
+	char responses[12];  // Format "$YP=OK" or "$YP=NG errno"
+	int  length = sizeof(responses);
+//--
+	// Make command and send it
+	sprintf(command, "$YP %d", (sw ? 1 : 0));
+	sendCommand(command);	// Send "Set Airplane mode" command
+
+	if (getResult(responses, &length, TIMEOUT_LOCAL))
+		return 1;  // NG -- maybe timeout
+
+	DEBUG_PRINT(">setAirplaneMode()", responses);
+
+	// parse response
+	if (strncmp(responses, "$YP=OK", 6))
+		return 1;	// NG -- Can't set airplane mode
+
+	return a3gsSUCCESS;	// OK
+}
+
+//***************************
+//	sendSMS
+//
+//	@description
+//		send SMS
+//	@return value
+//		0 .. OK
+//		otherwise .. NG
+//	@param
+//		to : MSN(10 digits)
+//		msg : message string(ASCII or UNICODE)
+//		encode : message's char set(default is ASCII)
+//***************************
+int A3GS::sendSMS(const char* to, const char* msg, int encode)
+{
+	char responses[20];  // FORMAT: "$SS=OK" or "$SS=NG errno"
+	int  length = sizeof(responses);
+//--
+	// Check parameter sizes
+	if (strlen(msg) > a3gsMAX_SMS_LENGTH)
+		return 1;	// NG - too long message
+	if (strlen(to) > a3gsMAX_MSN_LENGTH)
+		return 1;	// NG - too long msn
+
+	// Send comamnd little by little
+	sendData("$SS ");
+	sendData(to);
+	sendData(" \"");
+	sendData(msg);
+	sendData("\" ");
+	if (encode == a3gsCS_UNICODE)
+		sendCommand("UNICODE");
+	else
+		sendCommand("ASCII");
+
+	DEBUG_PRINT(">sendSMS", to);
+
+	if (getResult(responses, &length, TIMEOUT_NETWORK))
+		return 1;	// NG -- maybe timeout
+
+	DEBUG_PRINT(">sendSMS()", responses);
+
+	if (strncmp(responses, "$SS=OK", 6))
+		return 1;	// NG -- send error
+
+	return a3gsSUCCESS;	// OK
+}
+
+//***************************
+//	availableSMS
+//
+//	@description
+//		Check SMS is available or not
+//	@return value
+//		true .. Available
+//		false .. Not Available
+//	@param
+//		none
+//	@note
+//		
+//***************************
+bool A3GS::availableSMS()
+{
+  char responses[20];		// FORMAT: "$SC=OK n" or "$SC=NG errno"
+  int  length = sizeof(responses);
+//--
+	sendCommand("$SC");	// Send "Check SMS" command
+
+	if (getResult(responses, &length, TIMEOUT_LOCAL))
+		return false;	// NG -- maybe timeout
+
+	DEBUG_PRINT(">availableSMS()", responses);
+
+	if (strncmp(responses, "$SC=OK 1", 8))
+		return false;	// Not available
+
+	return true;	// Available
+}
+
+//***************************
+//	readSMS
+//
+//	@description
+//		Read received SMS
+//	@return value
+//		0 .. OK
+//		otherwise .. NG
+//	@param
+//		msg : [OUT] read message string
+//		msglength : msg length(max a3gsMAX_SMS_LENGTH)
+//		number : [OUT] MSN
+//		nlength : MSN length(max a3gsMAX_MSN_LENGTH)
+//	@note
+//		Use gWorkBuffer and destory it
+//***************************
+int A3GS::readSMS(char* msg, int msglength, char* number, int nlength)
+{
+	int	length = sizeof(gWorkBuffer);
+	char *cp;
+//--
+	sendCommand("$SR");		// Send "Read SMS" command
+
+	if (getResult(gWorkBuffer, &length, TIMEOUT_LOCAL))
+		return 1;	// NG -- maybe timeout
+
+	if (! strncmp(gWorkBuffer, "$SR=NG", 6))
+		return 1;	// NG -- No SMS
+
+	// Parse number(msn)
+	for (cp = gWorkBuffer+7; *cp != '\0'; cp++) {
+		while (ISDIGIT(*cp) && nlength-- > 1)
+			*number++ = *cp++;
+		*number = '\0';
+		DEBUG_PRINT(">readSMS() msn", number);
+		break;
+	}
+
+	// Parse message
+	for ( ; *cp != '\0'; cp++) {
+		if (*cp != '\"')
+			continue;	// Skip until "
+		cp++;	// skip "
+		while (*cp != '\0' && *cp != '\"' && msglength-- > 1)
+			*msg++ = *cp++;
+		*msg = '\0';
+		DEBUG_PRINT(">readSMS() msg", msg);
+		break;
+	}
+
+	return a3gsSUCCESS;	// OK
+}
+
+//***************************
+//	onSMSReceived
+//
+//	@description
+//		Set call back function when SMS was receieved
+//	@return value
+//		0 .. OK(always)
+//	@param
+//		handler : Call back function(Set) or NULL(Cancel)
+//	@note
+//		This function use attachInterrupt() and detachInterrupt() with interrupt number 0(D2).
+//		Interrupt mode is "LOW".
+//		It is necessary to call onSMSReceived() each time and to set up handler to use continuously.
+//***************************
+int A3GS::onSMSReceived(void (*handler)(void))
+{
+	if (handler == NULL)
+		_intin.fall(NULL);
+	else
+		_intin.fall(this, &A3GS::handleINT0);
+
+	SMShandler = handler;
+
+	return a3gsSUCCESS;	// OK
+}
+    
+//***************************
+//	httpGET
+//
+//	@description
+//		Request HTTP/GET to server and port
+//
+//	@return value
+//		0 .. OK
+//		otherwise .. NG
+//	@param
+//		server : server name(ex. "www,google.com")
+//		port : port number(ex. 80)
+//		path : path(ex. service/index.html)
+//		result : [OUT] responce raw data(no escaped, '\0' terminated)
+//		resultlength ; "result" size(max MAX_RESULT_LENGTH)
+//		ssled : if true then use https(SSL), otherwise use http
+//	@note
+//		This function is synchronized.
+//		Support optional parameter "header" since @R3.0
+//***************************
+int A3GS::httpGET(const char* server, uint16_t port, const char* path, char* result, int resultlength, bool ssled, const char* header)
+{
+	int	length = sizeof(gWorkBuffer);
+	int	nbytes;
+	int	c;		//@R2.0 Change
+//--
+	// Send command line little by little
+	if (ssled) {
+		if (strlen(server) + strlen(path) + 8 > a3gsMAX_URL_LENGTH)
+			return 1;  // NG -- Too long url
+
+		sendData("$WG https://");
+		sendData(server);
+		if (port != a3gsDEFAULT_PORT) {
+			char ports[6];
+			sendData(":");
+			sprintf(ports, "%u", port);
+			sendData(ports);
+		}
+	}
+	else {
+		if (strlen(server) + strlen(path) + 7 > a3gsMAX_URL_LENGTH)
+			return 1;  // NG -- Too long url
+
+		sendData("$WG http://");
+		sendData(server);
+		if (port != a3gsDEFAULT_PORT) {
+			char ports[6];
+			sendData(":");
+			sprintf(ports, "%u", port);
+			sendData(ports);
+		}
+	}
+
+	sendData(path);	
+
+	// Request with extra header --@R3.0 add
+	if (header != NULL) {
+		sendData(" \"");
+		sendData(header);
+		sendData("\"");
+	}
+	
+	sendCommand("");	// Go HTTP/GET request
+
+	DEBUG_PRINT(">httpGET()", "REQ");
+
+	if (getResult(gWorkBuffer, &length, TIMEOUT_NETWORK))
+		return 1;	// NG -- maybe timeout
+
+	DEBUG_PRINT(">httpGET()", gWorkBuffer);
+		// result's format: $WG=OK nbytes "response"
+
+	// Parse response
+	if (strncmp(gWorkBuffer, "$WG=OK", 6))
+		return 1;	// NG -- Can't get response
+
+	nbytes = atoi(gWorkBuffer + 7);
+	DEBUG_PRINT_NUM(">httpGET() nbytes", nbytes);
+
+	// Copy response body into "result" and set "resultlength"
+	Timer millis;
+	millis.start();
+	for (int n = 0; n < nbytes; n++) {
+		while (!iemSerial.readable()) {
+			// Wait until valid response
+			if (millis.read_ms() >= TIMEOUT_NETWORK)
+				return 2;	// NG -- Timeout
+		}
+		c = iemSerial.getc();
+		if (n < resultlength - 1)
+			result[n] = c;
+	}
+	discardUntil('\n');	// discard last '\n' -- @R2.0 Change
+	if (nbytes > resultlength - 1)			//@R2.0 Change
+		result[resultlength - 1] = '\0';
+	else
+		result[nbytes] = '\0';
+
+	return a3gsSUCCESS;		// OK
+}
+
+//***************************
+//	httpPOST
+//
+//	@discription
+//		Request HTTP/POST to server and port
+//
+//	@return value
+//		0 .. OK
+//		otherwise .. NG
+//  @param
+//		server : server name(ex. "www,google.com")
+//		port : port number(ex. 80)
+//		path : path(ex. service/index.html)
+//		header : header(without Content-Length, nul terminate, max MAX_HEADER_LENGTH)
+//		body : request body(nul terminate, max MAX_BODY_LENGTH)
+//		result : [OUT] responce raw data(no escaped)
+//		resultlength ; [OUT] "result" size(max MAX_RESULT_LENGTH)
+//		ssled : if true then use https(SSL), otherwise use http
+//	@note
+//		This function is synchronized.
+//***************************
+int A3GS::httpPOST(const char* server, uint16_t port, const char* path, const char *header, const char *body, char* result, int* resultlength, bool ssled)
+{
+	int	length = sizeof(gWorkBuffer);
+	int	nbytes;
+	int	c;		//@R2.0 Change
+//--
+	if (strlen(body) > a3gsMAX_BODY_LENGTH)
+		return 1;	// NG -- too long body
+	if (header != NULL && strlen(header) > a3gsMAX_HEADER_LENGTH)
+		return 1;	// NG -- too long header
+
+	// Send command line little by little
+	if (ssled) {
+		if (strlen(server) + strlen(path) + 8 > a3gsMAX_URL_LENGTH)
+			return 1;  // NG -- Too long url
+
+		sendData("$WP https://");
+		sendData(server);
+		if (port != a3gsDEFAULT_PORT) {
+			char ports[6];
+			sendData(":");
+			sprintf(ports, "%u", port);
+			sendData(ports);
+		}
+	}
+	else {
+		if (strlen(server) + strlen(path) + 7 > a3gsMAX_URL_LENGTH)
+			return 1;  // NG -- Too long url
+
+		sendData("$WP http://");
+		sendData(server);
+		if (port != a3gsDEFAULT_PORT) {
+			char ports[6];
+			sendData(":");
+			sprintf(ports, "%u", port);
+			sendData(ports);
+		}
+	}
+
+	if (path[0] != '/')
+		sendData("/");		// for compatiblity old version
+	sendData(path);
+
+	sendData(" \"");
+
+	sendData(body);
+
+	if (header != NULL && *header != '\0') {
+		sendData("\" \"");
+		sendData(header);
+	}
+
+	sendCommand("\"");	// Go command
+
+	DEBUG_PRINT(">httpPOST()", "REQ");
+
+	if (getResult(gWorkBuffer, &length, TIMEOUT_NETWORK))
+		return 1;	// NG -- maybe timeout
+
+	DEBUG_PRINT(">httpPOST()", gWorkBuffer);
+		// result's format: $WP=OK nbytes or $WP=NG errno
+
+	// Parse response
+	if (strncmp(gWorkBuffer, "$WP=OK", 6))
+		return 1;	// NG -- Can't post or get response
+
+	nbytes = atoi(gWorkBuffer + 7);
+	DEBUG_PRINT_NUM(">httpPOST() nbytes", nbytes);
+
+	// Copy response body into "result" and set "resultlength"
+	Timer millis;
+	millis.start();
+	for (int n = 0; n < nbytes; n++) {
+		while (!iemSerial.readable()) {
+			// Wait until valid response
+			if (millis.read_ms() >= TIMEOUT_NETWORK)
+				return 2;	// NG -- Timeout
+		}
+		c = iemSerial.getc();
+		if (n < *resultlength - 1)
+			result[n] = c;
+	}
+	discardUntil('\n');	// discard last '\n' -- @R2.0 Change
+	if (nbytes > *resultlength - 1)			//@R2.0 Change
+		result[*resultlength - 1] = '\0';
+	else {
+		result[nbytes] = '\0';
+		*resultlength = nbytes;
+	}
+
+	return a3gsSUCCESS;		// OK
+}
+
+//***************************
+//	tweet
+//
+//	@description
+//		Tweet a message
+//
+//	@return value
+//		0 .. OK
+//		otherwise .. NG
+//	@param
+//		token .. token from http://arduino-tweet.appspot.com/
+//		msg .. message to tweet(in UTF-8)
+//	@note
+//		This function use cloud service on "arduino-tweet.appspot.com".
+//		Notice from this service:
+//		- The library uses this site as a proxy server for OAuth stuff. Your tweet may not be applied during maintenance of this site.
+//		- Please avoid sending more than 1 request per minute not to overload the server.
+//		- Twitter seems to reject repeated tweets with the same contenet (returns error 403).
+//		Use gWorkBuffer and destory it
+
+//***************************
+int A3GS::tweet(const char* token, const char* msg)
+{
+	int	resultlength = sizeof(gWorkBuffer);
+//--
+	if (strlen(msg) > a3gsMAX_TWEET_LENGTH)
+		return 8;  // Too long message. (@@ check by number of characters, not bytes)
+
+	sprintf(gWorkBuffer, "token=%s&status=%s", token, msg);
+
+	// request POST to http://arduino-tweet.appspot.com
+	return httpPOST("arduino-tweet.appspot.com", 80, "update", NULL, gWorkBuffer, gWorkBuffer, &resultlength);
+}
+
+//***************************
+//	getTime
+//
+//	@description
+//		Get current date and time
+//	@return value
+//		0 .. OK
+//		otherwise .. NG
+//	@param
+//		date : [OUT] current date(JST) ("YYYY/MM/DD" format)
+//		time : [OUT] current time(JST) ("HH:MM:SS" format)
+//	@note
+//		
+//***************************
+int A3GS::getTime(char date[], char time[])
+{
+	char	responses[28];  // Format "$YT=OK 2012/03/18 13:28:25"
+	int	length = sizeof(responses);
+//--
+	sendCommand("$YT");		// Send "Get Time" command
+
+	if (getResult(responses, &length, TIMEOUT_LOCAL))
+		return 1;	// NG -- maybe timeout
+
+	DEBUG_PRINT(">getTime()", responses);
+
+	if (strncmp(responses, "$YT=OK", 6))
+		return 1;	// NG -- Can't get time
+
+	// Parse response
+	strncpy(date, responses + 7, 10);
+	date[10] = '\0';
+
+	strncpy(time, responses + 18, 8);
+	time[8] = '\0';
+
+	return a3gsSUCCESS;	// OK
+}
+
+//***************************
+//	getTime2
+//
+//	@description
+//		Get the current time as seconds since Jan 1 1970
+//	@return value
+//		0 .. OK
+//		otherwise .. NG
+// 	@param
+//		seconds : [OUT] Current seconds since Jan 1 1970(JST)
+//	@noyte
+//		
+//***************************
+int A3GS::getTime2(uint32_t& seconds)
+{
+	static  const uint8_t monthDays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+	char date[a3gsDATE_SIZE];
+	char time[a3gsTIME_SIZE];
+	uint8_t year, month, day, hour, minute, second;
+	uint32_t t;   // total seconds from 1970/01/01 00:00:00
+//--
+	// Get Current Time
+	if (getTime(date, time) != 0)
+		return 1;	// NG -- can't get time, Failed.
+
+	year = atoi(date) - 1970;
+	month = atoi(date+5);
+	day = atoi(date+8);
+	hour = atoi(time);
+	minute = atoi(time+3);
+	second = atoi(time+6);
+
+	// Seconds from 1970 till 1 jan 00:00:00 of the given year
+	t = year * (SECS_PER_DAY * 365);
+	for (int y = 0; y < year; y++) {
+		if (LEAP_YEAR(y))
+			t += SECS_PER_DAY;	// add extra days for leap years
+	}
+
+	// Add days for this year, months start from 1
+	for (int m = 1; m < month; m++) {
+		if ((m == 2) && LEAP_YEAR(year))
+			t += SECS_PER_DAY * 29;
+		else
+			t += SECS_PER_DAY * monthDays[m-1];  //monthDay array starts from 0
+	}
+	t += (day - 1) * SECS_PER_DAY;
+	t += hour * SECS_PER_HOUR;
+	t += minute * SECS_PER_MIN;
+	t += second;
+
+	seconds = t;
+
+	return a3gsSUCCESS;	// OK
+}
+
+//***************************
+//	getRSSI
+//
+//	@description
+//		Get 3G RSSI
+//	@return value
+//		0 .. OK
+//		1 .. NG
+//	@param
+//		rssi : [OUT] rssi (dBm)
+//	@note
+//		
+//***************************
+int A3GS::getRSSI(int& rssi)
+{
+	char	responses[13];  // Format "$YR=OK -999"
+	int	length = sizeof(responses);
+//--
+	sendCommand("$YR");	// Send "Get RSSI" command
+
+	if (getResult(responses, &length, TIMEOUT_LOCAL))
+		return 1;	// NG -- maybe timeout
+
+	DEBUG_PRINT(">getRSSI()", responses);
+
+	// Parse response
+	rssi = atoi(responses + 7);
+
+	return a3gsSUCCESS;	// OK
+}
+
+//***************************
+//	getVersion
+//
+//	@description
+//		Get Version
+//	@return value
+//		0 .. OK
+//		1 .. NG
+//	@param
+//		version : [OUT] version ("X.YY")
+//	@note
+//		
+//***************************
+int A3GS::getVersion(char *version)
+{
+	char	responses[13];  // Format "$YV=OK 9.99"
+	int	length = sizeof(responses);
+//--
+	sendCommand("$YV");	// Send "Get Version" command
+
+	if (getResult(responses, &length, TIMEOUT_LOCAL))
+		return 1;	// NG -- maybe timeout
+
+	DEBUG_PRINT(">getVersion()", responses);
+
+	// Copy version
+	strcpy(version, responses + 7);
+
+	return a3gsSUCCESS;	// OK
+}
+
+//***************************
+//	getLocation
+//
+//	@description
+//		get Current Location from GPS and Network
+//
+//	@return value
+//		0 .. OK
+//		1 .. NG(Bad parameters)
+//		otherwise .. NG(Can't locate position..)
+//	@param
+//		method .. method of locate(a3gsM)
+//		latitude .. [OUT] latitude
+//		longitude .. [OUT] longitude
+//	@note
+//		10-180 Sec. required.
+//***************************
+int A3GS::getLocation(int method, char* latitude, char* longitude)
+{
+	char *cp, responses[40];	// Format "$LG=OK lat lng"
+	int	length = sizeof(responses);
+//--
+	*latitude = '\0';
+	*longitude = '\0';
+
+	// Make command and send it
+	switch (method) {
+		case a3gsMPBASED :
+			sendCommand("$LG MSBASED");  // Default is method=MSBASED
+			break;
+		case a3gsMPASSISTED :
+			sendCommand("$LG MSASSISTED");
+			break;
+		case a3gsMPSTANDALONE :
+			sendCommand("$LG STANDALONE");
+			break;
+		default :
+			return 1;  // bad method
+	}
+
+	if (getResult(responses, &length, TIMEOUT_GPS))
+		return 1;	// NG -- maybe timeout
+
+	DEBUG_PRINT(">getLocation()", responses);
+
+	// parse response
+	if (! strncmp(responses, "$LG=NG", 6))
+		return 1;	// NG --  Can't locate
+
+	// Parse lattitude (assume enough space to store latitude)
+	for (cp = responses+7; *cp != '\0'; cp++) {
+		while (ISDIGIT(*cp) || *cp == '.')
+			*latitude++ = *cp++;
+		*latitude = '\0';
+		DEBUG_PRINT(">getLocation() lat", latitude);
+		break;
+	}
+
+	// Skip spaces
+	while (*cp != '\0' && ! ISDIGIT(*cp))
+		cp++;
+
+	// Parse longitude (assume enough space to store longitude)
+	for ( ; *cp != '\0'; cp++) {
+		while (ISDIGIT(*cp) || *cp == '.')
+			*longitude++ = *cp++;
+		*longitude = '\0';
+		DEBUG_PRINT(">getLocation() lng", longitude);
+		break;
+	}
+
+	return a3gsSUCCESS;	// OK
+}
+
+//***************************
+//	setDefaultProfile
+//
+//	@description
+//		Set default profile(APN) number
+//	@return value
+//		0 .. OK
+//		1 .. NG(Bad parameters)
+//	@param
+//		profileNum : profile number(1..a3gsMAX_PROFILE_NUMBER)
+//	@note
+//		
+//***************************
+int A3GS::setDefaultProfile(int profileNum)
+{
+	char	responses[20];	// Format "$PS=OK"
+	char	commands[20];	// Format "$PS 99"
+	int	length = sizeof(responses);
+//--
+	if (profileNum < 1 || a3gsMAX_PROFILE_NUMBER < profileNum)
+		return 1;	// NG -- Bad parameter
+
+	sprintf(commands, "$PS %d", profileNum);
+	sendCommand(commands);		// Send "Set Profile" command
+
+	if (getResult(responses, &length, TIMEOUT_LOCAL))
+		return 1;	// NG -- maybe timeout
+
+	DEBUG_PRINT(">setDefaultProfile()", responses);
+
+	// parse response
+	if (! strncmp(responses, "$PS=NG", 6))
+		return 1;	// NG --  Can't set prfoile number
+
+	return a3gsSUCCESS;	// OK
+}
+
+//***************************
+//	getDefaultProfile
+//
+//	@description
+//		Get default profile(APN) number
+//	@return value
+//		0 .. OK
+//		1 .. NG
+//	@param
+//		profileNum : [OUT] profile number(1..a3gsMAX_PROFILE_NUMBER)
+//	@note
+//		
+//***************************
+int A3GS::getDefaultProfile(int* profileNum)
+{
+	char responses[20];  // Format "$PR=OK 99\n"
+	int	length = sizeof(responses);
+//--
+	sendCommand("$PR");	// Send "Read Profile" command
+
+	if (getResult(responses, &length, TIMEOUT_LOCAL))
+		return 1;	// NG -- maybe timeout
+
+	DEBUG_PRINT(">getDefaultProfile()", responses);
+
+	// parse response
+	*profileNum = atoi(responses + 7);
+
+	return a3gsSUCCESS;  // Succeeded
+}
+
+//***************************
+//	connectTCP
+//
+//	@description
+//		Connect to server with TCP/IP connection
+//	@return value
+//		0 .. OK
+//		1 .. NG
+//	@param
+//		server : server name or IP address(ex. "www,google.com", "192.161.10.1")
+//		port : port number(ex. 80)
+//	@note
+//		
+//***************************
+int A3GS::connectTCP(const char* server, int port)
+{
+	char	responses[30];		// FORMAT: "$TC=OK\n" or "$TC=NG errno [info]\n"
+	int	length = sizeof(responses);
+//--
+	// Check parameter size
+	if (strlen(server) > a3gsMAX_HOST_LENGTH)
+		return 1;	// NG -- too long host name
+	// Make command and send it
+	sprintf(gWorkBuffer, "$TC %s %d", server, port);
+	sendCommand(gWorkBuffer);
+
+	if (getResult(responses, &length, TIMEOUT_NETWORK))
+		return 1;	// NG -- maybe timeout
+
+	DEBUG_PRINT(">connectTCP()", responses);
+
+	// parse response
+	if (! strncmp(responses, "$TC=NG", 6))
+		return 1;	// NG --  Can't connect
+
+	// Change status
+	_status = TCPCONNECTEDCLIENT;
+
+	return a3gsSUCCESS;	// OK
+}
+
+//***************************
+//	disconnectTCP
+//
+//	@description
+//		Disconnect from server with current TCP/IP connection
+//	@return value
+//		0 .. OK
+//		1 .. NG
+//	@param
+//		none
+//	@note
+//		
+//***************************
+int A3GS::disconnectTCP()
+{
+	char	responses[30];		// FORMAT: "$TD=OK\n" or "$TD=NG errno [info]\n"
+	int	length = sizeof(responses);
+//--
+	// Send command
+	sendCommand("$TD");
+
+	if (getResult(responses, &length, TIMEOUT_NETWORK))
+		return 1;	// NG -- maybe timeout
+
+	DEBUG_PRINT(">disconnectTCP()", responses);
+
+	// parse response
+	if (! strncmp(responses, "$TD=NG", 6))
+		return 1;	// NG --  Can't connect
+
+	// Change status
+	_status = READY;
+
+	return a3gsSUCCESS;	// OK
+}
+
+//***************************
+//	write
+//
+//	@description
+//		Write byte into current TCP/IP connection
+//	@return value
+//		-1 .. NG (Error)
+//		 1 .. OK
+//	@param
+//		c : byte data to write
+//	@note
+//		R2.3 bug fix(binary data escaping)
+//***************************
+int A3GS::write(uint8_t c)
+{
+	char	responses[30];		// FORMAT: "$TW=OK nbytes\n" or "$TW=NG errno [info]\n"
+	int	length = sizeof(responses);
+//--
+	// Make command and send it
+	sendData("$TW \"");
+	if (c < 0x20) {
+		char	escaped[6];
+		sprintf(escaped, "$x%02x", c);
+		iemSerial.puts(escaped); 
+	}
+	// @R2.3 add -begin-
+	else if (c == '"') {
+		iemSerial.puts("$\"");
+	}
+	else if (c == '$') {
+		iemSerial.puts("$$");
+	}
+	// @R2.3 add -end-
+	else {
+		iemSerial.putc(c); 
+	}
+	sendCommand("\"");
+
+	if (getResult(responses, &length, TIMEOUT_NETWORK))
+		return -1;	// NG -- maybe timeout
+
+	DEBUG_PRINT(">write()", responses);
+
+	// parse response
+	if (! strncmp(responses, "$TW=NG", 6))
+		return -1;	// NG --  Can't connect
+
+	int	nbytes = atoi(responses + 7);
+
+	return nbytes;	// OK
+}
+
+//***************************
+//	write
+//
+//	@description
+//		Write string into current TCP/IP connection
+//	@return value
+//		-1 .. NG (Error)
+//		0 < .. OK (= wrote bytes)
+//	@param
+//		str : string(= '\0' terminated byte array) to write
+//	@note
+//		"str" must be encoded by $ escape sequence
+//***************************
+int A3GS::write(const char* str)
+{
+	char	responses[30];		// FORMAT: "$TW=OK nbytes" or "$TW=NG errno [info]\n"
+	int	length = sizeof(responses);
+//--
+	if (strlen(str) > a3gsMAX_DATA_LENGTH)
+		return -1;	// NG -- too large data
+
+	// Make command and send it
+	sendData("$TW \"");
+	sendData(str);
+	sendCommand("\"");
+
+	if (getResult(responses, &length, TIMEOUT_NETWORK))
+		return -1;	// NG -- maybe timeou\t
+
+	DEBUG_PRINT(">write()", responses);
+
+	// parse response
+	if (! strncmp(responses, "$TW=NG", 6))
+		return -1;	// NG --  Can't connect
+
+	int	nbytes = atoi(responses + 7);
+
+	return nbytes;	// OK
+}
+
+//***************************
+//	write
+//
+//	@description
+//		Write byte data into current TCP/IP connection
+//	@return value
+//		-1 .. NG (Error)
+//		0 < .. OK (= wrote bytes)
+//	@param
+//		buffer : data to write(byte array)
+//		sz : data size(in byte)
+//	@note
+//		R2.3 bug fix(binary data escaping)
+//***************************
+int A3GS::write(const uint8_t* buffer, size_t sz)
+{
+	char	responses[30];		// FORMAT: "$TW=OK nbytes\n" or "$TW=NG errno [info]\n"
+	int	length = sizeof(responses);
+//--
+	if (sz > (size_t)a3gsMAX_DATA_LENGTH)
+		return -1;	// NG -- too large data
+
+	// Make command and send it
+	sendData("$TW \"");
+	while (sz-- > 0) {
+		if (*buffer < 0x20) {
+			char	escaped[6];
+			sprintf(escaped, "$x%02x", *buffer);
+			iemSerial.puts(escaped); 
+		}
+		// @R2.3 add -begin-
+		else if (*buffer == '"') {
+			iemSerial.puts("$\"");
+		}
+		else if (*buffer == '$') {
+			iemSerial.puts("$$");
+		}
+		// @R2.3 add -end-
+		else {
+			iemSerial.putc((char)*buffer); 
+		}
+		buffer++;
+	}
+	sendCommand("\"");
+
+	if (getResult(responses, &length, TIMEOUT_NETWORK))
+		return -1;	// NG -- maybe timeout
+
+	DEBUG_PRINT(">write()", responses);
+
+	// parse response
+	if (! strncmp(responses, "$TW=NG", 6))
+		return -1;	// NG --  Can't connect
+
+	int	nbytes = atoi(responses + 7);
+
+	return nbytes;	// OK
+}
+
+//***************************
+//	read
+//
+//	@description
+//		Read data from current TCP/IP connection
+//	@return value
+//		-2  .. NG (Closed connection by other side)
+//		-1  .. NG (Error)
+//		 0  .. OK (No data)
+//		0 < .. OK (= read bytes)
+//	@param
+//		result : [OUT] read data(byte array, not '\0' terminated)
+//		resultlength : data length(in byte)
+//	@note
+//		R2.3 fix return value bug
+//		This function leaves for compatibility
+//***************************
+int A3GS::read(char* result, int resultlength)
+{
+	int	length = sizeof(gWorkBuffer);
+	int	c;		//@R2.0 Change
+//--
+	if (resultlength > a3gsMAX_DATA_LENGTH)
+		return -1;	// NG -- Bad parameter
+
+	// Make command and send it
+	sprintf(gWorkBuffer, "$TR %d", resultlength);
+	sendCommand(gWorkBuffer);
+
+	if (getResult(gWorkBuffer, &length, TIMEOUT_NETWORK))
+		return (-1);		// NG -- can't read
+
+	DEBUG_PRINT(">read()", gWorkBuffer);
+
+	// Parse response
+	if (! strncmp(gWorkBuffer, "$TR=NG", 6)) {
+		int errno = atoi(gWorkBuffer + 7);
+		switch (errno) {
+		  case 635 :		// Connection closed
+		  case 636 :
+			return (-2);
+		  default :			// Other error
+			return (-1);
+		}
+	}
+
+	int nbytes = atoi(gWorkBuffer + 7);
+	DEBUG_PRINT_NUM(">read() nbytes", nbytes);
+
+	// Copy response body into "result" and set "resultlength"
+	for (int n = 0; n < nbytes; ) {
+		while (!iemSerial.readable())
+			;	// read until valid response
+		c = iemSerial.getc();
+		if (n < resultlength - 1)
+			result[n] = c;
+		n++;
+	}
+	discardUntil('\n');		// discard last '\n' -- @R2.0 Change
+
+	if (nbytes > resultlength - 1) {		//@R2.0 Change
+		result[resultlength - 1] = '\0';
+		return (resultlength - 1);	// OK
+	}
+	else {
+		result[nbytes] = '\0';
+		return nbytes;				// OK
+	}
+}
+
+//***************************
+//	read
+//
+//	@description
+//		Read (binary) data from current TCP/IP connection
+//	@return value
+//		-2  .. NG (Closed connection by other side)
+//		-1  .. NG (Error)
+//		 0  .. OK (No data)
+//		0 < .. OK (= read bytes)
+//	@param
+//		buffer : [OUT] read data(byte array, not '\0' terminated)
+//		sz     : binary data size(in byte)
+//	@note
+//		R2.3 Add this function
+//***************************
+int A3GS::read(uint8_t* buffer, size_t sz)
+{
+	int	length = sizeof(gWorkBuffer);
+	int	c;
+//--
+	if (sz > (size_t)a3gsMAX_DATA_LENGTH)
+		return -1;	// NG -- Bad parameter
+
+	// Make command and send it
+	sprintf(gWorkBuffer, "$TR %d", sz);
+	sendCommand(gWorkBuffer);
+
+	if (getResult(gWorkBuffer, &length, TIMEOUT_NETWORK))
+		return (-1);		// NG -- can't read
+
+	DEBUG_PRINT(">read()", gWorkBuffer);
+
+	// Parse response
+	if (! strncmp(gWorkBuffer, "$TR=NG", 6)) {
+		int errno = atoi(gWorkBuffer + 7);
+		switch (errno) {
+		  case 635 :		// Connection closed
+		  case 636 :
+			return (-2);
+		  default :			// Other error
+			return (-1);
+		}
+	}
+
+	size_t nbytes = atoi(gWorkBuffer + 7);
+	DEBUG_PRINT_NUM(">read() nbytes", nbytes);
+
+	// Copy response body into "result" and set "resultlength"
+	size_t n;
+	for (n = 0; n < nbytes; n++) {
+		while (!iemSerial.readable())
+			;	// read until valid response
+		c = iemSerial.getc();
+		if (n < sz)
+			buffer[n] = (uint8_t)c;
+	}
+
+	discardUntil('\n');		// discard last '\n'
+
+	return (int)((n < sz) ? n : sz);		// OK
+}
+
+//***************************
+//	read
+//
+//	@description
+//		Read byte from current TCP/IP connection
+//	@return value
+//		-2  .. NG (Closed connection by other side)
+//		-1  .. NG (Error)
+//		-3  .. OK (No data)
+//		 0..0xFF .. OK (= read byte)
+//	@param
+//		none
+//	@note
+//		This function is synchronized operation.
+//		Change return value type at @R2.0
+//***************************
+int A3GS::read(void)
+{
+	int	length = sizeof(gWorkBuffer);
+	int	c;		//@R2.0 Change
+//--
+	// Send command
+	sendCommand("$TR 1");
+
+	if (getResult(gWorkBuffer, &length, TIMEOUT_NETWORK))
+		return (-1);	// NG -- can't read
+
+	DEBUG_PRINT(">read()", gWorkBuffer);
+
+	// Parse response
+	if (! strncmp(gWorkBuffer, "$TR=NG", 6)) {
+		int errno = atoi(gWorkBuffer + 7);
+		switch (errno) {
+		  case 635 :		// Connection closed
+		  case 636 :
+			return (-2);
+		  default :			// Other error
+			return (-1);
+		}
+	}
+
+	size_t nbytes = atoi(gWorkBuffer + 7);
+	DEBUG_PRINT_NUM(">read() nbytes", nbytes);
+
+	if (nbytes == 1) {
+		while (!iemSerial.readable())
+			;	// read valid response
+		c = iemSerial.getc();
+	}
+	else // if (nbytes == 0)
+		c = -3;		// NG -- no data
+
+	discardUntil('\n');	// discard last '\n' -- @R2.0 Change
+
+	return c;	// OK
+}
+
+//***************************
+//	put
+//
+//	@description
+//		Put data to non-volatile storage in 3G shield
+//	@return value
+//		 -1 .. NG (Error)
+//		 0  .. OK
+//	@param
+//		storageNum : [IN] Storage number (1..a3gsMAX_STORAGE_NUMBER)
+//		buffer     : [IN] Put data
+//		sz         : [IN] size of data (in bytes)
+//	@note
+//		@3.0 add
+//***************************
+int A3GS::put(int storageNum, uint8_t *buffer, size_t sz)
+{
+	char	responses[30];		// FORMAT: "$RW=OK nbytes\n" or "$RW=NG errno [info]\n"
+	int	length = sizeof(responses);
+//--
+	if (storageNum < 0 || storageNum > a3gsMAX_STORAGE_NUMBER)
+		return a3gsERROR;	// NG -- Bad parameter
+
+	if (sz < 1 || sz > (size_t)a3gsMAX_STORAGE_LENGTH)
+		return a3gsERROR;	// NG -- too large data
+
+	// Make command and send it
+	sprintf(responses, "$RW %d \"", storageNum);
+	sendData(responses);
+	while (sz-- > 0) {
+		if (*buffer < 0x20) {
+			char	escaped[6];
+			sprintf(escaped, "$x%02x", *buffer);
+			iemSerial.puts(escaped); 
+		}
+		else if (*buffer == '"')
+			iemSerial.puts("$\"");
+		else if (*buffer == '$')
+			iemSerial.puts("$$");
+		else
+			iemSerial.putc(*buffer); 
+		buffer++;
+	}
+	sendCommand("\"");	// execute $RW command
+
+	if (getResult(responses, &length, TIMEOUT_NETWORK))
+		return a3gsERROR;	// NG -- maybe timeout
+
+	DEBUG_PRINT(">put()", responses);
+
+	// parse response
+	if (! strncmp(responses, "$RW=NG", 6))
+		return a3gsERROR;	// NG --  Can't connect
+
+	return a3gsSUCCESS;	// OK
+}
+
+//***************************
+//	get
+//
+//	@description
+//		Get data from non-volatile storage in 3G shield
+//	@return value
+//		 -1 .. NG (Error)
+//		 0..a3gsMAX_STORAGE_LENGTH .. OK (length of data to get)
+//	@param
+//		storageNum : [IN] Storage number (1..a3gsMAX_STORAGE_NUMBER)
+//		buffer     : [OUT] Got data space
+//		sz         : [OUT] size of data (in bytes)
+//	@note
+//		@3.0 add
+//***************************
+int A3GS::get(int storageNum, uint8_t *buffer, size_t sz)
+{
+	int	length = sizeof(gWorkBuffer);
+	int	c;
+//--
+	if (storageNum < 0 || storageNum > a3gsMAX_STORAGE_NUMBER)
+		return a3gsERROR;	// NG -- Bad parameter
+
+	if (sz > (size_t)a3gsMAX_STORAGE_LENGTH)
+		return a3gsERROR;	// NG -- Bad parameter
+
+	// Make command and send it
+	sprintf(gWorkBuffer, "$RR %d", storageNum);
+	sendCommand(gWorkBuffer);
+
+	if (getResult(gWorkBuffer, &length, TIMEOUT_NETWORK))
+		return a3gsERROR;	// NG -- can't read
+
+	DEBUG_PRINT(">get()", gWorkBuffer);
+
+	// Parse response
+	if (! strncmp(gWorkBuffer, "$RR=NG", 6))
+		return a3gsERROR;	// NG -- can't read
+
+	size_t nbytes = atoi(gWorkBuffer + 7);
+	DEBUG_PRINT_NUM(">get() nbytes", nbytes);
+
+	// Copy response body into "result" and set "resultlength"
+	size_t n;
+	for (n = 0; n < nbytes; n++) {
+		while (!iemSerial.readable())
+			;	// read until valid response
+		c = iemSerial.getc();
+		if (n < sz)
+			buffer[n] = (uint8_t)c;
+	}
+	discardUntil('\n');		// discard last '\n'
+
+	return (int)((n < sz) ? n : sz);		// OK
+}
+
+//***************************
+//	updateProfile
+//
+//	@description
+//		Update profile with encrypted data
+//	@return value
+//		 -1 .. NG
+//		 0  .. OK
+//	@param
+//		encryptedProfile : [IN] Encrypted profile
+//		sz               : [IN] bytes of encrypted profile
+//	@note
+//		@3.0 add
+//***************************
+int A3GS::updateProfile(const uint8_t *encryptedProfile, int sz)
+{
+	int	length = sizeof(gWorkBuffer);
+//--
+	// Make command and send it
+	sendData("$YD - \"");
+	for (char *p = (char *)encryptedProfile; sz-- > 0; p++) {
+		if (*p < 0x20) {
+			char	escaped[6];
+			sprintf(escaped, "$x%02x", (uint8_t)*p);
+			iemSerial.puts(escaped); 
+//			Serial.print(escaped);
+		}
+		else if (*p == '"') {
+			iemSerial.puts("$\"");
+//			Serial.print("$\"");
+		}
+		else if (*p == '$') {
+			iemSerial.puts("$$");
+//			Serial.print("$$");
+		}
+		else {
+			iemSerial.putc(*p); 
+//			Serial.print((char)*p); 
+		}
+	}
+	sendCommand("\"");
+
+	if (getResult(gWorkBuffer, &length, TIMEOUT_LOCAL))
+		return -1;	// NG -- maybe timeout
+
+	// parse response
+	if (! strncmp(gWorkBuffer, "$YD=NG", 6))
+		return -1;	// NG --  Can't do
+
+	return 0;	// OK
+}
+
+//***************************
+//	encryptString
+//
+//	@description
+//		Encrypt string with password
+//	@return value
+//		 -1 .. NG
+//		 0  .. OK
+//	@param
+//		password : [IN] Password to encrypt
+//		s        : [IN] String to encrypt
+//	@note
+//		@3.0 add
+//***************************
+int A3GS::encryptString(const char *password, const char *s)
+{
+	int	length = sizeof(gWorkBuffer);
+//--
+	// Make command and send it
+	sendData("$YY ");
+	sendData(password);
+	sendData(" \"");
+	sendData(s);
+	sendCommand("\"");
+
+	if (getResult(gWorkBuffer, &length, TIMEOUT_LOCAL))
+		return -1;	// NG -- maybe timeout
+
+	// parse response
+	if (! strncmp(gWorkBuffer, "$YY=NG", 6))
+		return -1;	// NG --  Can't encrypt
+
+	size_t nbytes = atoi(gWorkBuffer + 7);
+	DEBUG_PRINT_NUM(">encrypt() nbytes", nbytes);
+
+	printf(">>");
+	int c;
+	for (int n = 0; n < (int)nbytes; n++) {
+		while (!iemSerial.readable())
+			;	// read until valid response
+		c = iemSerial.getc();
+		printf("\\x");
+		printf("%02x", c);
+	}
+	printf("<<\r\n");
+
+	discardUntil('\n');		// discard last '\n'
+
+	return 0;	// OK
+}
+
+
+//***
+//  Private methods
+//***
+
+//  sendCommand() -- send command to iem with \n
+void A3GS::sendCommand(const char* cmd)
+{
+	// Send command to IEM with '\n'
+	iemSerial.puts(cmd);
+	iemSerial.putc('\n');
+	DEBUG_PRINT("<sendCommand()", cmd);
+}
+
+//  sendData() -- send data to iem without \n
+void A3GS::sendData(const char* data)
+{
+	// Send data to IEM without '\n'
+	iemSerial.puts(data); 
+	DEBUG_PRINT("<sendData()", data);
+}
+
+// discardUntl() -- discard characters from iemSerial until match specified character
+void A3GS::discardUntil(const char match)
+{
+	int	c;		//@R2.0 Change
+	Timer millis;
+	bool	done = false;
+//--
+	millis.start();
+	while (! done) {
+		if (millis.read_ms() >= TIMEOUT_LOCAL) {
+			DEBUG_PRINT(">discardUntil()", "TIMEOUT");
+			break;		// Timeout !
+		}
+
+		if (!iemSerial.readable())
+			continue;	// discard until valid response -- @R2.1 Change
+
+		c = iemSerial.getc();
+		if (c == match)
+			done = true;
+	}
+}
+	
+// getResult() -- get result from IEM
+int A3GS::getResult(char *buf, int *len, uint32_t timeout)
+{
+	Timer millis;
+	bool completed = false;
+	int length = 0;
+	millis.start();
+	while (! completed) {
+		if (millis.read_ms() >= timeout) {
+			DEBUG_PRINT(">getResult()", "TIMEOUT");
+			return 1;		// NG -- Timeout
+		}
+		if (!iemSerial.readable())
+			continue;
+		int c = iemSerial.getc();
+		if (c >= 0x20 && c < 0x7f) {
+			DBG("%c", c);
+		} else {
+			DBG("_%02x", c);
+		}
+		if (c == 0x0a) {	// end of line
+			if (buf[0] == '$')
+				completed = true;	// got result
+			else
+				length = 0;			// got status, so retry
+		}
+		else if (length < *len - 1)
+			buf[length++] = c;
+	}
+	buf[length] = '\0';
+	*len = length;
+
+	DEBUG_PRINT("<getResult", buf);
+
+	return 0;	// OK
+}
+
+//	handleINT0() -- Interrupt Service Routine(ISR) of INT0
+void A3GS::handleINT0(void)
+{
+	// disable INT0
+	_intin.fall(NULL);
+
+	(*SMShandler)();	// Call user handler(You can use other interrupts(UART, millis()..))
+
+	// Clear hander
+	SMShandler = NULL;
+}
+
+// END OF a3gs.cpp
+