Sunday, August 7, 2016

Posting to Thingspeak over GPRS with SIM900 & Seeeduino Stalker & GPRSBee

My family has a cabin in the remote upper peninsula of Michigan. There we have a solar array that keeps the various vehicles batteries charged over the winter. I wanted to track the battery status through the winter when no one is up there. However, there is no internet up that far, but there is cell phone reception. So the plan is to have a Seeeduino Stalker run up 24/7/365 off of a lipo battery. Every hour or so it will power up and push the state of charge of the battery bank and voltage of the solar panels to Thingspeak.

A few people have worked on this, but even less have published in detail how to do it for the rest of us. So I've been working on it for the last month or so. I now have the GPRS able to connect to thingspeak and push data manually. All I have to do it clean up the code and make it into a more power friendly version that will work year round.

Hardware:

SeeedStalker 3.0
GPRS Bee Rev. 4 (SIM900)
LiPo Battery

Steps:
  1. Wake Seeedstalker from sleep
  2. Power up GPRSBee
  3. Pull data from sensors
  4. Connect to wireless network
  5. Connect to thingspeak
  6. Push data to thingspeak
Hardware Modifications:

It is necessary to solder P3 and P4 for for the RTC interrupts to function correctly. See the Seeeduino Stalker wiki for more details on this.
You will also need to solder a wire from pin 9 on the Bee socket to D5. See why here:
http://old.gprsbee.com/ 
Finally, for debugging, I find it's essential to have serial communication with my computer, so I've had to switch to using SW serial on D6 & D7. This means dealing with the Seeeduino Stalker v3.0's hardware bug. I found Seeed's wiki does not completely address the hardware bug. I had to cut a trace on P6 in addition to the one for P7. If you're having a hard time, just use a multi-meter to ensure you are wired how you expect to be.


Code:

1) Wake Seeedstalker from sleep -

This code has been written mostly by SeeedStudio on their wiki:
http://www.seeedstudio.com/wiki/Seeeduino-Stalker_v3#Data_Logger_Example
I'm not going to cover it again.

2) Power up GPRSBee - I will be adding this gradually in the coming weeks.
void gprspwr_on()
{
  Serial.println("Turning on GPRS");
  pinMode(5, OUTPUT);
  digitalWrite(5,LOW);
  delay(1000);
  digitalWrite(5,HIGH);
  delay(2000);
  digitalWrite(5,LOW);
  readATcommand("Call Ready",6,10000);
  if (answer == 1){
    Serial.println("GPRSbee is on!");
  }
}

void gprspwr_off()
{
  Serial.println("Turning off GPRS");
  pinMode(5, OUTPUT);
  digitalWrite(5,LOW);
  delay(1000);
  digitalWrite(5,HIGH);
  delay(2000);
  digitalWrite(5,LOW);
  answer = readATcommand("NORMAL POWER DOWN",2,2000);
  if (answer == 1){
    Serial.println("Sucessful Powerdown Complete");
  }
}

boolean gprspwr_status()
{
  Serial.println("GPRS Power Status");
  answer = sendATcommand("AT", "OK", 2, 2000);
  if (answer == 0){
    Serial.println("GPRSbee is Powered off!");
  }  
  else if (answer == 1){
    Serial.println("GPRSbee is on!");
  }
  return answer;
}

int8_t readATcommand(char* expected_answer1, unsigned int expected_answers, unsigned int timeout)
{

  uint8_t x=0,  answer=0;
  boolean complete = 0;
  char a;
  char response[100];
  unsigned long previous;
  String  incomingdata;
  boolean first;

  previous = millis();

  for(int i = 0; i < expected_answers; i++){
    x = 0;
    complete = 0;
    a = 0;
    first = 0;
    memset(response, '\0', 100);    // Initialize the string
    do{
      if(SIM900.available() != 0){
        a = SIM900.read();
        //Serial.println(a,DEC);
        if (a == 13){
          a = SIM900.read();
          //Serial.println(a,DEC);
          if (a == 10){
            if (first == 0){
              //keep going, just ignore it
            }
            else{
              Serial.print("string: #");
              Serial.print(response);
              Serial.println("#");
              complete = 1;
            }
          }
        }
        else if(a == 0){
          Serial.println("Found a blank");
        }
        else  {
          response[x] = a;
          x++;
          first = 1;
          //Serial.println(response);
        }
        if(strstr(response, expected_answer1) != NULL)
        {
          answer = 1;
          complete = 1;
          Serial.print("string: #");
          Serial.print(response);
          Serial.println("#");
          return answer;
        }
        else if(strstr(response, "ERROR") != NULL)
        {
          answer = 2;
        }
      }
    }
    while((complete == 0) && ((millis() - previous) < timeout));
    //Serial.println(i);
 
  }

  return answer;
}

3) Pull data from sensors - This is dependent on your application, so I'm not going to cover it.

4) Connect to wireless network & thingspeak - I will be adding this gradually in the coming weeks.

void loop() {
  if (SIM900.available())
  {
    Serial.write(SIM900.read());
  }
  //power up gprs
  gprspwr_on();

  //connect gprs to internet
  answer = sendATcommand("AT+CGATT?","OK",5,2000);
  answer = sendATcommand("AT+CSTT=\"CMNET\"","OK",3,2000);
  answer = sendATcommand("AT+CIICR","OK",3,2000);
  answer = sendATcommand("AT+CIFSR","OK",3,2000);
  answer = sendATcommand("AT+CIPSPRT=0","OK",3,2000);

  //connect gprs to thingspeak
  answer = sendATcommand("AT+CIPSTART=\"tcp\",\"api.thingspeak.com\",\"80\"","CONNECT OK",5,2000);

  //post data to thingspeak
  answer = senddata(battery_percent());

  delay(5000);

  //power down gprs
  gprspwr_off();

  //put arduino to sleep?
  for (int i=0; i<60; i++){
    delay(1000);
    Serial.println(i);
  }
}

