Receiving SMS in Particle Electron


#5

Hello,
I need a bit help :slight_smile:
How can I receive the message into a string variable?
In which part of the code can be inserted.
I need to keep the message into string variable so I can send to another function.
So I need something like only to read the message and puts puts it in a string.


#6

Hi,

Suggest adding maxIndex = 1 in CMD_DELETE_ALL to avoid crashes on SMS_RECEIVE after DELETEALL command;

if(CMD_DELETE_ALL == command){
        Cellular.command(phoneCallback, szReturn, TIMEOUT, "AT+CMGD=1,4\r\n");

        command = CMD_NONE;
        maxIndex = 1
    }

#7

Thanks @alan_m for pointing out!.


#8

I too am having some difficulty finding the right commands to pull out just the message in a string format (without the pre-able info).

Has anyone had a chance to solve this?
I think it is a combination of a string trim function
e.g. https://docs.particle.io/reference/firmware/electron/#remove-

But I’m a little concerned that if a user were to use a shorter or longer phone number than expected, then it may result in the function cutting off too much of the message and failing to process the instruction.

Would greatly appreciate any advice here


#9

I am using a library to manage SMS on the electron. Works great: https://github.com/robynjayqueerie/electron-atcommand-library


#10

@Cameron
I have used the library from Twilio and works great. Only problem is that no information about the receiving number and has no feature for sending messages. Also good feature is that the library works with interruptions.


For sending messages @ScruffR has written an excellent example.

@Fabien
This is a excellent library, I do not have used so far but I see has a function for sending, receiving and ID feature.
I think it contains everything required for working with SMS. :+1:


#11

Thanks, that library looks good - but I’m not getting the message body back from the functions listed in the sms.cpp file.

I (think) it gets lost at >
Cellular.command(_cbCMGR, &param, "AT+CMGR=%d\r\n", ix)

Which is located in sms.cpp
It returns the value -2 and after putting in Serial.println everywhere, I can’t seem to find a function that puts out the message string

Do you have a working copy you may be able to share for me?


#12

Thanks @Fabien

I managed to get the library working! yay :smile:

Although I’m a tad concerned around stability, the library author does mention a few issues with it in their GitHub page re: buffer checking and when I send 4 messages rapidly, the message text comes back with the text from the 4 messages on new lines in the same string.

Any chance you have found any other issues in your experiences with that library?

I’m wondering whether I spend a lot of time finding the risk factors/issues with the library and start solving them - or find a slightly simpler approach… ponders*


#13

@ScruffR

Any chance you have tackled the reading a direct String from SMS challenge after your amazing SMS sending code?

I liked your really low overhead/no complexity approach last time


#14

Here is a simple example that I just did but at the moment I’m not able to test.

// This #include statement was automatically added by the Particle  IDE.
#include "application.h"
// This #include statement was automatically added by the Particle IDE.
#include "sms.h"

STARTUP(cellular_credentials_set("internet", "internet", "t-mobile", NULL));
/* Receive SMS callback. Requires base firmware 0.5.1.rc2
https://github.com/spark/firmware/releases/tag/v0.5.1-rc.2 */
STARTUP(cellular_sms_received_handler_set(sms_received, NULL, NULL));

SYSTEM_THREAD(ENABLED);
//SYSTEM_MODE(AUTOMATIC);
SYSTEM_MODE(SEMI_AUTOMATIC);

const int REL1 = D7; 

volatile int newSMS = 0;
String smsBody;

void setup(){

  Particle.keepAlive(45);  // send keep alive ping every 45 sec

    Serial.begin(115200);
  
    delay(50);
  
    pinMode(REL1, OUTPUT);

    Particle.variable("mesage", smsBody);

    Particle.connect();  // for semi automatic mod

    //smsBody.reserve(40);  // 

    /* Delete all potentially stale SMS from the modem */
    SmsDeleteFlag flag = DELETE_ALL;
    smsDeleteAll(flag);
 
}

void loop(){

if (newSMS > 0) {
  smsBody = checkUnreadSMS();
  if (smsBody != NULL) {
 
    Serial.println(smsBody);

    if(smsBody == "ON")
    {
       digitalWrite(REL1, HIGH);
    }
    else if(smsBody == "OFF")
    {
       digitalWrite(REL1, LOW);
    }

  }
  if (newSMS > 0) newSMS--;
}

}

/* ----------------------------------------------

     INCOMING SMS-BASED COMMANDS

------------------------------------------------ */
void sms_received(void* data, int index)
{
    newSMS++;
}

