Sending SMS using adafruit FONA working but returning Failed

Dear all,

i am working with an Arduino mega, connected to a SIM800L GPRS module. I use the adafruit FONA library to interact with the module.

I want to use the module to send an SMS to a predefined phone number.

Now i've setup a program to see if i can send a SMS:

#include "Adafruit_FONA.h"

#define FONA_RX 19
#define FONA_TX 18
#define FONA_RST 2

// this is a large buffer for replies
char replybuffer[255];


// I USE HARDWARESERIAL BECAUSE SOFTWARESERIAL IS NOT WORKING
HardwareSerial *fonaSerial = &Serial1;

// Use this for FONA 800 and 808s
Adafruit_FONA fona = Adafruit_FONA(FONA_RST);


uint8_t readline(char *buff, uint8_t maxbuff, uint16_t timeout = 0);

uint8_t type;

void setup() {
  while (!Serial);

  Serial.begin(9600);
  Serial.println(F("FONA basic test"));
  Serial.println(F("Initializing....(May take 3 seconds)"));

  fonaSerial->begin(4800);
  if (! fona.begin(*fonaSerial)) {
    Serial.println(F("Couldn't find FONA"));
    while (1);
  }

 

  // Unlock the SIM with a PIN code
  char PIN[5] = "0000";
  Serial.println(PIN);
  Serial.print(F("Unlocking SIM card: "));
  if (! fona.unlockSIM(PIN)) {
    Serial.println(F("Failed"));
  } else {
    Serial.println(F("OK!"));
  }

}


int8_t smsnum;
bool do_once = false;
void loop() {
  if (digitalRead(10) == true && do_once == false) {
    do_once = true;
    char sendto[21] = "+31612345678", message[141] = "testing..."; // NOT A REAL PHONE NUMBER
    Serial.print(F("Send to #"));
    Serial.println(sendto);
    Serial.println(message);
    if (!fona.sendSMS(sendto, message)) {
      Serial.println(F("Failed"));
    } else {
      Serial.println(F("Sent!"));
    }
  }
  else if (digitalRead(10) == false) {
    do_once = false;
  }
}

The code around the FONA funcitons are all copy pasted (and slightly modified) from the FONAtest example.

I use pin10 to simulate as an error. Every time 10 is set to HIGH, a message needs to be sent.

Now what happens is The sens SMS function returns FALSE meaning it prints out FAILED.
But then a couple of seconds later i DO get the SMS on my phone!

So the message did send, but the function responds the wrong outcome.

i looked into the Adafruit_FONA.cpp. The SMS function looks like this:

bool Adafruit_FONA::sendSMS(char *smsaddr, char *smsmsg) {
  if (!sendCheckReply(F("AT+CMGF=1"), ok_reply))
	  return false;

  char sendcmd[30] = "AT+CMGS=\"";
  strncpy(sendcmd + 9, smsaddr,
          30 - 9 - 2); // 9 bytes beginning, 2 bytes for close quote + null
  sendcmd[strlen(sendcmd)] = '\"';

  if (!sendCheckReply(sendcmd, F("> ")))
	  return false;

  DEBUG_PRINT(F("> "));
  DEBUG_PRINTLN(smsmsg);

  mySerial->println(smsmsg);
  mySerial->println();
  mySerial->write(0x1A);

  DEBUG_PRINTLN("^Z");

  if ((_type == FONA3G_A) || (_type == FONA3G_E)) {
    // Eat two sets of CRLF
    readline(200);
    // DEBUG_PRINT("Line 1: "); DEBUG_PRINTLN(strlen(replybuffer));
    readline(200);
    // DEBUG_PRINT("Line 2: "); DEBUG_PRINTLN(strlen(replybuffer));
  }
  readline(10000); // read the +CMGS reply, wait up to 10 seconds!!!
  // DEBUG_PRINT("Line 3: "); DEBUG_PRINTLN(strlen(replybuffer));
  if (strstr(replybuffer, "+CMGS") == 0) {
    return false;
  }
  readline(1000); // read OK
  // DEBUG_PRINT("* "); DEBUG_PRINTLN(replybuffer);

  if (strcmp(replybuffer, "OK") != 0) {
    return false;
  }

  return true;
}

