Sim800L Weather Station

Hi guys

I am trying to build a Weather Station which transmits temperature data by GPRS to a Thingspeak channel.
For this purpose, i decided to use Sim800L module. Since i’m going on a battery+solar pannel system, i need to reduce the power consumption of the Sim800L as much as i can.One way to achieve this is to reduce the delay AT commands take. Currently i have like 3 or 4 seconds delay after each AT command and instead of waiting a preset amount of time i would like my program to continue as soon as the OK message is returned.

I found a similar project here and tried to adapt the code for mine. Unfortunately, his code is kind of complex to me as i’m a beginner and i can’t make it work. I’m planning to use the fonction waitforok() after each AT command. However the original author uses HWSERIAL and when i changed it for SoftwareSerial in my waitfor() function i’m getting “expected primary-expression before ‘.’ token”

Can you help me to make my project work ? Why am i getting this error ? Am i on the right track ?

#include <SoftwareSerial.h>
#include <String.h>
#include <elapsedMillis.h>
char incomingByte;
elapsedMillis waitTime;

SoftwareSerial mySerial(7, 8);

void setup()
{
  mySerial.begin(9600);            
  Serial.begin(9600);    
}
 
void loop()
{         
  sendtemp();
  if (mySerial.available())
    Serial.write(mySerial.read());
    mySerial.println("AT+CPOWD=1");
}
void sendtemp()
{
 
  mySerial.println("AT+CSTT=\"Free\"");
  waitforok();
 
  ShowSerialData();
 
  mySerial.println("AT+CIICR");//bring up wireless connection
  waitforok();
 
  ShowSerialData();
  
  mySerial.println("AT+CIPSTART=\"TCP\",\"api.thingspeak.com\",\"80\"");//start up the connection
  waitforok();
 
  ShowSerialData();
 
  mySerial.println("AT+CIPSEND");//begin send data to remote server
  waitforok();
  ShowSerialData();
  
    String str="GET http://api.thingspeak.com/update?api_key=xxxxxxxxxx&field1=" + String("1234");
  mySerial.println(str);//begin send data to remote server
 waitforok();
  ShowSerialData();

  mySerial.println((char)26);//sending
  waitforok();;//waitting for reply, important! the time is base on the condition of internet 
  mySerial.println();
 
  ShowSerialData();
 
  mySerial.println("AT+CIPSHUT");//close the connection
 delay(100);
  ShowSerialData();
  
} 
void ShowSerialData()
{
  while(mySerial.available()!=0)
    Serial.write(mySerial.read());
    
}


bool waitFor(String searchString, int waitTimeMS) {
    //Serial.println("Waiting for string");
    waitTime = 0;
    String foundText;
    while (waitTime < waitTimeMS) {
        if (!SoftwareSerial.available()) {
          // Nothing in the buffer, wait a bit
            delay(5);
            continue;
        }
        
        // Get the next character in the buffer
         incomingByte = SoftwareSerial.read();
        if (incomingByte == 0) {
          // Ignore NULL character
          continue;
        }
        //Serial.print(incomingByte);
        foundText += incomingByte;
        
        if (foundText.lastIndexOf(searchString) != -1) {
            //Serial.print("Found string after ");
            //Serial.print(waitTime);
            //Serial.println("ms.");
            return true;
        }
    }
    
    // Timed out before finding it
    //Serial.println("string not found, timed out");
    return false;
}

/**
 * Wait for "OK" from the Sim800l
 */
bool waitforok() {
    return waitFor("\nOK\r\n", 4000);
}

and when i changed it for SoftwareSerial in my waitfor() function i'm getting "expected primary-expression before '.' token"

The EXACT error message, with line numbers and file name, please.

         incomingByte = SoftwareSerial.read();

You do not have an instance, of anything, called SoftwareSerial, so you can't call a method on a non-existent object.

Ok i decided to proceed from the beginning.
I changed “Softwareserial” in waitfor() by “mySerial” and now it seems to be working. My sketch compiles at least. However i can’t see messages in Serial monitor anymore when i use waitforok() after each AT command instead of delay(). Additionally, i’m not sure it is actually working because it still takes a lot of time to send the message to Thingspeak even though it sent the message.

I don’t now what i’ve done in last 15 mins but now i can’t even send the message i constantly get :

AT

OK
AT+CPIN?

+CPIN: READY

OK
AT+CREG?

+CREG: 0,5

OK
AT+CGATT?

+CGATT: 1