#15

Not gone there yet, but I might do.
How close is @developer_bt’s solution to your expectations?


#16

Humm - no luck. Can’t seem to get it to go past this line:

smsBody = checkUnreadSMS();

@developer_bt - I’m wondering - maybe the libraries were edited on the GitHub page and don’t work any more. Do you have a copy of your working library copy?


#17

I think the problem is probably to Particle firmware version.
Which version are you using? It is necessary to use 0.5.1. or up.
Here is a copy:

#include "sms.h"

int _cbCMGL(int type, const char* buf, int len, CMGLparam* param)
{
    if ((type == TYPE_PLUS) && param && param->num) {
        // +CMGL: <ix>,...
        int ix;
        if (sscanf(buf, "\r\n+CMGL: %d,", &ix) == 1)
        {
            *param->ix++ = ix;
            param->num--;
        }
    }
    return WAIT;
}

int smsList(const char* stat /*= "ALL"*/, int* ix /*=NULL*/, int num /*= 0*/) {
    int ret = -1;
    CMGLparam param;
    param.ix = ix;
    param.num = num;
    if (RESP_OK == Cellular.command(_cbCMGL, &param, "AT+CMGL=\"%s\"\r\n", stat))
        ret = num - param.num;
    return ret;
}

bool smsDelete(int ix)
{
    /* <index> = Storage position */
    bool ok = false;
    ok = (RESP_OK == Cellular.command("AT+CMGD=%d\r\n", ix));
    return ok;
}

bool smsDeleteAll(SmsDeleteFlag flag) {
  /*
  * <flag> = Deletion flag. If present, and different from 0, <index> is ignored:
  * • 1: delete all the read messages from the preferred message storage, leaving unread messages
  * and stored mobile originated messages (whether sent or not) untouched
  * • 2: delete all the read messages from the preferred message storage and sent mobile originated
  * messages, leaving unread messages and unsent mobile originated messages untouched
  * • 3: delete all the read messages from the preferred message storage, sent and unsent mobile
  * originated messages leaving unread messages untouched
  * • 4: delete all the messages from the preferred message storage including unread messages
  */
  bool ok = false;
  ok = (RESP_OK == Cellular.command("AT+CMGD=1,%d\r\n", flag));
  return ok;
}

int _cbCMGR(int type, const char* buf, int len, CMGRparam* param)
{
    if (param) {
        if (type == TYPE_PLUS) {
            if (sscanf(buf, "\r\n+CMGR: \"%*[^\"]\",\"%[^\"]", param->num) == 1) {
            }
        } else if ((type == TYPE_UNKNOWN) && (buf[len-2] == '\r') && (buf[len-1] == '\n')) {
            memcpy(param->buf, buf, len-2);
            param->buf[len-2] = '\0';
        }
    }
    return WAIT;
}

bool smsRead(int ix, char* num, char* buf, int len)
{
    bool ok = false;
    CMGRparam param;
    param.num = num;
    param.buf = buf;
    ok = (RESP_OK == Cellular.command(_cbCMGR, &param, "AT+CMGR=%d\r\n", ix));
    return ok;
}

String checkUnreadSMS() {
    String body;
    char buf[512] = "";
    int ix[8];
    int n = smsList("REC UNREAD", ix, 8);

    if (n > 8) n = 8;

    while (n-- > 0)
    {
        char num[32];
        if (smsRead(ix[n], num, buf, sizeof(buf))) {
            body = String(buf);

            // All done with this message, let's delete it
            smsDelete(ix[n]);
            return body;
        }
    }
}

// header file
    #ifndef SMS_H
    #define SMS_H

    #include "application.h"

    typedef struct { char* buf; char* num; } CMGRparam;
    typedef struct { int* ix; int num; } CMGLparam;

    int _cbCMGL(int type, const char* buf, int len, CMGLparam* param);
    int _cbCMGR(int type, const char* buf, int len, CMGRparam* param);
    int smsList(const char* stat /*= "ALL"*/, int* ix /*=NULL*/, int num /*= 0*/);
    bool smsDelete(int ix);
    enum SmsDeleteFlag {DELETE_READ = 1, DELETE_READ_SENT = 2, DELETE_READ_SENT_UNSENT = 3, DELETE_ALL = 4};
    bool smsDeleteAll(SmsDeleteFlag flag);
    bool smsRead(int ix, char* num, char* buf, int len);
    String checkUnreadSMS();
    void checkReadSMS();

    #endif

#18

Got it working! :slight_smile:

I adjusted, while using the Twilio SMS library:

  1. The force delete in setup seems to work well.

void setup()
{
    char res;
    int atResult;
	Serial.println("Entering sms setup");
	atResult = uCmd.setSMSMode(1);
	if(atResult == RESP_OK)
	{
		Serial.println("Text mode setup was OK");
	}
	else
	{
		Serial.println("Did not set up text mode");
	}
	deleteSMSOnStart();
}

The next part, was more around where to put the SMS processing function.
For some reason when I send a SMS (so there should only be 1 SMS in the buffer)
The ‘for’ loop below in it’s last loop, picks up a blank SMS. So what I had to do was put the processing function within the ‘for’ loop.

void smsRecvCheck(void* data, int index)
{
    	Serial.println("Looking for an sms message");
		// read next available messages
		if(uCmd.checkMessages(10000) == RESP_OK){
			uCmd.smsPtr = uCmd.smsResults;
			for(i=0;i<uCmd.numMessages;i++){
				Serial.printlnf(uCmd.smsPtr->sms);
				Serial.printlnf(uCmd.smsPtr->phone);
				messageText = String(uCmd.smsPtr->sms);
				messageText.toLowerCase();
				phoneReturn = String(uCmd.smsPtr->phone);
				phoneReturn.trim();
				processMessage(messageText, phoneReturn);
				uCmd.smsPtr++;
			}
		}
		//----Delete SMS's----
		
		
    	uCmd.smsPtr = uCmd.smsResults;
		
		for(i=0;i<uCmd.numMessages;i++){
			if(uCmd.deleteMessage(uCmd.smsPtr->mess,10000) == RESP_OK)
			{
				Serial.println("message deleted successfully");
			}
			else 
			{
			    Serial.println("could not delete message");
			}
			uCmd.smsPtr++;
		}
			
}

The magic was putting the below in the ‘for’ loop.

messageText = String(uCmd.smsPtr->sms);
messageText.toLowerCase();
phoneReturn = String(uCmd.smsPtr->phone);
phoneReturn.trim();
processMessage(messageText, phoneReturn);

I found how to get the phone number as well from the device that sent it by adding in ->phone
And calling a new function I wrote called processMessage(…) also helped :slight_smile:

Now begins the trick of trying to separate commands out from comma separated string text.


#19

@Cameron Excellent work :slight_smile:
Whether you can to put the complete code of the current library?
And if you can make a simple example of how you receive a message and phone number in the loop ().


#20

Sure can! I found this approach to be a little bit easier than the one first posted very kindly by krvarma - but it was a bit over my head and didn’t seem to work for me out of the box.

I have added in a whole heap of comments, read me and list of future improvements which should be considered.

The code allows you to:

  1. Send a return SMS acknowledging a SMS command
  2. Check commands have the correct user pass code before processing the command
  3. Send codes in a multi-layer format: e.g. 1234,on,5 aka ,,<command_option>
  4. Automatically manage the receiving of SMSs
  5. Print out Strings of either the message and/or phone number that sent you the message.

Hope it works well for you and others. Feel free to message if you need more detail or branch improvements off it :slight_smile:

Cheers again for your help @developer_bt, @ScruffR


#21

@Cameron Great features!

What exactly are you using these features in? I’m curious.

They need to add this to the Particle library if it’s all good and working.


#22

We are adding new features to our Bush Fire protection products - www.firegrid.com.au :slight_smile:


#23

Interesting and useful product.
Congratulations guys. :slight_smile:
Thanks for the code will be very helpful. :thumbsup:


#24

Well - I thought I had it working…

I’m having issues where the Electron throws buffer overflow errors and the red light begins its long flashing.

I have tried doing some freememory() tests and haven’t really had much luck trying to find where I run out of memory (assuming that is the actual cause of the error message I see via the console.particle.io and red flashing).

The issue occurs when I get a SMS when using:

STARTUP(cellular_sms_received_handler_set(smsTrigger, NULL, NULL));

While, If I use:

attachInterrupt(RI_UC, smsRecvCheck, CHANGE);

Then, I don’t get the red flash of death and the device crashing… but I do get a highly unreliable detection mechanism which only seems to work half the time.

Does anyone have any insight into how this command works and why it may be causing the device to crash? - I can’t find any doco on it…

``STARTUP(cellular_sms_received_handler_set(smsTrigger, NULL, NULL));`

To clarify > this code works when I have very little in terms of code in my app, but when I load it into the full set of code - it seems to kill itself.