It seems as the code above returns false to the main sketch even though the SMS got send.

Does anyone have any idea why this is happening and how to fix it, or if i should just leave it en ignore the return of the function for now?

Kind regards,
Jan Tromp.

It looks like it is expecting certain replies based on the exact model of FONA being used. I don't know if the library figures out the model itself or if the user is supposed to configure it somewhere.

For example, if '_type' is FONA3G_A or FONA3G_E then it expects two lines of reply before it expects to get:

+CMGS ...
OK ...

If the '_type' value doesn't match the hardware/firmware it will either be expecting those first two lines to start with "+CMGS" and "OK" when they don't OR it will throw away the two lines starting with "+CMGS" and "OK" and then start looking for the two lines that start with "+CMGS" and "OK". Either way it will report that the send has failed.

Hey johnwasser,

Thanks for your reply.

I think i know what you are talking about:

In the example code there was a extra part of the code, which i left out to keep the code short:

type = fona.type();
  Serial.println(F("FONA is OK"));
  Serial.print(F("Found "));
  switch (type) {
    case FONA800L:
      Serial.println(F("FONA 800L")); break;
    case FONA800H:
      Serial.println(F("FONA 800H")); break;
    case FONA808_V1:
      Serial.println(F("FONA 808 (v1)")); break;
    case FONA808_V2:
      Serial.println(F("FONA 808 (v2)")); break;
    case FONA3G_A:
      Serial.println(F("FONA 3G (American)")); break;
    case FONA3G_E:
      Serial.println(F("FONA 3G (European)")); break;
    default: 
      Serial.println(F("???")); break;
  }

This part printed out "Found ? ? ?" (i cant write triple ? cus its apperantly a shotcut for ??? )

I did some checking and the fona.type() returned "0", the type i am using is the SIM800L, which is "1" in the switch case.

Do you know what could be the reason it's not returning the right type?
Or is there a way to manually overwrite the type?

I did some more looking around in the library after your comment and i think i know whats going on.
it would be awesome if someone can check if i'm right or if i'm missing something.

Lets break down the class method adafruit_FONA::SendSMS:

if (!sendCheckReply(F("AT+CMGF=1"), ok_reply))
	  return false;

this part sets the msg mode of the SIM800L to 1. Assuming the pin number is unlocked, this function will return "OK". ok_reply is set to the same "OK". This means that under normal circumstances this the if will return true and the inside of the if is skipped.

char sendcmd[30] = "AT+CMGS=\"";
  strncpy(sendcmd + 9, smsaddr,
          30 - 9 - 2); // 9 bytes beginning, 2 bytes for close quote + null
  sendcmd[strlen(sendcmd)] = '\"';

  if (!sendCheckReply(sendcmd, F("> ")))
	  return false;

Next, it sends the send sms command and checks if the response is ">". This is also the case in normal circumstances.

DEBUG_PRINT(F("> "));
  DEBUG_PRINTLN(smsmsg);

  mySerial->println(smsmsg);
  mySerial->println();
  mySerial->write(0x1A);

  DEBUG_PRINTLN("^Z");

Then it writes the SMS msg to the serial monitor and sends 1A hex (command for ctrl-z, which is needed to end the msg field).

if ((_type == FONA3G_A) || (_type == FONA3G_E)) {
    // Eat two sets of CRLF
    readline(200);
    // DEBUG_PRINT("Line 1: "); DEBUG_PRINTLN(strlen(replybuffer));
    readline(200);
    // DEBUG_PRINT("Line 2: "); DEBUG_PRINTLN(strlen(replybuffer));
  }

