Extracting password from SMS before performing an action

Hi all.

I’m working on this door lock at home where I would want to be able to send an SMS containing a password and have it open the lock if the password is correct.
I really don’t know how to go about this despite going through the forum and other projects on the web.
I have tried out commands and gotten responses using a terminal.
I have also been able to successfully send and receive messages and display them in the serial monitor.
Please I need some help.

I am using an Arduino Uno and the GSM module is a SIM800l.

Also, later in this device, I’d want to be able to reply a message (say a confirmation) to the number that sent the text as opposed to hard-coding the phone number into the program.
This would come later but any help on it will be appreciated.

Here is the code for receiving and displaying SMS.

#include <SoftwareSerial.h>
SoftwareSerial mySerial(3, 2);


void setup()
{
  Serial.begin(9600);
  mySerial.begin(9600);
  Serial.println("Starting...");
  delay(1000);
  mySerial.println("AT");
  pollSms();
  mySerial.println("AT+CMGF=1");
  pollSms();
  mySerial.println("AT+CNMI=1,2,0,0,0");
  pollSms();
}



void loop()
{
  pollSms();
}


void pollSms()
{
  delay(500);
  while (Serial.available())
  {
    mySerial.write(Serial.read());
  }
  while (mySerial.available())
  {
    Serial.write(mySerial.read());
  }
}



//This is the later feature I'll add
//It's just hanging here for now

/*  void reply()
  {
    Serial.print("AT+CMGS=\"+ZZXXXXXXXXXX\"\r");    //I have replaced the number with these x's
    delay(1000);
    //The text of the message to be sent.
    Serial.print("Command Successful! Door unlocked.");
    delay(1000);
    Serial.write(0x1A);
    delay(1000);
  }
  */

When a text message is received, there are four fields.
The ones of interest are the sender and the text content which are the second and fourth respectively.
I think I'm going to have to extract the text message.
Is this the way to go??

What does a typical received SMS message look like and how is the data delimited ?

I have a suspicion that the strtok() function may prove useful

It has four fields.
And it's delimited by a comma.
But the message content seems to be on its own line.

I have attached a sample of how it is.
I got that from a website but it's exactly how mine is.

I have attached a sample of how it is.

You have attached a photo of a sample with no explanation of which field is which

Sorry about that.

The lines of interest are the last four lines.

On the first and second line...
The first field is the "+CMT:" with the sender number.
The second field is the name of the sender. If not stored, it is an empty string.
The third field is the time and date of the message.

Then the message content follows below.

This will give you some ideas about how to read the message into a buffer and then parse it using strtok.

//+CMT: "+447xxxxxxxx","","18/09/21,20:54:59+04" SITE WHATS HAPPENING
//enter the message from serial monitor for testing
//actual modem reply message begins with<CR><LF>
char Grsp[100];//size for max message
char str1[30];//size appropriately for parsed characters
char str2[30];//size appropriately for parsed characters
boolean newMessage = false;
boolean prefixMatch = false;
void setup()
{
  Serial.begin(115200);
  //set ms timeout for Serial.readBytes() if no data
  Serial.setTimeout(100);
}

void loop() {

  while (Serial.available() > 0)
  {
    //readBytes returns number read not zero referenced
    byte numChars = Serial.readBytes(Grsp, 100);
    Grsp[numChars] = '\0';//null Terminate
    newMessage = true;
    Serial.println(Grsp);
  }
//can define a series of prefix messages for match

  //if (strncmp(Grsp, "+CMT", 4) == 0 && newMessage == true)
  //change to accomodate lead characters <CR> <LF>
  if(strstr(Grsp, "+CMT") != 0 and newMesage == true)
  {
    Serial.println("prefixMatch +CMT");
    prefixMatch = true;
    newMessage = false;
  }
  else if (newMessage == true)
  {
    Serial.println("no prefixMatch");
    newMessage = false;
  }

  if (prefixMatch == true)
  {
    prefixMatch = false;
    char* strtokIndx;
    //find first "
    strtokIndx = strtok(Grsp, " \" ");//need excape for " delimiter
    //find second "
    strtokIndx = strtok(NULL, " \" ");
    strcpy(str1, strtokIndx); //characters between first set of ""
    for (byte i = 0; i < 3; i++) //skip3 " delimiters
    {
      strtokIndx = strtok(NULL, " \" ");
    }
    //find ending character message
    strtokIndx = strtok(NULL, ""); //last " to end of string no delimiter \0 terminator
    strcpy(str2, strtokIndx + 1); //skip space preceding SITE
    
    Serial.print("Number = ");
    Serial.println(str1);
    Serial.print("Ending message = ");
    Serial.println(str2);
  }
}

