Receiving SMS in Particle Electron

@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.

2 Likes

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

3 Likes

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

1 Like

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.

A project needs to receive a SMS with an APN, and reply with a copy of the message to the original sender. This has proved to be quite a challenge on FW 0.6.1 and Electron 2G.

I have learned a lot from the examples here, but did not succeed yet.

@krvarma 's example picks a value, and I could not expand it to something working for this purpose.

The lib @Fabien points to returns an extra ghost SMS as noted by @Cameron , and sometimes “mis fires” in reception getting for example +USOST messages.

The library from Twillio is broken with 0.6.1 in getting the SMS body. I found a solution, but then the STARTUP code for a callback when receiving a SMS also seems unstable - I can only receive one SMS sometimes, the second one trips up the system (logging stops + SOS with one blink or not).

I also experimented with @ScruffR send SMS code that seems to run, but not if I receive and SMS first with the Twillio SMS.CPP and SMS.H

To find stable ground I have eliminated code (below).

If I delete the particle.variable line, then logging, receiving SMS and the indication multiple times works fine.

If I keep the particle.variable line logging breaks, after sending the second SMS to the device. Sometimes it blinks cyan fast (connecting again), sometimes SOS with one red blink.

STARTUP(cellular_credentials_set("internet.ts.m2m", "", "", NULL));
STARTUP(cellular_sms_received_handler_set(sms_received, NULL, NULL)); // Should work with v0.5.1-rc.2

volatile int newSMS = 0;

SerialLogHandler logHandler(LOG_LEVEL_ALL);

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

void setup() {
    Particle.keepAlive(30);
    Particle.variable("Value", String(newSMS));
    Log.trace("Setup done ...");
}

void loop() {
    if(newSMS){
        newSMS=0;
        Log.info("Got SMS"); 
    }
}

I believe some GSM modules can not receive SMS with a GPRS connection active.

Is it possible to reliably receive and send several SMS’s while connected to the Particle Cloud?

Any info is appreciated.

I’d advise against using Particle.variable() the way you did.
Particle.variable() can expose int direct. So I’d suggest to try this instead

  Particle.variable("Value", newSMS); 

The reason why the other option won’t work is that String(newSMS) only exists on the stack and will vanish once setup() ends.

@ScruffR thanks. Did that because I got a compiler error just using newSMS, but without volatile it works…

I see, then try this instead, to keep the volatile

Particle.variable("Value", (int)newSMS);

@ScruffR With that change, when I read the variable via the console it returns 134507349.

This seems to work ok:

Particle.variable("Value", (int&)newSMS);

Time to read some of those SMS’s …

2 Likes

SMS reception works with sms files from the Twilio code on github referenced above, after applying the proposed solution in the issue I raised there.

Also made a function variant to get the body and number of the latest SMS for replying, where len is sizeof(body):

bool getLastUnreadSMS(char* num, char* body, int len ) {
    bool ok=false;
    int ix[8];

    int n = smsList("REC UNREAD", ix, 8);
    if (n > 8) n = 8;

    if (n>0){
        if(smsRead(ix[n-1], num, body, len)) ok = true;
    }
    else ok = false;
    
    return ok;
}

Time to send reply SMS …

I am replying to a SMS with @ScruffR send code referenced above without SYSTEM_THREAD(ENABLED);

A delay was needed, or I consistently got “+CMS ERROR: unknown error” when sending.

void loop() {
    if(newSMS){
        Log.info("Got SMS");
        newSMS=0;
        char jbody[162] = "";
        char jnum[32] = "";

        if(getLastUnreadSMS(jnum, jbody, sizeof(jbody))){
            Log.info("SMS in: '%s' '%s'", jnum, jbody);
            delay(1000);
            sendMessage(jnum, jbody);
            SmsDeleteFlag flag = DELETE_ALL;  // Delete all other SMS
            smsDeleteAll(flag);
        }
        else Log.info("No SMS");
    }
}

With the delay above there was a success rate of 8/10 in replying to a received SMS.

1st fail was “+CME ERROR: unknown” and log “WARN: Waiting but probably”.
2nd fail was “+CME ERROR: operation not allowed” and “Message not sent”.

One messages got “WARN: Message not sent (-4)”, but it was actually sent.

Twilio code use 512 as buffer size (jBody). If it is not long enough for what you are doing, a memcpy can overstep, as the supplied buffer length does not seem to actually be used in the Twilio code.

Foreign/special chars will get zapped (UCS2).

BTW at some point nothing worked anymore, until I remembered to skip the USB hub.

I am not sure how AT commands for SMS will work on top of system communication with the modem such as keep alive messages if SYSTEM_THREAD(ENABLED);?

1 Like

Hi

I get this error when I use the sample code from the first post. Why?