it then checks the type of SIM. In my case SIM800L, to we skip this part (even though my type doesnt return the right type, it does not return any of the above types).

readline(10000); // read the +CMGS reply, wait up to 10 seconds!!!
  // DEBUG_PRINT("Line 3: "); DEBUG_PRINTLN(strlen(replybuffer));
  if (strstr(replybuffer, "+CMGS") == 0) {
    return false;
  }

It then checks if the response from the sim is equal to "+CMGS".

And this is where it goes wrong:

I used an example sketch where i can send AT commands via the serial monitor and read the response from the SIM module.
The serial monitor can be seen in the attached pic.

As mentioned, the if aksed if the response was equal to "+CMGS". You can see the SIM returns "+CMGS: 38" where 38 is i think the amount of SMS sent.

The last if in the method asks if the next response is equal to OK, which it is again.

So what does this tell us?

Is this a smol bug in the library? is this something i can ignore for now?

kind regards,
Jan Tromp

jantromp:

readline(10000); // read the +CMGS reply, wait up to 10 seconds!!!

// DEBUG_PRINT("Line 3: "); DEBUG_PRINTLN(strlen(replybuffer));
 if (strstr(replybuffer, "+CMGS") == 0) {
   return false;
 }


It then checks if the response from the sim is equal to "+CMGS".

And this is where it goes wrong:

It's using 'strstr()', not 'strcmp()'. I think that is looking for the index of the first occurrence of "+CMGS" in the string and checking for 0. I think that means: Does the line start with the characters "+CMGS"?

i am working with an Arduino mega, connected to a SIM800L GPRS module. I use the adafruit FONA library to interact with the module.

Why struggle with the FONA library. Why not uses either the direct AT commands or a more generic library like GitHub - cristiansteib/Sim800l: Library sim800l for Arduino UNO (maybe sim900l work)

I am not using an arduino mega and sim module as separate modules.

i am using an M-Duino from industrial shields (link to industrial shields)

Industrial shields recommends using the FONA library and honestly i haven't looked for alternative libraries :confused:

Your link is not working --> here is it fixed : link to industrial shields

do the FONA library basic examples work with your setup ?

Thanks, fixed the link!

The FONA test example is able to send the SMS but it does return FAILED to the serial monitor.

The FONA test example is able to send the SMS but it does return FAILED to the serial monitor.

can you go edit the "FONAConfig.h" file in your system and ensure the the debug traces are activated (you should have that line)?

Ensure as well that in FONAPlatStd.h, you have defined the output stream to be Serial (like this)

with both those set up and your arduino connected to a Serial console, run the example and check the AT command trail. that would help understand why the library does not catch the correct sending of the SMS

Both of the above were already defined so i didnt ave to change anything.

I did what you said and i made a screenshot of the serial monitor.

I use command U ot unlock the sim, then use s to send an sms.

+- 1 second after the OK is returned i get the SMS.

The image

Could you post your Exact Code (with fake phone number)?

Do you type the message through the console ? If so How quickly did you type your sms ?

Seems function times out and theN the OK arrives

its a long one but here is the code:

Note that i use hardware serial and since i'm using the Mduino as said before the RX,TX and RST pin might be unusual but these numbers are correct according to datasheet.

#include "Adafruit_FONA.h"

#define FONA_RX 19
#define FONA_TX 18
#define FONA_RST 2

// this is a large buffer for replies
char replybuffer[255];

// We default to using software serial. If you want to use hardware serial
// (because softserial isnt supported) comment out the following three lines 
// and uncomment the HardwareSerial line
//#include <SoftwareSerial.h>
//SoftwareSerial fonaSS = SoftwareSerial(FONA_TX, FONA_RX);
//SoftwareSerial *fonaSerial = &fonaSS;

// Hardware serial is also possible!
HardwareSerial *fonaSerial = &Serial1;