Thank you.
I have been able to modify the code to receive and split content from my SMS.

I have begun working on how I would compare the message content which would be the password to an already stored password.

At first, I thought “str2” was a string and I was trying to convert it to a character array right until I saw where it was declared at the top of the program.

This made the work to do kinda easy but also kinda harder.
The str2 array declared has a size of 30 and I’m working with a four-digit password.
This means I’ll store my password in an array with a size of four.

I declared my password this way

char passwrd[] = {'1','2','1','2'};

I have this if statement to check the incoming message against the password but it is never correct (it keeps showing “wrong”).
How do I then compare these two having different sizes?

I have attached the full code also.

Gsm_test.ino (2.86 KB)

I have been able to modify the code to receive and split content from my SMS.

#include <SoftwareSerial.h>
SoftwareSerial mySerial(3, 2); //Rx and Tx respectively

char Grsp[100];//size for max message
char str1[30];//size appropriately for parsed characters
char str2[4];//size appropriately for parsed characters
char passwrd[] = {'1', '2', '1', '2'};
boolean newMessage = false;
boolean prefixMatch = false;

void setup()
{
  Serial.begin(9600);
  Serial.setTimeout(100);

  mySerial.begin(9600);
  mySerial.setTimeout(100);

  Serial.println("Starting...");
  delay(1000);
  mySerial.println("AT");

  mySerial.println("AT+CMGF=1");
  delay(100);
  mySerial.println("AT+CNMI=1,2,0,0,0");
}

void loop()
{
  while (mySerial.available() > 0)
  {
    //readBytes returns number read not zero referenced
    byte numChars = mySerial.readBytes(Grsp, 100);
    Grsp[numChars] = '\0';//null Terminate
    newMessage = true;
    Serial.println(Grsp);
  }

  //can define a series of prefix messages for match

  //if (strncmp(Grsp, "+CMT", 4) == 0 && newMessage == true)
  //change to accomodate lead characters <CR> <LF>

  if (strstr(Grsp, "+CMT") != 0 and newMessage == true)
  {
    Serial.println("prefixMatch +CMT");
    prefixMatch = true;
    newMessage = false;
  }

  else if (newMessage == true)
  {
    Serial.println("no prefixMatch");
    newMessage = false;
  }

  if (prefixMatch == true)
  {
    prefixMatch = false;
    char* strtokIndx;
    //find first "
    strtokIndx = strtok(Grsp, " \" ");//need excape for " delimiter
    //find second "
    strtokIndx = strtok(NULL, " \" ");
    strcpy(str1, strtokIndx); //characters between first set of ""
    for (byte i = 0; i < 3; i++) //skip3 " delimiters
    {
      strtokIndx = strtok(NULL, " \" ");
    }
    //find ending character message
    strtokIndx = strtok(NULL, ""); //last " to end of string no delimiter \0 terminator
    strcpy(str2, strtokIndx + 1); //skip space preceding SITE

    Serial.print("Number = ");
    Serial.println(str1);
    Serial.print("Ending message = ");
    Serial.println(str2);

    if (str2 == passwrd) {
      Serial.println("Correct");
    }
    else {
      Serial.println("wrong!");
    }
  }
}

Good progress. It’s always best to post code directly rather than in an attachment.

char str2[4]

str2 is a 4 digit password. To use the standard cstring functions the character arrays need to be null terminated with ‘\0’ so the array needs to be sized to hold it. So you will need

char str2[5];//4 digits of password plus null terminator
char passwrd[] = {'1', '2', '1', '2'};
if (str2 == passwrd) 
    {
      Serial.println("Correct");
    }

You can not use == for equality of character arrays. For that, you can use strcmp()

If you store your password as a character array like this, it is null terminated by the compiler and can be used directly

//char passwrd[] = {'1', '2', '1', '2'};
char passwrd[]  = "1212";

if(strcmp(str2,passwrd) == 0)
    {
      Serial.println("Correct");
    }

