1 /* GSMSMSInterface.cpp */
2 /* Copyright (C) 2012 mbed.org, MIT License
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
5 * and associated documentation files (the "Software"), to deal in the Software without restriction,
6 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
7 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
10 * The above copyright notice and this permission notice shall be included in all copies or
11 * substantial portions of the Software.
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
14 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
16 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 #define __MODULE__ "GSMSMSInterface.cpp"
27 #include "GSMSMSInterface.h"
32 #define DEFAULT_TIMEOUT 10000
34 GSMSMSInterface::GSMSMSInterface(ATCommandsInterface
* pIf
) : m_pIf(pIf
), m_msg(NULL
), m_maxMsgLength(0), m_msisdn(NULL
)
36 m_pIf
->registerEventsHandler(this); //Add us to the unsolicited result codes handlers
39 int GSMSMSInterface::init()
41 m_msgRefListCount
= 0;
46 //Set Text mode format
47 int ret
= m_pIf
->executeSimple("AT+CMGF=1", NULL
, DEFAULT_TIMEOUT
);
53 DBG("Setup new messages indication");
54 //Setup new messages indication
55 ret
= m_pIf
->executeSimple("AT+CNMI=2,1,0,0,0", NULL
, DEFAULT_TIMEOUT
);
61 DBG("Try to fetch inbox");
65 ret
= updateInbox(); //Fetch existing messages references
74 DBG("Initialization done");
78 int GSMSMSInterface::send(const char* number
, const char* message
)
80 if( strlen(number
) > 16 )
82 return NET_INVALID
; //Number too long to match 3GPP spec
88 m_state
= SMS_SEND_CMD_SENT
;
89 m_msg
= (char*) message
;
94 std::sprintf(cmd
, "AT+CMGS=\"%s\"", number
);
95 ret
= m_pIf
->execute(cmd
, this, NULL
, DEFAULT_TIMEOUT
);
97 if( (ret
!= OK
) || (m_state
!= SMS_CMD_PROCESSED
) )
99 WARN("ret %d, state %d", ret
, m_state
);
110 int GSMSMSInterface::get(char* number
, char* message
, size_t maxLength
)
114 return NET_INVALID
; //Buffer too short
119 DBG("Get next message");
121 if( ((m_msgRefListCount
== 0) && m_needsUpdate
) || ((m_msgRefListCount
> 0) && (m_msgRefList
[0] == -1)) )
123 DBG("Message list count is 0 and needs updating or next index is unknown, calling updateInbox()");
133 DBG("%d messages to read", m_msgRefListCount
);
135 if(m_msgRefListCount
== 0)
138 DBG("Message list count is 0, I think it's empty and returning.");
139 return NET_EMPTY
; //No message to read
143 m_state
= SMS_GET_CMD_SENT
;
144 m_msisdn
= (char*) number
;
145 m_msg
= (char*) message
;
146 m_maxMsgLength
= maxLength
;
151 std::sprintf(cmd
, "AT+CMGR=%d", m_msgRefList
[0]);
152 ret
= m_pIf
->execute(cmd
, this, NULL
, DEFAULT_TIMEOUT
);
155 WARN("AT+CMGR returned %d", ret
);
161 if (m_state
!= SMS_CMD_PROCESSED
)
163 WARN("State variable is not 'SMS_CMD_PROCESSED' - returning 'NET_EMPTY'");
166 DBG("Deleting message from index number: %d", m_msgRefList
[0] );
167 //Delete message from outbox
168 std::sprintf(cmd
, "AT+CMGD=%d", m_msgRefList
[0]);
169 ret
= m_pIf
->executeSimple(cmd
, NULL
, DEFAULT_TIMEOUT
);
172 ERR("Could not delete message");
174 //Remove message from list
175 std::memmove(&m_msgRefList
[0], &m_msgRefList
[1], MIN(m_msgRefListCount
-1,MAX_SM
-1)*sizeof(m_msgRefList
[0]));
178 if(m_msgRefListCount
> MAX_SM
- 1) //Last message index is unknown, so put -1 to tell the lib to fetch it when needed
180 DBG("Last message index is unknown, will need to be updated");
181 m_msgRefList
[MAX_SM
- 1] = -1;
184 DBG("%d messages to read", m_msgRefListCount
);
186 if (m_state
!= SMS_CMD_PROCESSED
)
200 int GSMSMSInterface::getCount(size_t* pCount
)
215 *pCount
= m_msgRefListCount
;
222 /*virtual*/ int GSMSMSInterface::onNewATResponseLine(ATCommandsInterface
* pInst
, const char* line
)
224 if(m_state
== SMS_SEND_CMD_SENT
)
226 if( std::sscanf(line
, "+CMGS: %*d") == 0 )
229 m_state
= SMS_CMD_PROCESSED
;
232 else if(m_state
== SMS_GET_CMD_SENT
)
234 DBG("Header: %s", line
);
235 if( std::sscanf(line
, "+CMGR: %*[^,],\"%16[^\"]\"", m_msisdn
) == 1 ) //Get message ref
237 m_state
= SMS_GET_HDR_RECEIVED
;
240 else if(m_state
== SMS_GET_HDR_RECEIVED
)
242 DBG("Message: %s", line
);
243 size_t cpyLen
= MIN( std::strlen(line
), m_maxMsgLength
- 1 );
244 std::memcpy( m_msg
, line
, cpyLen
);
245 m_msg
[cpyLen
] = '\0';
246 m_state
= SMS_CMD_PROCESSED
;
248 else if(m_state
== SMS_GET_COUNT_CMD_SENT
)
250 DBG("Header: %s", line
);
252 if( std::sscanf(line
, "+CMGL: %d,\"REC", &msgRef
) == 1 ) //Filter on REC READ and REC UNREAD messages
254 m_state
= SMS_GET_COUNT_HDR_RECEIVED
;
255 //Add message to list
256 if(m_msgRefListCount
< MAX_SM
)
258 m_msgRefList
[m_msgRefListCount
] = msgRef
;
260 m_msgRefListCount
++; //Always count message
261 DBG("m_msgRefListCount=%d",m_msgRefListCount
);
264 else if(m_state
== SMS_GET_COUNT_HDR_RECEIVED
)
266 DBG("Message (debug only): %s", line
); //For debug only
267 m_state
= SMS_GET_COUNT_CMD_SENT
;
272 /*virtual*/ int GSMSMSInterface::onNewEntryPrompt(ATCommandsInterface
* pInst
)
274 if(m_state
== SMS_SEND_CMD_SENT
)
276 char* crPtr
= strchr(m_msg
, CR
);
279 int crPos
= crPtr
- m_msg
;
280 //Replace m_inputBuf[crPos] with null-terminating char
281 m_msg
[crPos
] = '\x0';
283 //If there is a CR char, split message there
285 //Do print the message
286 int ret
= pInst
->sendData(m_msg
);
292 char cr
[2] = {CR
, '\0'};
293 ret
= pInst
->sendData(cr
);
303 m_msg
++; //Discard LF char as well
310 //Do print the message
311 pInst
->sendData(m_msg
);
319 /*virtual*/ bool GSMSMSInterface::isATCodeHandled(const char* atCode
) //Is this AT code handled
321 DBG("AT code is %s", atCode
);
322 if( strcmp("+CMTI", atCode
) == 0 )
331 /*virtual*/ void GSMSMSInterface::onDispatchStart()
336 /*virtual*/ void GSMSMSInterface::onDispatchStop()
341 /*virtual*/ char* GSMSMSInterface::getEventsEnableCommand()
343 return "AT+CNMI=2,1,0,0,0";
346 /*virtual*/ char* GSMSMSInterface::getEventsDisableCommand()
348 return "AT+CNMI=0,0,0,0,0"; //Indications will be buffered within the modem and flushed back when the former command is executed
351 /*virtual*/ void GSMSMSInterface::onEvent(const char* atCode
, const char* evt
)
353 if( strcmp("+CMTI", atCode
) != 0 )
355 return; //Not supported
358 DBG("Unsollicited result code: %s - %s", atCode
, evt
);
362 if(( std::sscanf(evt
, "\"SM\",%d", &msgRef
) == 1 ) ||
363 ( std::sscanf(evt
, "\"ME\",%d", &msgRef
) == 1 ))
365 DBG("Adding message to list (ref %d)", msgRef
);
366 if(m_inboxMtx
.trylock())
368 //Add message to list
369 if(m_msgRefListCount
< MAX_SM
)
371 m_msgRefList
[m_msgRefListCount
] = msgRef
;
373 m_msgRefListCount
++; //Always count message
378 WARN("Could not get lock");
379 m_needsUpdate
= true;
384 int GSMSMSInterface::updateInbox()
386 //Get memory indexes of unread messages
388 DBG("Updating inbox");
389 m_msgRefListCount
= 0; //Reset list
390 m_needsUpdate
= false; //Assume we won't need update after this routine (can be set to true by an incoming SM event)
392 //First list the "REC READ" messages that were not processed in the previous session
393 m_state
= SMS_GET_COUNT_CMD_SENT
;
394 int ret
= m_pIf
->execute("AT+CMGL=\"REC READ\"", this, NULL
, DEFAULT_TIMEOUT
);
397 WARN("AT+CMGL returned %d", ret
);
399 m_msgRefListCount
= 0; //List could be invalid
400 m_needsUpdate
= true;
404 //Now list the "REC UNREAD" messages that were received by the modem since
405 m_state
= SMS_GET_COUNT_CMD_SENT
;
406 ret
= m_pIf
->execute("AT+CMGL=\"REC UNREAD\"", this, NULL
, DEFAULT_TIMEOUT
);
409 WARN("AT+CMGL returned %d", ret
);
411 m_msgRefListCount
= 0; //List could be invalid
412 m_needsUpdate
= true;
416 DBG("%d incoming messages in inbox", m_msgRefListCount
);