// Use this for FONA 800 and 808s
Adafruit_FONA fona = Adafruit_FONA(FONA_RST);
// Use this one for FONA 3G
//Adafruit_FONA_3G fona = Adafruit_FONA_3G(FONA_RST);

uint8_t readline(char *buff, uint8_t maxbuff, uint16_t timeout = 0);

uint8_t type;

void setup() {
  while (!Serial);

  Serial.begin(115200);
  Serial.println(F("FONA basic test"));
  Serial.println(F("Initializing....(May take 3 seconds)"));

  fonaSerial->begin(4800);
  if (! fona.begin(*fonaSerial)) {
    Serial.println(F("Couldn't find FONA"));
    while (1);
  }
  type = fona.type();
  Serial.println(F("FONA is OK"));
  Serial.print(F("Found "));
  switch (type) {
    case FONA800L:
      Serial.println(F("FONA 800L")); break;
    case FONA800H:
      Serial.println(F("FONA 800H")); break;
    case FONA808_V1:
      Serial.println(F("FONA 808 (v1)")); break;
    case FONA808_V2:
      Serial.println(F("FONA 808 (v2)")); break;
    case FONA3G_A:
      Serial.println(F("FONA 3G (American)")); break;
    case FONA3G_E:
      Serial.println(F("FONA 3G (European)")); break;
    default: 
      Serial.println(F("???")); break;
  }
  
  // Print module IMEI number.
  char imei[16] = {0}; // MUST use a 16 character buffer for IMEI!
  uint8_t imeiLen = fona.getIMEI(imei);
  if (imeiLen > 0) {
    Serial.print("Module IMEI: "); Serial.println(imei);
  }

  // Optionally configure a GPRS APN, username, and password.
  // You might need to do this to access your network's GPRS/data
  // network.  Contact your provider for the exact APN, username,
  // and password values.  Username and password are optional and
  // can be removed, but APN is required.
  //fona.setGPRSNetworkSettings(F("your APN"), F("your username"), F("your password"));

  // Optionally configure HTTP gets to follow redirects over SSL.
  // Default is not to follow SSL redirects, however if you uncomment
  // the following line then redirects over SSL will be followed.
  //fona.setHTTPSRedirect(true);

  printMenu();
}