If for some reason you need to have the individual characters separated in the array, you can null terminate the array and still use strcmp().

char passwrd[] = {'1', '2', '1', '2', '\0'};
//char passwrd[]  = "1212";

if(strcmp(str2,passwrd) == 0)
    {
      Serial.println("Correct");
    }

Thank you so much for your help.

This is for someone who might face this later on.
I have been able to extract the message content and the sender successfully.

After trying out the strncmp() function which didn't work for me probably due to the different character array sizes, I ended up using an if statement to compare each element of both arrays.

I noticed that the first element in the content of the message received from my GSM module was 'the enter key' (not sure if it is carriage return or line feed or both) so I ignored that and began comparing from the second element.

char passwrd[] = "1212"; //I declared my set password as this

//This is the if statement
 if ((str2[1] == passwrd[0]) && (str2[2] == passwrd[1]) && (str2[3] == passwrd[2]) && (str2[4] == passwrd[3])) {
      Serial.println("Correct");
      
    }
    else {
      Serial.println("Wrong!");
    }

I noticed that the first element in the content of the message received from my GSM module was 'the enter key' (not sure if it is carriage return or line feed or both)

Please post your full code showing how you got to where you did

After trying out the strcmp() function which didn't work for me probably due to the different character array sizes,

As far as strcmp() is concerned it does not matter if the actual arrays are different sizes just that the strings they hold are the same sizes, ie both have the '\0' at the same place in the array and that they fit into the array

I noticed that the first element in the content of the message received from my GSM module was 'the enter key' (not sure if it is carriage return or line feed or both) so I ignored that and began comparing from the second element.

If you have parsed the received message such that the str2 message contains a leading character which the password does not then you have a couple of solutions.

First, it is important to understand the actual recieved message, byte by byte, so you should get that to see what you are dealing with.

I think you may be already skipping one character when you copy the message to str2 if you are using this code which assumed a blank space before the actually message.

 strcpy(str2, strtokIndx + 1);

Try instead

 strcpy(str2, strtokIndx + 2);

If there are issues of errors in operation due to varying message formats from different phones, you may wish to check other methods to see if they are more reliable.

  1. Use strstr() instead of strcmp() like you did when finding "+CMT" which was proceeded by cr/lf.

  2. Use the last 4 characters from the end of the message.

This project may help
https://www.forward.com.au/pfod/pfodSMS/SMS_HotWater/index.html

It uses my pfodApp (paid android app) to control a hotwater system via SMS. No need for a separate password, the SMS pfod library provides password protected connection, and message security, without sending the password over the SMS.

The free pfodDesigner app lets you design you own menu and then generates the Arduino code for you.

Alright @UKHeliBob

Here’s the full code

#include <SoftwareSerial.h>
SoftwareSerial mySerial(3, 2); //Rx and Tx respectively


char Grsp[100];//size for max message
char str1[30];//size appropriately for parsed characters
char str2[30];//size appropriately for parsed characters
//char passwrd[30] = {'1', '2', '1', '2',};
char passwrd[] = "1212"; //(It is the same thing with the line above this line maybe except for the null terminator which you'd need to look into)
boolean newMessage = false;
boolean prefixMatch = false;


void setup()
{
  Serial.begin(9600);
  Serial.setTimeout(100);

  mySerial.begin(9600); //
  mySerial.setTimeout(100);//

  Serial.println("Starting...");
  delay(1000);
  mySerial.println("AT");//
  delay(100);
  mySerial.println("AT+CMGF=1");//
  delay(100);
  mySerial.println("AT+CNMI=1,2,0,0,0");//

}



