c++embeddeduartgsm

gsm900 module half duplex communication, serie , uart , Embedded system


I developed a program which sends in the first time a single msg a single time a msg which contains "please go" and waits until this person answers it with a msg either the letter B or the letter P is from the say a second time "bravo" and if this person sent B and to say it already send if sent p if did not answer does nothing but be careful the most important thing is that he waits very well until until he receives a msg but it didn't work i don't know what the problem is:

`  #include <SoftwareSerial.h>

SoftwareSerial SIM900(7, 8);

char incoming_char = 0;
bool waitForResponse = false;

void setup() {
 SIM900.begin(19200);
 Serial.begin(19200);
 delay(1000);
 SIM900.print("AT+CMGF=1\r");
 delay(100);
 SIM900.print("AT+CNMI=2,2,0,0,0\r");
 delay(100);
 sendSMS("please allez");
 waitForResponse = true;
}

void loop() {
 if (waitForResponse && SIM900.available() > 0) {
     incoming_char = SIM900.read();
     Serial.print(incoming_char);

  if (incoming_char == 'b') {  // Assuming 'b' is the first character of "bravo"
     waitForResponse = false;
     sendSMS("bravo");
     } else if (incoming_char == 'p') {  // Assuming 'p' is the first character of "please"
     waitForResponse = false;
     sendSMS("already sent");
    }
  }
 }

void sendSMS(const char *message) {
SIM900.print("AT+CMGF=1\r");
delay(100);
SIM900.println("AT+CMGS=\"+212684174580\"");
delay(100);
SIM900.println(message);
delay(100);
SIM900.println((char)26);
delay(100);
SIM900.println();
delay(5000);
}`

send me sms and waiting that im reponding and send again an sms depend of me answer


Solution

  • As identified through discussions in comments, the main issue seems to be poor handling of asynchronous responses from the GSM module. As a result, you are either discarding input or assuming that responses to your AT commands are actually SMS and thereby taking inappropriate action.

    Buffering

    While there are many valid strategies for dealing with this, one of the most natural ways is to buffer the input and deliver it to a handler function when it's complete. As you have indicated, a complete response ends with a newline or CRLF sequence. So you can simply collect input in your loop() until you encounter this.

    Here is an easy way to buffer your input, delivering whole lines to a handler function:

    #define MSG_BUFFER_SIZE 1000
    
    // This will receive a full message, called when process_input() receives
    // a whole line of text.
    void handle_message(const char* message, int length, bool overflow)
    {
    }
    
    // Process any characters in the input buffer. This will deliver a line of text
    // to handle_message(). Lines are delimited by CRLF or LF. The string is NUL-
    // terminated and excludes the line delimiter.
    void process_input()
    {
        static char buffer[MSG_BUFFER_SIZE];
        static int end = 0;
        
        while (SIM900.available() > 0) {
            char c = SIM900.read();
            if (c == '\n') {
                // Check for overflow and truncate
                bool overflow = (end >= MSG_BUFFER_SIZE);
                if (overflow) {
                    end = MSG_BUFFER_SIZE - 1;
                }
    
                // Carriage return is optional
                if (end > 0 && buffer[end-1] == '\r') {
                    --end;
                }
    
                // Terminate string, deliver, and reset
                buffer[end] = '\0';
                handle_message(buffer, end, overflow);
                end = 0;
            }
            else
            {
                // Add character to buffer if there is sufficient storage
                if (end < MSG_BUFFER_SIZE) {
                    buffer[end] = c;
                    ++end;
                }
            }
        }
    
        // Small delay to avoid busy looping
        delay(100);
    }
    

    Then your loop can be simply:

    void loop()
    {
        process_input();
    }
    

    That provides some basic scaffolding around line-based input. It provides a generous buffer size of 1000 characters (including NUL-terminator), and handles over-sized messages by discarding extra characters and setting a flag. The handle_message function then only needs to worry about whole messages, and you can make that as simple or complex as you desire.

    Sequencing

    From here, you may wish to add logic that ensures things happen in order. Now, I don't have this hardware to test, but going by the SIM900 AT command reference here, it appears that each of your commands should expect OK as the response.

    Furthermore, that document also describes the exact response format (in section 1.4 AT Command Syntax):

    Commands are usually followed by a response that includes <CR><LF><response><CR><LF>

    So, you should expect that every time you issue an AT command that has a documented response, you should expect a blank line, followed by that response on the next line. It's up to you whether to enforce this, or whether you want to be slightly more flexible and simply ignore blank lines.

    Since you need to issue a bunch of AT commands to get set up, a simple way of doing that without using delay hacks is to put your program into a limited set of "modes". You are either in the startup sequence, or you're ready to receive SMS. Issuing commands one at a time also avoids potentially overflowing the module's internal buffer.

    Here, I've defined a basic set of modes and array of AT commands, which will be run in sequence at startup. The next command is not issued until the previous one got an "OK" as per the specification:

    enum Mode {
        MODE_STARTUP_COMMAND,        // Issue next startup command
        MODE_STARTUP_EXPECT_EMPTY,   // Expect blank line followed by OK
        MODE_STARTUP_EXPECT_OK,      // Expect OK
        MODE_STARTUP_ERROR,          // Startup sequence failed
        MODE_READY,                  // Ready to receive SMS
    };
    Mode mode = MODE_STARTUP_COMMAND;
    
    const char* startup_commands[] = {
        "AT+CMGF=1\r",
        "AT+CNMI=2,2,0,0,0\r",
        NULL,                        // NULL ends the command sequence
    };
    const char** startup_next_command = startup_commands;
    
    void setup()
    {
        SIM900.begin(19200);
        Serial.begin(19200);
        delay(1000);
    }
    

    Now you can add the required state handling to your loop and message-handler, ensuring that things happen in the expected order:

    void handle_message(const char* message, int length, bool overflow)
    {
        if (mode == MODE_STARTUP_EXPECT_EMPTY) {
            if (length == 0) {
                mode = MODE_STARTUP_EXPECT_OK;
            } else {
                mode = MODE_STARTUP_ERROR;
            }
        }
        else if (mode == MODE_STARTUP_EXPECT_OK) {
            if (0 == strcmp(message, "OK")) {
                ++startup_next_command;
                mode = MODE_STARTUP_COMMAND;
            } else {
                mode = MODE_STARTUP_ERROR;
            }
        }
        else if (mode == MODE_READY)
        {
            // You probably received a SMS, and can handle it here. Add extra logic
            // as befits your needs.
        }
    }
    

    Finally, to actually run the startup sequence, extend the loop function as follows:

    void loop()
    {
        // Send next command in startup sequence
        if (mode == MODE_STARTUP_COMMAND) {
            if (*startup_next_command != NULL) {
                SIM900.send(*startup_next_command);
                mode = MODE_STARTUP_EXPECT_EMPTY;
            } else {
                mode = MODE_READY;
            }
        }
    
        // Input processing
        process_input();
    }
    

    Demo

    An example of this sequencing in action is here: https://godbolt.org/z/heM5K87eM

    When developing software for an embedded device, it can be helpful to make a test-bed like this to check the basics of your code. That's especially useful if you don't have easy access to (or simply don't use) other techniques such as logging messages via the serial port.

    There's nothing fancy about what I did in the demo. It doesn't need to be. It just needs to be functional.

    Notes

    Using the above as a basic template, you should be in a reasonable position to perform error recovery if necessary, by adding extra states. For instance, if one of the AT commands returns an error, then right now your device is totally hosed until you reboot it. Instead, you might want to jump into a state that waits for some amount of time and then reinitializes the startup sequence.