void printMenu(void) {
  Serial.println(F("-------------------------------------"));
  Serial.println(F("[?] Print this menu"));
  Serial.println(F("[a] read the ADC 2.8V max (FONA800 & 808)"));
  Serial.println(F("[b] read the Battery V and % charged"));
  Serial.println(F("[C] read the SIM CCID"));
  Serial.println(F("[U] Unlock SIM with PIN code"));
  Serial.println(F("[i] read RSSI"));
  Serial.println(F("[n] get Network status"));
  Serial.println(F("[v] set audio Volume"));
  Serial.println(F("[V] get Volume"));
  Serial.println(F("[H] set Headphone audio (FONA800 & 808)"));
  Serial.println(F("[e] set External audio (FONA800 & 808)"));
  Serial.println(F("[T] play audio Tone"));
  Serial.println(F("[P] PWM/Buzzer out (FONA800 & 808)"));

  // FM (SIM800 only!)
  Serial.println(F("[f] tune FM radio (FONA800)"));
  Serial.println(F("[F] turn off FM (FONA800)"));
  Serial.println(F("[m] set FM volume (FONA800)"));
  Serial.println(F("[M] get FM volume (FONA800)"));
  Serial.println(F("[q] get FM station signal level (FONA800)"));

  // Phone
  Serial.println(F("[c] make phone Call"));
  Serial.println(F("[A] get call status"));
  Serial.println(F("[h] Hang up phone"));
  Serial.println(F("[p] Pick up phone"));

  // SMS
  Serial.println(F("[N] Number of SMSs"));
  Serial.println(F("[r] Read SMS #"));
  Serial.println(F("[R] Read All SMS"));
  Serial.println(F("[d] Delete SMS #"));
  Serial.println(F("[s] Send SMS"));
  Serial.println(F("[u] Send USSD"));
  
  // Time
  Serial.println(F("[y] Enable network time sync (FONA 800 & 808)"));
  Serial.println(F("[Y] Enable NTP time sync (GPRS FONA 800 & 808)"));
  Serial.println(F("[t] Get network time"));

  // GPRS
  Serial.println(F("[G] Enable GPRS"));
  Serial.println(F("[g] Disable GPRS"));
  Serial.println(F("[l] Query GSMLOC (GPRS)"));
  Serial.println(F("[w] Read webpage (GPRS)"));
  Serial.println(F("[W] Post to website (GPRS)"));

  // GPS
  if ((type == FONA3G_A) || (type == FONA3G_E) || (type == FONA808_V1) || (type == FONA808_V2)) {
    Serial.println(F("[O] Turn GPS on (FONA 808 & 3G)"));
    Serial.println(F("[o] Turn GPS off (FONA 808 & 3G)"));
    Serial.println(F("[L] Query GPS location (FONA 808 & 3G)"));
    if (type == FONA808_V1) {
      Serial.println(F("[x] GPS fix status (FONA808 v1 only)"));
    }
    Serial.println(F("[E] Raw NMEA out (FONA808)"));
  }
  
  Serial.println(F("[S] create Serial passthru tunnel"));
  Serial.println(F("-------------------------------------"));
  Serial.println(F(""));

}
void loop() {
  Serial.print(F("FONA> "));
  while (! Serial.available() ) {
    if (fona.available()) {
      Serial.write(fona.read());
    }
  }

  char command = Serial.read();
  Serial.println(command);


  switch (command) {
 
    case 'U': {
        // Unlock the SIM with a PIN code
        char PIN[5];
        flushSerial();
        Serial.println(F("Enter 4-digit PIN"));
        readline(PIN, 3);
        Serial.println(PIN);
        Serial.print(F("Unlocking SIM card: "));
        if (! fona.unlockSIM(PIN)) {
          Serial.println(F("Failed"));
        } else {
          Serial.println(F("OK!"));
        }
        break;
      }

 
    case 's': {
        // send an SMS!
        char sendto[21], message[141];
        flushSerial();
        Serial.print(F("Send to #"));
        readline(sendto, 20);
        Serial.println(sendto);
        Serial.print(F("Type out one-line message (140 char): "));
        readline(message, 140);
        Serial.println(message);
        if (!fona.sendSMS(sendto, message)) {
          Serial.println(F("Failed"));
        } else {
          Serial.println(F("Sent!"));
        }

        break;
      }

 
    

    
     


    
  }
  
  // flush input
  flushSerial();
  while (fona.available()) {
    Serial.write(fona.read());
  }

}

void flushSerial() {
  while (Serial.available())
    Serial.read();
}

char readBlocking() {
  while (!Serial.available());
  return Serial.read();
}
uint16_t readnumber() {
  uint16_t x = 0;
  char c;
  while (! isdigit(c = readBlocking())) {
    //Serial.print(c);
  }
  Serial.print(c);
  x = c - '0';
  while (isdigit(c = readBlocking())) {
    Serial.print(c);
    x *= 10;
    x += c - '0';
  }
  return x;
}

uint8_t readline(char *buff, uint8_t maxbuff, uint16_t timeout) {
  uint16_t buffidx = 0;
  boolean timeoutvalid = true;
  if (timeout == 0) timeoutvalid = false;

  while (true) {
    if (buffidx > maxbuff) {
      //Serial.println(F("SPACE"));
      break;
    }

    while (Serial.available()) {
      char c =  Serial.read();

      //Serial.print(c, HEX); Serial.print("#"); Serial.println(c);

      if (c == '\r') continue;
      if (c == 0xA) {
        if (buffidx == 0)   // the first 0x0A is ignored
          continue;

        timeout = 0;         // the second 0x0A is the end of the line
        timeoutvalid = true;
        break;
      }
      buff[buffidx] = c;
      buffidx++;
    }

    if (timeoutvalid && timeout == 0) {
      //Serial.println(F("TIMEOUT"));
      break;
    }
    delay(1);
  }
  buff[buffidx] = 0;  // null term
  return buffidx;
}