void loop()
{
  while (mySerial.available() > 0)//
  {
    //readBytes returns number read not zero referenced
    byte numChars = mySerial.readBytes(Grsp, 100);//
    Grsp[numChars] = '\0';//null Terminate
    newMessage = true;
    Serial.println(Grsp);
  }

  //can define a series of prefix messages for match

  //if (strncmp(Grsp, "+CMT", 4) == 0 && newMessage == true)
  //change to accomodate lead characters <CR> <LF>

  if (strstr(Grsp, "+CMT") != 0 and newMessage == true)
  {
    Serial.println("prefixMatch +CMT");
    prefixMatch = true;
    newMessage = false;
  }

  else if (newMessage == true)
  {
    Serial.println("no prefixMatch");
    newMessage = false;
  }

  if (prefixMatch == true)
  {
    prefixMatch = false;
    char* strtokIndx;
    
    //find first "
    strtokIndx = strtok(Grsp, " \" ");//need escape for " delimiter
    //find second "
    strtokIndx = strtok(NULL, " \" ");
    strcpy(str1, strtokIndx); //characters between first set of ""
    for (byte i = 0; i < 3; i++) //skip3 " delimiters
    {
      strtokIndx = strtok(NULL, " \" ");
    }
    //find ending character message
    strtokIndx = strtok(NULL, ""); //last " to end of string no delimiter \0 terminator
    strcpy(str2, strtokIndx + 1); //skip space preceding SITE

    Serial.print("Number = ");
    Serial.println(str1);
    Serial.print("Ending message = ");
    Serial.println(str2);


 if ((str2[1] == passwrd[0]) && (str2[2] == passwrd[1]) && (str2[3] == passwrd[2]) && (str2[4] == passwrd[3])) {
      Serial.println("Correct");
      reply();
    }
    else {
      Serial.println("Wrong!");
    }

  }


}

  void reply(){
  char outstring[22];
  sprintf_P(outstring, PSTR("AT+CMGS=\"%s\"\r"), str1);
  mySerial.println(outstring);
  delay(1000);
  //The text of the message to be sent.
  mySerial.print("Password confirmation successful.");
  delay(1000);
  mySerial.write(0x1A);
  delay(1000);
  }

Alright @cattledog

I’ll try that and see what the outcome is.
But for now, I seem to have run into a little problem.

For the same system, I’m trying to obtain the airtime balance on the number in the module.
After a series of headaches and tests, I saw how to parse the account balance.

The problem is here.
I try to assign that value to “str3” so it can be printed out but what I notice is that str3 is first printed out as an empty string then after about four of five seconds after the module must have completed the USSD operation, the module then returns str3 with the right balance.

Could this be because of the loop it is in??
I tried bringing out this operation into its own function and then calling it from the main loop but the result is the same.

Here are my code and the result on the serial monitor.

//USSD Response
//+CUSD: 0, "You are on SmartCONNECT.Main Bal: N30.42;Dial
//*123*1# for Bonus Bal.Dial *315#, Call
//@ 11k/s; N7 access fee on 1st call of the day applies", 15

#include <SoftwareSerial.h>
SoftwareSerial mySerial(3, 2); //Rx and Tx respectively

char str3[10];//maximum size for str3
char Grsp[200];//size for max message
boolean newussd = false;//you may or may not use this

//declares these three to be false
boolean ussdprefixMatch = false;
boolean newMessage = false;
boolean prefixMatch = false;

void setup()
{
  Serial.begin(9600);
  Serial.setTimeout(100);

  mySerial.begin(9600); //
  mySerial.setTimeout(100);//

  Serial.println("Starting...");
  delay(1000);
  mySerial.println("AT");//
  delay(100);
  mySerial.println("AT+CMGF=1");//
  delay(100);

  mySerial.println("AT+CNMI=1,2,0,0,0");//
  delay(500);
  mySerial.println("AT+CUSD=1,\"*123#\"");

}


void loop()
{
  while (mySerial.available() > 0)//
  {
    //readBytes returns number read not zero referenced
    byte numChars = mySerial.readBytes(Grsp, 100);//
    Grsp[numChars] = '\0';//null Terminate
    newMessage = true;
    //Serial.println(Grsp);
  }


  if (strstr(Grsp, "+CUSD") != 0 and newMessage == true)
  {
    Serial.println("prefixMatch +CUSD");
    prefixMatch = true;
    newMessage = false;
  }

  else if (newMessage == true)
  {
    // Serial.println("no prefixMatch");
    newMessage = false;
  }

  if (prefixMatch == true)
  {
    prefixMatch = false;

    char* strtokussd;
    char* strtokussd2;


    //strtokussd tokenizes the string with the first colon
    //then strtokussd2 takes in null, i.e it continues with the first string then looks for the next colon
    strtokussd = strtok(Grsp, ":");
    strtokussd2 = strtok(NULL, ":");

    //the same strtokussd2 then finds the first semicolon
    //find semicolon which is where the amount stops
    strtokussd2 = strtok(NULL, ";");
    //strcopy then copies what's between the second colon and the first semi-colon and fills it in str3
    strcpy(str3, strtokussd2);

    //Below is the delay I tried using to wait so the right value must have been assigned to str3 but it still repeated same after waiting
    //delay (5000);//actually this delay might have to be put right before the balance printing
    //on the lcd screen takes place
    Serial.print("Balance = ");
    Serial.println(str3);
  }

}
//USSD Response for testing
//+CUSD: 0, "You are on SmartCONNECT.Main Bal: N30.42;Dial *123*1# for Bonus Bal.Dial *315#, Call @ 11k/s; N7 access fee on 1st call of the day applies", 15