OK
AT+CIPSHUT

SHUT OK
AT+CIPSTATUS

OK

STATE: IP INITIAL
AT+CIPMUX=0

OK
AT+CSTT="Free"

OK
AT+CIICR

OK
AT+CIPSPRT=0

OK
AT+CIPSTART="TCP","api.thingspeak.com","80"

ERROR

in serial monitor

Let’s proceed step by step:

  • Why am i now getting ERROR when i’m trying to connect to Thingspeak (worked fine 15 mins ago even with waitforok() functions ) ?

  • Which AT commands can i drop and which ones must i keep in order to send my Thingspeak message ? (This could spare me some seconds).

Here is the latest version of my sketch :

#include <SoftwareSerial.h>
#include <String.h>
#include <elapsedMillis.h>
char incomingByte;
elapsedMillis waitTime;
SoftwareSerial mySerial(7, 8);


void setup()
{
  mySerial.begin(9600);               // the GPRS baud rate   
  Serial.begin(9600);    // the GPRS baud rate 

}
 
void loop()
{      
      sendtemp();
      if (mySerial.available())
    Serial.write(mySerial.read());
    mySerial.println("AT+CPOWD=1");
}

void sendtemp()
{

 mySerial.println("AT");
  delay(1000);
ShowSerialData();

  mySerial.println("AT+CPIN?");
  delay(1000);
ShowSerialData();

  mySerial.println("AT+CREG?");
  delay(1000);
ShowSerialData();

  mySerial.println("AT+CGATT?");
  delay(1000);
ShowSerialData();

  mySerial.println("AT+CIPSHUT");
  delay(1000);
ShowSerialData();

  mySerial.println("AT+CIPSTATUS");
  delay(2000);
ShowSerialData();

  mySerial.println("AT+CIPMUX=0");
  delay(2000);
 
  ShowSerialData();
 
  mySerial.println("AT+CSTT=\"Free\"");//start task and setting the APN,
  delay(1000);
 
  ShowSerialData();
 
  mySerial.println("AT+CIICR");//bring up wireless connection
  delay(3000);
 
  ShowSerialData();
 
  mySerial.println("AT+CIPSPRT=0");
  delay(3000);
 
  ShowSerialData();
  
  mySerial.println("AT+CIPSTART=\"TCP\",\"api.thingspeak.com\",\"80\"");//start up the connection
  delay(6000);
 
  ShowSerialData();
 
  mySerial.println("AT+CIPSEND");//begin send data to remote server
  delay(4000);
  ShowSerialData();
  
    String str="GET http://api.thingspeak.com/update?api_key=FEC51NIPAQ2LWYCH&field1=" + String("125");
  mySerial.println(str);//begin send data to remote server
  delay(4000);
  ShowSerialData();

  mySerial.println((char)26);//sending
  delay(4000);//waitting for reply, important! the time is base on the condition of internet 
  mySerial.println();
 
  ShowSerialData();
 
  mySerial.println("AT+CIPSHUT");//close the connection
  delay(100);
  ShowSerialData();
  
} 
void ShowSerialData()
{
  while(mySerial.available()!=0)
    Serial.write(mySerial.read());
}


bool waitFor(String searchString, int waitTimeMS) {
    //Serial.println("Waiting for string");
    waitTime = 0;
    String foundText;
    while (waitTime < waitTimeMS) {
        if (!mySerial.available()) {
          // Nothing in the buffer, wait a bit
            delay(5);
            continue;
        }
        
        // Get the next character in the buffer
         incomingByte = mySerial.read();
        if (incomingByte == 0) {
          // Ignore NULL character
          continue;
        }
        //Serial.print(incomingByte);
        foundText += incomingByte;
        
        if (foundText.lastIndexOf(searchString) != -1) {
            //Serial.print("Found string after ");
            //Serial.print(waitTime);
            //Serial.println("ms.");
            return true;
        }
    }
    
    // Timed out before finding it
    //Serial.println("string not found, timed out");
    return false;
}

/**
 * Wait for "OK" from the Sim800l
 */
bool waitforok() {
    return waitFor("\nOK\r\n", 10000);
}

bool waitforconnectok(){
  return waitFor("\nCONNECT OK\r\n",6000);
}