strcmpi() and strncmpi() are the case insensitive versions of strcmp() and strncmp() and these seem not supported (anymore since 0.6.3).
From 0.6.3 on you need to use strcasecmp() and strncasecmp()

I adjusted it but it isn’t working :frowning:
I can send SMS with my Electron but I can’t receive it :frowning:

Have you adapted this line in the code to fit your needs?

I have added:

ledState = HIGH;

if you write “deleteall”. This is working, the LED turns HIGH.
But if I write “D7=HIGH”, it doesnt work :frowning: (code sample)

#define TIMEOUT                     10000
#define CMD_NONE                    0
#define CMD_DELETE_ALL              1

#define LED_PIN                     D7
// Replace with a phone number, the application will only process
// the SMS received from this number
#define AUTHORIZED_PHONE_NUMBNER    "+xxxxxxx"

void riInterrupt(void);
STARTUP(cellular_credentials_set("gprs.swisscom.ch", "", "", NULL));
int state = LOW;
bool isCallRecognized = false;
char szReturn[32] = "";
int maxIndex = 1;
int ledState = LOW;
int command = CMD_NONE;

int phoneCallback(int type, const char* buf, int len, char* param){  
    Serial.print("Phone Callback: ");
    
    if(strncmp(&buf[2], "+CMGR:", 6) == 0){
        Serial.write((const uint8_t*)&buf[2], len - 2);
        Serial.println();
        
        char szState[32];
        char szPhoneNumber[18];

        sscanf(&buf[9], "%[^/','],%[^/','],%s", szState, szPhoneNumber);
        
        maxIndex++;
        
        strcpy(szPhoneNumber, &szPhoneNumber[1]);        
        szPhoneNumber[strlen(szPhoneNumber) - 1] = 0;
        
        if(strcmp(AUTHORIZED_PHONE_NUMBNER, szPhoneNumber) == 0){
            Serial.println("SMS detected");
            
            isCallRecognized = true;    
        }
        
        Serial.print("Phone Number ");
        Serial.print(szPhoneNumber);
        Serial.print(", ");
        Serial.print(szState);
        Serial.println();
    }
    else if(strncmp(&buf[2], "+CPMS:", 6) == 0){
        char szData[13];
        
        Serial.write((const uint8_t*)&buf[2], len - 2);
        Serial.println();

        sscanf(&buf[9], "%[^/','], %d", szData, &maxIndex);
        
        maxIndex++;

        Serial.print("Current Index ");
        Serial.print(maxIndex);
        Serial.print(", ");
        Serial.print(szData);
        Serial.println();
    }
    else{
        Serial.write((const uint8_t*)buf, len - 2);
        Serial.println();
        
        if(isCallRecognized){
            isCallRecognized = false;
            
            if(strncasecmp(buf, "deleteall", 9) == 0){
                Serial.println("Delete all command received.");
                ledState = HIGH;
                command = CMD_DELETE_ALL;
            }
            else{
                char szPin[3];
                char szState[5];
                
                sscanf(buf, "%[^/'=']=%s", szPin, szState);
                
                Serial.println();
                Serial.print(szPin);
                Serial.print("=");
                Serial.print(szState);
                Serial.println();
                
                if(strcasecmp(szPin, "D7") == 0){
                    if(strcasecmp(szState, "HIGH") == 0){
                        ledState = HIGH;
                    }
                    else if(strcasecmp(szState, "LOW") == 0){
                        ledState = LOW;
                    }
                }
            }
        }
    }
    
    state = LOW;
    
    return WAIT;
}

void setup(){
    Serial.begin(115200);
    Particle.keepAlive(90);
    pinMode(LED_PIN, OUTPUT);
    pinMode(RI_UC, INPUT_PULLUP);
    
    attachInterrupt(RI_UC, riInterrupt, CHANGE);
    
    Cellular.command(phoneCallback, szReturn, TIMEOUT, "AT+CMGF=1\r\n");
    Cellular.command(phoneCallback, szReturn, TIMEOUT, "AT+CPMS?\r\n");
}

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

        command = CMD_NONE;
    }
    else if(state == HIGH){
        char szCommand[32];
        
        sprintf(szCommand, "AT+CMGR=%d\r\n", maxIndex);
        
        Serial.println();
        Serial.print("Sending command: ");
        Serial.print(szCommand);
        Serial.println();
        
        Cellular.command(phoneCallback, szReturn, TIMEOUT, szCommand);
    }
}

void riInterrupt(){
    state = digitalRead(RI_UC);
}

If the deleteall command works you can obviously receive SMS.
If other commands don’t work as you expected, then you may want to add some Serial.print() statements in the respective code branch (or use the ones that are already there) to see what you get and compare with what you expect.

1 Like

Is it correct when I write the SMS: “D7=HIGH”?

Should be, but you should really check the serial output to see what’s happening.

1 Like