int8_t sendATcommand(char* ATcommand, char* expected_answer1, unsigned int expected_answers, unsigned int timeout)
{

  uint8_t x=0,  answer=0;
  boolean complete = 0, first = 0;
  char a;
  char response[100];
  unsigned long previous;
  String  incomingdata;


  delay(100);
  //Serial.println("Send AT Command");
  while( SIM900.available() > 0) SIM900.read();    // Clean the input buffer
  SIM900.println(ATcommand);    // Send the AT command
  Serial.println(ATcommand);
  //Serial.println("AT Command Sent!");

  previous = millis();

  for(int i = 0; i < expected_answers; i++){
    x = 0;
    complete = 0;
    a = 0;
    first = 0;
    memset(response, '\0', 100);    // Initialize the string
    do{
      if(SIM900.available() != 0){
        a = SIM900.read();
        //Serial.println(a);
        //Serial.println(a,DEC);
        if (a == 13){
          a = SIM900.read();
          //Serial.println(a,DEC);
          if (a == 10){
            if (first == 0){
              //keep going, just ignore it
            }
            else{
              Serial.print("string: #");
              Serial.print(response);
              Serial.println("#");
              complete = 1;
            }
          }
        }
        else if(a == 0){
          Serial.println("Found a blank");
        }
        else  {
          response[x] = a;
          x++;
          first = 1;
          //Serial.println(response);
        }
        if (strstr(response, expected_answer1) != NULL)  
        {
          answer = 1;
          complete = 1;
          Serial.print("string: #");
          Serial.print(response);
          Serial.println("#");
        }
        else if(strstr(response, "ERROR") != NULL)
        {
          answer = 2;
          complete = 1;
        }
      }
    }
    while((complete == 0) && ((millis() - previous) < timeout));
 
  }
  return answer;
}

5) Push data to thingspeak - I will be adding this gradually in the coming weeks.

int8_t senddata(float data){

  Serial.print("Battery Percent = ");
  Serial.println(data);

  //while( SIM900.available() > 0) Serial.write(SIM900.read());    // Clean the input buffer
  SIM900.println("AT+CIPSEND");
  //Serial.println("AT+CIPSEND");
  while( SIM900.available() > 0) Serial.write(SIM900.read());    // Clean the input buffer
  delay(500);
  SIM900.println("POST /update HTTP/1.1");    // Send the AT command
  //Serial.println("POST /update HTTP/1.1");
  while( SIM900.available() > 0) Serial.write(SIM900.read());    // Clean the input buffer
  delay(500);
  SIM900.println("Host: api.thingspeak.com");    // Send the AT command
  //Serial.println("Host: api.thingspeak.com");
  while( SIM900.available() > 0) Serial.write(SIM900.read());    // Clean the input buffer
  delay(500);
  SIM900.println("Connection: close");    // Send the AT command
  //Serial.println("Connection: close");
  while( SIM900.available() > 0) Serial.write(SIM900.read());    // Clean the input buffer
  delay(500);
  SIM900.println("X-THINGSPEAKAPIKEY: J79IQXQ0EOM6NCZX");    // Send the AT command
  //Serial.println("X-THINGSPEAKAPIKEY: J79IQXQ0EOM6NCZX");
  while( SIM900.available() > 0) Serial.write(SIM900.read());    // Clean the input buffer
  delay(500);
  SIM900.println("Content-Type: application/x-www-form-urlencoded");    // Send the AT command
  //Serial.println("Content-Type: application/x-www-form-urlencoded");
  while( SIM900.available() > 0) Serial.write(SIM900.read());    // Clean the input buffer
  delay(500);
  SIM900.println("Content-Length:12");    // Send the AT command
  //Serial.println("Content-Length:12");
  while( SIM900.available() > 0) Serial.write(SIM900.read());    // Clean the input buffer
  delay(500);
  SIM900.println("");    // Send the AT command
  //Serial.println("");
  while( SIM900.available() > 0) Serial.write(SIM900.read());    // Clean the input buffer
  delay(500);
  SIM900.print("field1=");    // Send the AT command
  SIM900.println(data);
  while( SIM900.available() > 0) Serial.write(SIM900.read());    // Clean the input buffer
  delay(500);
  SIM900.println((char)26);
  delay(500);
  while( SIM900.available() > 0) Serial.write(SIM900.read());    // Clean the input buffer
  delay(500);

  answer = 0;
  return answer;
}


Complete Code:

GPRS ThingSpeak v0.1 Beta

Useful Links:

Seeeduino Stalker v3 Wiki:
http://www.seeedstudio.com/wiki/Seeeduino-Stalker_v3

Original GPRSBee Site:
 http://old.gprsbee.com/

GSM Particulate Sensor - Thanks alcomposer!!! This code helped me a lot and some of my code is base on this.
https://github.com/alcomposer/GSM-Particulate-Meter/blob/master/ParticulateSensorSIM900.ino

SIM900 Thingspeak Code - Thanks maliki!!! This code was also key in helping me get all of this together.
https://codebender.cc/sketch:267851#sim900%20thingspeak.ino

Misc Others:
http://community.thingspeak.com/forum/thingspeak-api/http-post-using-gsm-module/
https://www.pubnub.com/blog/2015-02-17-connect-arduino-gsm-gprs-shield-to-the-internet/
https://community.particle.io/t/uploading-sensor-data-to-thingspeak/5497
http://www.seeedstudio.com/wiki/Seeeduino_GPRS