For some odd reason, I did not see the option to attach when posting the first message so here's the result on the serial monitor.

The reading routine of this code was designed around the fact that the modem makes the complete message available after receiving it, and it can be read in one shot with .readBytes().

Your +CUSD message is over 150 characters, but you only read 100.

byte numChars = mySerial.readBytes(Grsp, 100);

Then, because of the while(mySerial.available()) you read the second part. I can’t quite replicate your exact outcome, but I can correct the problem I see, and can parse the balance correctly.

200 characters is probably a good max message size, and I have used that. I also limit the reading to 199 characters so that there is always room for a null terminator.

//USSD Response
//+CUSD: 0, "You are on SmartCONNECT.Main Bal: N30.42;Dial
//*123*1# for Bonus Bal.Dial *315#, Call
//@ 11k/s; N7 access fee on 1st call of the day applies", 15

#include <SoftwareSerial.h>
SoftwareSerial mySerial(3, 2); //Rx and Tx respectively

const byte maxMessage = 200;
char str3[10];//maximum size for str3
char Grsp[maxMessage];//size for max message
boolean newussd = false;//you may or may not use this

//declares these three to be false
boolean ussdprefixMatch = false;
boolean newMessage = false;
boolean prefixMatch = false;

void setup()
{
  Serial.begin(9600);
  Serial.setTimeout(100);

  mySerial.begin(9600); //
  mySerial.setTimeout(100);//

  Serial.println("Starting...");
  delay(1000);
  mySerial.println("AT");//
  delay(100);
  mySerial.println("AT+CMGF=1");//
  delay(100);

  mySerial.println("AT+CNMI=1,2,0,0,0");//
  delay(500);
  mySerial.println("AT+CUSD=1,\"*123#\"");

}

void loop()
{
  while (mySerial.available() > 0)//
  {
    //readBytes returns number read not zero referenced
    //byte numChars = mySerial.readBytes(Grsp, 100);//
    byte numChars = mySerial.readBytes(Grsp, maxMessage-1);//
    Grsp[numChars] = '\0';//null Terminate
    newMessage = true;
    //Serial.print(numChars);
    //Serial.print(" ");
    //Serial.println(Grsp);
  }

  if (strstr(Grsp, "+CUSD") != 0 and newMessage == true)
  {
    Serial.println("prefixMatch +CUSD");
    prefixMatch = true;
    newMessage = false;
  }

  else if (newMessage == true)
  {
    // Serial.println("no prefixMatch");
    newMessage = false;
  }

  if (prefixMatch == true)
  {
    prefixMatch = false;

    char* strtokussd;
    char* strtokussd2;

    //strtokussd tokenizes the string with the first colon
    //then strtokussd2 takes in null, i.e it continues with the first string then looks for the next colon
    strtokussd = strtok(Grsp, ":");
    strtokussd2 = strtok(NULL, ":");

    //the same strtokussd2 then finds the first semicolon
    //find semicolon which is where the amount stops
    strtokussd2 = strtok(NULL, ";");
    //strcopy then copies what's between the second colon and the first semi-colon and fills it in str3
    strcpy(str3, strtokussd2);

    //Below is the delay I tried using to wait so the right value must have been assigned to str3 but it still repeated same after waiting
    //delay (5000);//actually this delay might have to be put right before the balance printing
    //on the lcd screen takes place
    Serial.print("Balance = ");
    Serial.println(str3);
  }
}
//USSD Response for testing
//+CUSD: 0, "You are on SmartCONNECT.Main Bal: N30.42;Dial *123*1# for Bonus Bal.Dial *315#, Call @ 11k/s; N7 access fee on 1st call of the day applies", 15