bool waitforsendok(){
return waitFor("\nSEND OK\r\n",6000);
}
void loop()
{      
      sendtemp();
      if (mySerial.available())
    Serial.write(mySerial.read());
    mySerial.println("AT+CPOWD=1");

Why is AT+CPOWD sent to the modem on every pass through loop()?

However i can't see messages in Serial monitor anymore when i use waitforok() after each AT command instead of delay().

waitforok() calls waitfor() in which all the Serial.print() statements are commented out. I don't know why, after doing that, you expect to see anything in the Serial Monitor application.

It was on the original code, i removed the at+cpowd part for now.

You’re right i hadn’t paid attention to the comments in waitfor() function.
Now the wait for () function works properly.

The error message when trying to connect to thingspeak was caused by the fact that i had removed the CIFSR part. I looked in the Sim800 application note "how to establish TCP server connexion page 6 here.
Now it works randomly but most of the time the message is not sent and i’m getting ERROR after CIPSEND and no SEND OK after GET

#include <SoftwareSerial.h>
#include <String.h>
#include <elapsedMillis.h>
char incomingByte;
elapsedMillis waitTime;
SoftwareSerial mySerial(7, 8);


void setup()
{
  mySerial.begin(9600);               // the GPRS baud rate   
  Serial.begin(9600);    // the GPRS baud rate 
}
 
void loop()
{      
      sendtemp();
      delay(10000);
}
void sendtemp()
{
 
  mySerial.println("AT");
  waitforok();

  mySerial.println("AT+CPIN?");
  waitforok();

  mySerial.println("AT+CREG?");
  waitforok();

  mySerial.println("AT+CGATT?");
  waitforok();

  mySerial.println("AT+CIPSHUT");
  waitforshutok();

  mySerial.println("AT+CIPSTATUS");
  waitforok();

  mySerial.println("AT+CIPMUX=0");
  waitforok();


  mySerial.println("AT+CSTT=\"Free\"");
  waitforok();
 
  mySerial.println("AT+CIICR");//bring up wireless connection
  waitforok();

  mySerial.println("AT+CIPSPRT=0");
  waitforok();

  mySerial.println("AT+CIFSR");
  delay(200);
  
  mySerial.println("AT+CIPSTART=\"TCP\",\"api.thingspeak.com\",\"80\"");//start up the connection
  waitforok();
 
  mySerial.println("AT+CIPSEND");//begin send data to remote server
  waitforconnectok();

  String str="GET http://api.thingspeak.com/update?api_key=OB4N9YVF5WQ63CE2=" + String("100");
  mySerial.println(str);//begin send data to remote serve
  delay(5000);
 ShowSerialData();
  
  mySerial.println((char)26);//sending
 delay(5000)
 ShowSerialData();

//mySerial.println("AT+CSCLK=2");
  
} 
void ShowSerialData()
{
  while(mySerial.available()!=0)
Serial.write(mySerial.read());
}

bool waitFor(String searchString, int waitTimeMS) {
    Serial.println("Waiting for string");
    waitTime = 0;
    String foundText;
    while (waitTime < waitTimeMS) {
        if (!mySerial.available()) {
          // Nothing in the buffer, wait a bit
            delay(5);
            continue;
        }
        
        // Get the next character in the buffer
         incomingByte = mySerial.read();
        if (incomingByte == 0) {
          //Ignore NULL character
          continue;
        }
        Serial.print(incomingByte);
        foundText += incomingByte;
        
        if (foundText.lastIndexOf(searchString) != -1) {
            Serial.print("Found string after ");
            Serial.print(waitTime);
            Serial.println("ms.");
            return true;
        }
    }
    
    // Timed out before finding it
    Serial.println("string not found, timed out");
    return false;
}

/**
 * Wait for "OK" from the Sim800l
 */
bool waitforok() {
    return waitFor("\nOK\r\n", 3000);
}

bool waitforshutok() {
    return waitFor("\nSHUT OK\r\n", 3000);
}


bool waitforconnectok(){
  return waitFor("\nCONNECT OK\r\n",6000);
}

bool waitforsendok(){
return waitFor("\nSEND OK\r\n",6000);
}
AT

OK
Found string after 14ms.
Waiting for string
AT+CPIN?

+CPIN: READY

OK
Found string after 37ms.
Waiting for string
AT+CREG?

+CREG: 0,5

OK
Found string after 50ms.
Waiting for string
AT+CGATT?

+CGATT: 1

OK
Found string after 49ms.
Waiting for string
AT+CIPSHUT

SHUT OK
Found string after 2233ms.
Waiting for string
AT+CIPSTATUS

OK
Found string after 31ms.
Waiting for string

STATE: ⸮⸮9%JŤAT+CIPMUX=0

OK
Found string after 38ms.
Waiting for string
AT+CSTT="Free"

OK
Found string after 42ms.
Waiting for string
AT+CIICR

OK
Found string after 210ms.
Waiting for string
AT+CIPSPRT=0

OK
Found string after 24ms.
Waiting for string
AT+CIFSR

10.92.216.247
AT+CIPSTART="TCP","api.thingspeak.com","80"

OK
Found string after 57ms.
Waiting for string
AT+CIPSEND

ERROR

CONNECT OK
Found string after 272ms.
GET http://api.thingspeak.com/update?api_key=OB4N9YVF5WQ63CE2=1Waiting for string

string not found, timed out
Waiting for string

CLOSED

What am i doing wrong this time ?

  mySerial.println("AT+CIPSPRT=0");
  waitforok();

  mySerial.println("AT+CIFSR");
  delay(200);

  mySerial.println("AT+CIPSTART=\"TCP\",\"api.thingspeak.com\",\"80\"");//start up the connection
  waitforok();

After you send the CIPSTART command, you wait for OK. When a response arrives, which command is it in response to?

If you are going to force asynchronous communications into a synchronous pattern, you can NOT ignore ANY communications in either direction.

I don't understand what you mean in your second sentence but i removed the waitfor() after cipsend and everything seems to be OK now.

  mySerial.println("AT+CIPSHUT");
  waitforshutok();

  mySerial.println("AT+CSTT=\"Free\"");
  waitforok();

  mySerial.println("AT+CIICR");//bring up wireless connection
  waitforok();

  mySerial.println("AT+CIPSPRT=0");
  waitforok();

  mySerial.println("AT+CIFSR");
  delay(200);
   ShowSerialData();
  
  mySerial.println("AT+CIPSTART=\"TCP\",\"api.thingspeak.com\",\"80\"");//start up the connection
  waitforconnectok();
  mySerial.println("AT+CIPSEND");//begin send data to remote server
  delay(100);
  String str="GET http://api.thingspeak.com/update?api_key=WC6X253PJ3AO9FF7&field1=" + String("200");
  mySerial.println(str);//begin send data to remote server
  ShowSerialData();
  mySerial.println((char)26);//sending
  mySerial.println();

The whole thing takes 25 seconds at approx 50 ma mean power consumption. (i guess, since my SIM800 can't connect through the multimeter). Correct me if i'm wrong but i think it can't get much better right ? I have no delay or waitfor() for CIPSEND and GET and they take nearly 20 secs on their own.

Now i will try to add sleep command and send temperature every 3 minutes to thingspeak. I'm planning to send a message only when temperature is different by 0.1°C from the previous recording to make my project even more power efficient. I'm also planning to send battery voltage in a different thingspeak channel to monitor batteries.

I even removed the blinking LED to spare some precious mahs. Any other ideas are welcome ! :)

Everything is working fine now. The last thing left to do is to store the battery voltage obtained with AT+CBC into a variable. I really don't know how to do this.

With this command i'm getting something like 0,56,3851 in the serial monitor. I only want to store the last number (here its 3851) in a "voltage" variable and send this variable to thingspeak as i did with temperature.

I have found some code on this forum that may be related. They speak about the strok function.

void parseData() {      

    char * strtokIndx; 

    strtokIndx = strtok(tempChars,",");     
    chargestate = atoi(strtokIndx); // 

    strtokIndx = strtok(NULL, ","); // 
    percent = atoi(strtokIndx);     //

    strtokIndx = strtok(NULL, ",");
    voltage = atoi(strtokIndx);     

}

My AT commands will look like this

void sendtemp()
{
float temppt=gettemp();

  mySerial.println("AT");
  waitforok();
  
  mySerial.println("AT");
  waitforok();

  mySerial.println("AT+CBC");

  mySerial.println("AT+CIPSHUT");
  waitforshutok();

etc..

I don't know how to handle this. I don't know what to type after myserialprint at+cbc in my sendtemp() function for the strok function to parse the response. And even if i manage to parse the response, how can i store the third part only (voltage) in a variable ?

Could you help me please ? :(

Ok no need to bother i will use atmega Vcc 1.1v as voltage monitor. :slight_smile:

how can i store the third part only (voltage) in a variable ?

How many times will you need to call strtok() to get the third token?

What do you need to do with the first two tokens?

Can you see how this would be really easy, if you thought about it for minute or two?