i deleted a large portion of the code because i need to keep this within 900 characters. The original code im using is the FONAtest from the fona library so you could also use that,

With this code i write 'U' in serial monitor and unlock, then write 's', then fill in phonenumber and msg, then it responds

If you look here in the library they hardcoded something for FONA3G_A and FONA3G_E modem types, skipping two sets of CRLF if they come back within 200ms

Then here they wait up to 10 seconds to receive a line and check if it contains "+CMGS"

I would bet that your modem firmware/type does not Exactly match the expected output sequence and thus they miss the line that contains "+CMGS" (as we still see it coming in the serial console)

They have debug statements commented out in the library, I would suggest you un comment them to see exactly what your modem answers (or write a small code sending the same hardcoded at commands and look at what comes back) and then adapt the code to capture the correct line with "+CMGS".

That was also a solution i was thinking of. Although i dont like messing around in libraries :stuck_out_tongue:

Thanks you all for helping me out!

you're welcome.

Hi jantromp,

You have the same problem as I do. I have tried several options and hope this will solve the problem.

There are several response lines obtained after the message sending command(AT+CMGS=)
Response when the message delivery process is successful maybe like this

+CMGS: 112

OK

the "+CMGS" character appears on the third line. this may be fine when our module type is identified by the library. in the sendSMS() function that you mentioned before(Adafruit_FONA.cpp), When the module we use is not identified (???), the response that is read is the character in the first line. And then when data on replybuffer checking using strstr(replybuffer, "+CMGS") == 0 the result will be false

if ((_type == FONA3G_A) || (_type == FONA3G_E)) {
    // Eat two sets of CRLF
    readline(200);
    // DEBUG_PRINT("Line 1: "); DEBUG_PRINTLN(strlen(replybuffer));
    readline(200);
    // DEBUG_PRINT("Line 2: "); DEBUG_PRINTLN(strlen(replybuffer));
  }
  readline(10000,true); // read the +CMGS reply, wait up to 10 seconds!!!
  //DEBUG_PRINT("Line 3: "); DEBUG_PRINTLN(strlen(replybuffer));
  DEBUG_PRINTLN(replybuffer); //read replybuffer data
  if (strstr(replybuffer, "+CMGS") == 0) {
    return false;
  }

the solution is to add a readline() function to read the first and second line responses like this

if ((_type == FONA3G_A) || (_type == FONA3G_E)) {
    // Eat two sets of CRLF
    readline(200);
    // DEBUG_PRINT("Line 1: "); DEBUG_PRINTLN(strlen(replybuffer));
    readline(200);
    // DEBUG_PRINT("Line 2: "); DEBUG_PRINTLN(strlen(replybuffer));
  }else{
	// Eat two sets of CRLF
    readline(200);
    // DEBUG_PRINT("Line 1: "); DEBUG_PRINTLN(strlen(replybuffer));
    readline(200);
    // DEBUG_PRINT("Line 2: "); DEBUG_PRINTLN(strlen(replybuffer));  
  }
  readline(10000); // read the +CMGS reply, wait up to 10 seconds!!!
  // DEBUG_PRINT("Line 3: "); DEBUG_PRINTLN(strlen(replybuffer));
  if (strstr(replybuffer, "+CMGS") == 0) {
    return false;
  }
   
  //continue to catch OK response

The proper way to handle any request would be to read the full answer until OK not taking a guess at which line to read... and then parse it (or to listen to the answer looking for a specific keyword for a while and then trying to empty the incoming buffer for the rest of the answer).