arduino-libraries / MKRGSM

GNU Lesser General Public License v2.1
55 stars 51 forks source link

communicating repeatedly over a persistent connection #30

Open holomorph opened 6 years ago

holomorph commented 6 years ago

I tried writing a program structured like so

GSMSSLClient client;

void setup() {
  /* ... */
  client.connect(host, 443);
  /* ... */
}

void loop() {
  unsigned long now;
  int sensor;

  now = millis();
  if (now - then >= period) {
    then = now;

    sensor = analogRead(A1);
    snprintf(json, sizeof(json), JSON_FMT, sensor);
    snprintf(req, sizeof(req), REQ_FMT, path, host, 443, strlen(json), json);

    client.print(req);
  }
}

However, it appeared loop() would only run a small number of times before the program halted. I also had code for checking/re-establishing a connection before client.print(req), so a dropped connection was not the problem. I don't know how to introspect the program, so my best guess is an issue with memory. I ended up working around this by keeping the GSMSSLCLient declaration local within loop(), as below

void loop() {
  unsigned long now;
  int sensor;

  now = millis();
  if (now - then >= period) {
    then = now;

    sensor = analogRead(A1);
    snprintf(json, sizeof(json), JSON_FMT, sensor);
    snprintf(req, sizeof(req), REQ_FMT, path, host, 443, strlen(json), json);

    GSMSSLClient client;
    client.connect(host, 443);
    client.print(req);
    client.stop();
  }
}

but this isn't desirable because I'd like the period to be less than the time it takes to connect. I realize I can work around this further by keeping collected data in an array and just fire off several HTTP requests each loop, but I can't help but wondering if I'm doing something wrong in the former snippet.

FrancMunoz commented 6 years ago

Hi! Maybe I could help you... So:

Do you read incoming data from SARA? I guess incoming buffer gets full and SARA stops Arduino in an infinite loop.

Also I think your first code is better but I would improve with the following:

If you put all in the main loop I think it will become more stable.

Hope it helps!

holomorph commented 6 years ago

Thanks, GSM and GPRS status gets checked in setup() which seems fine, but using the first code I inserted the following into loop() before if (now - then >= period) { to count bytes read/look at the response:

  while (client.available()) {
    char c = client.read();
    // Serial.print(c);
    bytes_read++;
  }

Lots of useless headers in the response, 849 bytes! Did some experimenting and found that it takes needing period to be in excess of 3000ms for the entire response to be read before another HTTP request is sent, which is unfortunate because I'd like it to be 1000ms. Perhaps the server can be configured to send less junk back.

Does client.flush() do anything? Normally I'd expect such a function to read() until there's nothing left (one might think the snippet above should achieve that), but I see the function body is empty.

FrancMunoz commented 6 years ago

Hi! yes GSM and GPRS are checked in setup() but their state can change... even the socket could get closed or whatever... so i think I has to be checked.

About response... a better approach is read from client using a buffer instead of char by char. About the headers, in my case opted to make my own socket server, it's more complicated but thus there's no need to send/receive all http junk only required bytes then all becomes faster. Instead, you could try to remove headers from server maybe the safer option. All in all if you use buffering reading data from socket it will become considerably faster.

A buffer example taken from #12 (it requieres a timeout in while):

...
while(client.available()>0) {
                // Read max buffer data Leaving last byte to convert the buffer to String NULLing last byte.
                avail=client.read(&buffer[0], 255); 

                // Limit received data to 1024 bytes this will avoid flooding and collapsing arduino. 
                // Enough for HTTP header and command or data but limited.
                // It can be removed safely.
                while(avail>0 && received<1024) {
                   received+=avail;
                    buffer[avail]=0; // Set last char to NULL to terminate String and concat.
                    data.concat(String((char *)buffer));
                    avail=client.read(&buffer[0], 255);
                }
            }.
...

Nope, client.flush() doesn't do anything... in the source:

void GSMClient::flush()
{
}

is empty.

holomorph commented 6 years ago

Hum, ok. I guess the way to check these is gsm.isAccessAlive() and gprs.status()? I'm finding that the online documentation and even the header documentation are not what I'd expect, and there is no use of these in any of the examples. Checking the GSMClient status is documented, though.

Thanks for the example for reading chunks into a buffer, that makes an enormous difference.

FrancMunoz commented 6 years ago

Hi! Yes I check gsm.isAccessAlive(), gprs.status() and also GSMClient::connected(). No there's any exemple abou that... i fact I'm explaining my own experience.

Right now I'm facing a problem with socket: it connects, but is unable to send data and gprs.status is connected and gsm.isAccessAlive returns true. This is a GRPS problem it's solved reattaching... but there's no way to determine the cause and it happens between two and three days. (so it's hard to debug).

When I get an stable version will try to make an example.

holomorph commented 6 years ago

Here, if I include the check

  if (!gsm.isAccessAlive())
    Serial.println(F("gsm access is down!"));

before doing anything else in loop () as you said, the sketch goes for a few minutes, then the message gets triggered and everything stops. The past few days the code has been running nonstop without any GSM or GPRS checks in loop (), and the server has been getting the data.

Rocketct commented 6 years ago

Hi @holomorph could you share with us the log and a sketch for reproduce your issue?

holomorph commented 6 years ago

Example sketch:

#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <MKRGSM.h>

#define PIN "123456"
#define GPRS_APN "foobar"
#define GPRS_LOGIN "user"
#define GPRS_PASSWORD "secret"

#define REQ_FMT                         \
  "POST %s HTTP/1.1\r\n"                \
  "Host: %s:%d\r\n"                     \
  "X-Session-Id: xxxxxx"                \
  "Content-Type: application/json\r\n"  \
  "Content-Length: %d\r\n"              \
  "\r\n%s"

#define JSON_FMT                        \
  "{\"uid\":\"abcdef\","                \
  "\"date\":\"%s\","                    \
  "\"hp\":%d,"                          \
  "\"vibx\":%d,"                        \
  "\"viby\":%d,"                        \
  "\"pres\":%d,"                        \
  "\"temp\":%d,"                        \
  "\"session\":\"xxxx\"}"

GSMSSLClient client;
GPRS gprs;
GSM gsm;

static struct tm *timtm;
static uint8_t res[256];
static char buf[1024];
static char json[128];
static char date[128];
static const char *host = "hostname";
static const char *path = "/path/to/resource";

unsigned long then = 0;
const unsigned long period = 1000; // ms

void setup() {
  boolean connected = false;

  Serial.begin(9600);
  while (!connected) {
    if ((gsm.begin(PIN) == GSM_READY) &&
        (gprs.attachGPRS(GPRS_APN, GPRS_LOGIN, GPRS_PASSWORD) == GPRS_READY)) {
      connected = true;
    } else {
      Serial.println(F("not connected"));
      delay(1000);
    }
  }

  Serial.print(F("connecting to "));
  Serial.print(host);
  Serial.print(F("..."));
  if (client.connect(host, 443))
    Serial.println(F("done"));
  else
    Serial.println(F("failed"));
}

void loop() {
  unsigned long now;
  int h, vy, vx, p, t;
  time_t tim;

  if (!gsm.isAccessAlive())
    Serial.println(F("gsm access is down!"));
  if (!(gprs.status() == GPRS_READY))
    Serial.println(F("gprs status is not ready!"));

  while (!client.connected()) {
    Serial.print(F("connection lost.  reconnecting..."));
    if (client.connect(host, 443))
      Serial.println(F("done"));
    else {
      Serial.println(F("failed"));
      delay(1000);
    }
  }

  while (client.available()) {
    client.read(&res[0], 255);
  }

  now = millis();
  if (now - then >= period) {
    then = now;
    tim = gsm.getTime();

    if (!(timtm = gmtime(&tim))) {
      perror("gmtime");
      exit(EXIT_FAILURE);
    }
    if (!strftime(date, sizeof(date) - 1, "%D %T", timtm)) {
      perror("strftime");
      exit(EXIT_FAILURE);
    }

    h = analogRead(A1);
    vy = 0; //analogRead(A2);
    vx = analogRead(A3);
    p = 0; //analogRead(A4);
    t = analogRead(A5);

    snprintf(json, sizeof(json), JSON_FMT, date, h, vx, vy, p, t);
    snprintf(buf, sizeof(buf), REQ_FMT, path, host, 443, strlen(json), json);

    client.print(buf);
  }
}
FrancMunoz commented 6 years ago

Hi!

I'm sorry for my delay. Well, here I'll post my experience, all i've done to make it work and finally an sketch based on the one that has been working 4 weeks, today stills running, without any reset neither manual nor watchdogged.

I think the code should be improved, like it's said in #27.

I've to say that in my own desperation, I programmed a complete socket server in .NET to connect and control multiple MKR's, That reduced a lot the amount of data to be send/received and all becomes super fast... about 0.3 seconds to communicate... Acting over a relay is like be there and pushing a button. So I don't use a web server, but I guess all I explain here would help anyone uses it.

Introduction

First of all detected that documentation says there should be 20ms between receiving data from SARA and send any to it... so modified ModemClass::send in Modem.cpp:

void ModemClass::send(const char* command)
{
  if (_lowPowerMode) {
    digitalWrite(_dtrPin, LOW);
    delay(5);
  }
  unsigned long dif=millis()-_uartMillis;
  if(dif<20) delay(20-dif);

  _uart->println(command);
  _uart->flush();
  _atCommandState = AT_COMMAND_IDLE;
  _ready = 0;
}

and ModemClass::poll:

void ModemClass::poll()
{
  while (_uart->available()) {
    char c = _uart->read();
    _uartMillis=millis();

    if (_debug) {
      Serial.write(c);
    }

Also downgraded modem speed to 57600. I did this because it becomes more stable. Didn't tried to upgrade again but someday when got time...

Also downloaded the latests SAMD core from arduino even hourly updates.

Here's is the modified library. MKRGSM.zip

Then I tried to improve the code making putting a timeout in all the loops to avoid these infinite and damned loops. Unfortunately this didn't solved the problem: then when a couple of infinite loops should occur SARA came crazy. So, the arduino didn't freezed but SARA wasn't able to do nothing, so... another dead-end-street.

After that, realized that it's SARA that makes arduino fall into the infinite loops not responding when it's supossed to do. Reading SARA docs found that SARA needs its time to update its status and do whatever it has to do... I guess that's why reducing modem speed all seemed to worked at least better.

And changed all my sketch:

  1. Preparing loop to reconnect socket, GPRS and GSM if needed. This make the arduino work for a week without any interruption.
  2. Reading all the available data in SARA even if I don't need it.
  3. Letting SARA breath when something is going wrong (that is... just adding a delay when cant send data)... that did the trick... SARA just need time to update GSM or GPRS status in case of disconnection. So I cant disconnect the antenna, loose signal, stop the server, etc and it don't hangs.

Also it's very important to have the right power on Arduino if not SARA will hang it in an infinite loop.

The light...

I don't know if I'm right or wrong, but it's working. Here is a sample sketch based on the one it's working. It has some limitations about data received/sent because I don't need too much data but It's easy to adapt.

  #include <MKRGSM.h>

  #define PINGSM "1234"
  #define REMOTEHOST "000.000.000.000" 
  #define REMOTEPORT 12345

  #define GPRSAPN   "movistar.es" 
  #define GPRSLOGIN "movistar"
  #define GPRSPASS  "movistar"

  int timerHeartBeat=0;
  int timerToken=0;
  int timerCurrent=0;
  bool GSMConnected=false;
  bool socketConnected=false;
  bool isDebug=false;
  bool isLowPower=false;

  String IMEI;

  GPRS gprs;
  GSM gsm(isDebug); 
  GSMModem modem;
  GSMClient remoto;
  IPAddress LocalIP;

  void getIMEI() {
      IMEI.reserve(15);
      MODEM.send("AT+CGSN");
      MODEM.waitForResponse(100, &IMEI);
      Serial.print("IMEI: ");
      Serial.println(IMEI);
  }

  bool sendMessage(const String& msg) {
    bool retorno = false;
    int escrito = 0;
    byte buf[256]; 
    int len;

    len=msg.length();
    if(len>256) len=256;

    msg.getBytes(buf, len+1);
    escrito=remoto.write((uint8_t *)buf, len);
    if(escrito==len) {
      retorno = true;  
    } else {
      Serial.print("Write Error: ");
      Serial.print(escrito);
      Serial.print(" of ");
      Serial.println(len);
      // Check ERROR with +USOCTL=x, y where x is socket number and y is action:
      // with AT+USOCTL=socket, 1 we get error detail
      // with AT+USOCTL=socket, 4 we get remote ip and port
      // with AT+USOCTL=socket, 11 we get number of bytes not received remotelly
      // have we to disconnect GSM?
    }
    return retorno;
  }

  bool isConnected() {
    return (socketConnected && remoto!=NULL);
  }

  void disconnected() {
    if(socketConnected) {
      remoto.stop();
      Serial.println("Socket disconnected.");
    }
    socketConnected=false;
  }

  void connectGSM() {
      int intentos=0;

      // When fails sengind a AT+UPSDA=0,3 it works again.

      GSMConnected=false;
      while (!GSMConnected && intentos<10) {
          Serial.write("Inicializando GSM\n");
          if (gsm.begin(PINGSM)==GSM_READY) {
              Serial.write("Inicializando GPRS\n");
              if(gprs.attachGPRS(GPRSAPN, GPRSLOGIN, GPRSPASS)==GPRS_READY) {
                  GSMConnected = true;
              }
          } else {
              Serial.println("Not connected retrying...");
              delay(1000); // Let's Sara Breath...
          }
          intentos++;
      }
      if(!GSMConnected) {
          Serial.println("Can't initialize, retrying.");
          delay(3000); // Let's SARA breath.
          // Reboot??
          //doReset();
      } else {
          getIMEI();
          LocalIP = gprs.getIPAddress();
          Serial.print("IP Address: ");
          Serial.println(LocalIP);    
      }    
  }

  void connectRemote() {
    if(!isConnected()) {
      Serial.println("Connecting Remote Socket");
      if(remoto.connect(REMOTEHOST, REMOTEPORT)==1) {
        Serial.println("Socket Connected.");
        socketConnected=true;
      } else {
        Serial.println("Error connecting socket.");
        delay(1000); // Let's SARA breath... 
        MODEM.noop(); // Send AT
        delay(1000); // And let's SARA breath again... it will update its internal state. That did the trick!
      }
    } else {
      Serial.println("Remote socket yet connected.");
    }
  }

  void checkAvailable() {
    int avail;
    int recibido=0;
    bool hay=false;
    uint8_t buffer[256];
    int len;
    unsigned long start = millis();
    String cadena="";

    if(remoto.connected()) { 
      avail=remoto.available();
      while(avail>0) {
        if(millis()-start>10000) break;

        hay=true;
        if(avail>255) avail=255;
        avail=remoto.read(&buffer[0], avail);
        recibido+=avail;
        buffer[avail]=0;
        cadena.concat(String((char *)buffer));
        avail=remoto.available();
      }

      if(hay) {
        len=cadena.length();
        if(len>256) cadena=cadena.substring(len-256); // Only need first 256 chars
        Serial.print("Received: ");
        Serial.println(cadena);
        //parseReceived(); // Parse received data.
      }
    } else {
      disconnected();
    }
  }

  void enterLowPower() {
      Serial.println("Enable low power");
      MODEM.lowPowerMode();
      isLowPower=true;
  }

  void exitLowPower() {
      Serial.println("Exit low power");
      MODEM.noLowPowerMode();
      isLowPower=false;
  }

  void sendHeartbeat() {
    String msg="";
    int currentMillis=millis();

    if(remoto.connected()) {      
        Serial.println("Send Heartbeat");
        msg.concat("BOM HELLO WORLD EOM");

      if(msg.length()>0) {
        Serial.print("Message: ");
        Serial.println(msg);
        if(sendMessage(msg)) {
          Serial.println("Communication OK");
        } else {
          Serial.println("Error Writing Data...");
          disconnected();
        }
      }
    } else {
      disconnected();
    }
  }

  void doReset() {
      //NVIC_SystemReset(); // Removed to reduce requirements.
      while (true);
  }

  void setup() {
    Serial.begin(115200);
    delay(1000);
    Serial.println("Persistent Client v0.67");
    connectGSM();
  }

  void loop() {
    int currentMillis=millis();
    char c;

    // Send # or $ though console to enable/disable debug.
    while(Serial.available()) {
      c=Serial.read();
        if(c=='#') {
          if(isDebug) {
            isDebug=false;
            MODEM.noDebug();
            Serial.println("Disable DEBUG");
          }
        } else if(c!='\n' && c!='\r') {
          if(!isDebug) {
            isDebug=true;
            MODEM.debug();
            Serial.println("Enable DEBUG");
          }
        }
      //MODEM.write(c);
    }

    if(isConnected()) {
      checkAvailable();
      if((currentMillis-timerHeartBeat)>60000 || timerHeartBeat==0) {
          // Send every minute.
          sendHeartbeat();
          timerHeartBeat=currentMillis;
      }
    } else {
      if((currentMillis-timerHeartBeat)>5000) {
        // No connection try every 5 seconds. Let's SARA breath again.
        if(gsm.isAccessAlive()) {
          if(gprs.status()==GPRS_READY) {
            connectRemote();
            timerHeartBeat=currentMillis;
          } else {
              // Disconnected... 
              Serial.println("Connecting GPRS...");
              if(gprs.attachGPRS(GPRSAPN, GPRSLOGIN, GPRSPASS)==GPRS_READY) {
                connectRemote();
              }
          }
        } else {
          connectGSM();
        }
      }
    }
  }

Hope it helps!

eduardcabanas commented 6 years ago

Hi Franc,

I have an issue with the library and the MKR GSM 1400. I'm using the following code to check if the connection has been done correctly, but looking forward to possible problems (in this case a broken or unscrewed antenna and the impossibility of connecting to the network) the code in question loops and never ends and obviously ends up in an endless loop. ` use_proxy = false;

// start GSM shield // if your SIM has PIN, pass it as a parameter of begin() in quotes Serial.print("Connecting GSM network..."); if (gsmAccess.begin(PINNUMBER) != GSM_READY) { Serial.println(errortext); while (true); } Serial.println(oktext);` There is some way to make X attempts and then else because now else never gets to execute it, because the module is asking for the connection "in eternum".

Thank you very much because I'm very very stuck

Muchas gracias

Eduard

FrancMunoz commented 6 years ago

Hi @eduardcabanas !

As @Rocketct said in #36 you should check both GSM and GPRS connection before trying to do something, and take care about current drawn by your power source: without a good antenna SARA will require more current and if your power isn't good enough it will hangs. I think this should be sorted out: if SARA doesn't have enough power it should return an error or hang it self not leave MKR hanged. I'm working on it and will share when done.

For your code I'd encapsulate all in nested if sequence for connection and while for retry:

int i=0;
while(i<3) {
  i++;
  if (gsmAccess.begin(PINNUMBER) != GSM_READY) {
    if(gprsAccess.attachGPRS(GPRS_APN, GPRS_LOGIN, GPRS_PASSWORD) == GPRS_READY) {
      // Do something.
      break;
    } else {
      Serial.println("GPRS Error");
  } else {
    Serial.println("GSM Error");
  }
}
gsmAccess.shutdown();
// Sleep...

Hope it helps!

eduardcabanas commented 6 years ago

Hi @FrancMunoz

Thank you for your help, but the problem is not if it's connected, I'm installing this arduino in the middle of a field, and sometimes there we lose roaming due to lack of service or somebody hit the antenna, etc... Now I'm checking all the variants that could happen there, and the first simulation was disconnect the antenna simulating one of these situations that I commented before. That's the problem if you disconnect the antenna, the system enters an infinite loop because he could not connect to the tower. If this happens, the arduino will be dead forever. If I debug the connection, I saw infinite +CREG: 0,0 responses after AT+CREG? callings...

a) is it possible to "count" for example 5 +CREG: 0,0 responses and if this happens shutdown the arduino (I'm using lowpower to put arduino in sleep mode till nexts 15 minutes) b) there is any function in the library to check this +CREG: 0,0 response?

Thank you very very much for yor help

Eduard

FrancMunoz commented 6 years ago

Hi @eduardcabanas !

In my case after some +CREG responses SARA return an error, that's why theGSM.begin() and GPRS.attachGPRS() are functions: because both should return the state.

In your case, you should not loose MKR control (well, in fact no one wants that), you have two options here:

  1. Implement your own timeout in CREG modifyng GSM.cpp with a timer or counting number of times CREG is called, this is a non tested example:

GSM.h in private section of the class:

int _processMillis;

GSM.cpp

int GSM::ready()
{
  if (_state == ERROR) {
    return 2;
  }

  int ready = MODEM.ready();

  if (ready == 0) {
    return 0;
  }

// CHECK wether begin lasts more than 30 seconds.
  if(_readyState!=READY_STATE_CHECK_SIM && _readyState!=READY_STATE_DONE && millis()-_processMillis>30000) {
    _state = ERROR;
    ready = 2;
    return ready;
  }

  switch (_readyState) {
    case READY_STATE_CHECK_SIM: {
      _processMillis=millis(); // Set start time.
    ...

That should be implemented to in GRPS.cpp attaching GPRS in should last a lot too if bad signal from antenna when AT+UPSDA=0,3 is called. Just not lot loose control of MKF.

And the second option:

  1. Use the asynchonous call in GSM.begin() and GRPS.attachGPRS(). I'm just playing with it right now because I need MRK do some tasks while connecting or when connection is not available. I prefer this way but control logic is little bit complicated because you have to care the status all the time. As a result the modem only will block MKR when you need to read or send data. When I have it working will try to make PR whith the example. I would only use asynchronous function in GSM.begin() and GRPS.attachGPRS() the rest of calls would use the synced ones that are easier to manage.

In your case you could read sensors while MRK is connecting or just save records in an SD card as a datalogger does. So, when all is right you have data in server but when a failure happens you have the log.

Hope it helps!

eduardcabanas commented 6 years ago

Hi @FrancMunoz

Thank you for your help. I modified the library as you mentioned, I uploaded the sketch, disconnect the antenna but with the same result See the code and the library modified attached. Could you review what I di is correct or if it works in your? Many many thank's for your patience Regards from Barcelona

Eduard

sketch_aug07a.zip

FrancMunoz commented 6 years ago

Hi @eduardcabanas !

I made a MISTAKE!! %&·$!!** ... I apologize. That use to happens to me when I don't read what I write.

Please change:

int GSM::ready()
{
  if (_state == ERROR) {
    return 2;
  }

  int ready = MODEM.ready();

  if (ready == 0) {
    return 0;
  }

// CHECK wether begin lasts more than 30 seconds.
  if(_readyState!=READY_STATE_CHECK_SIM && _readyState!=READY_STATE_DONE && millis()-_processMillis>30000) {
    _state = ERROR;
    ready = 2;
    return ready;
  }

  switch (_readyState) {
    case READY_STATE_CHECK_SIM: {
      _processMillis=millis(); // Set start time.
    ...

I've updated the previous post... to avoid confusing people.

All in all, this is weird and should be written in a better way... Ah! ten seconds to connect aren't enough give from 30 seconds to a minute...

Hope it helps!

eduardcabanas commented 6 years ago

Hi @FrancMunoz

There is an error compiling:

error: 'readyState' was not declared in this scope

Thank's

FrancMunoz commented 6 years ago

Hi @eduardcabanas !

Oh.... there's an underscore before readyState... it has to be: _readyState. I updated boths posts above.

Hope this time is right. I apologize for the mistakes... didn't have enough time to test...

eduardcabanas commented 6 years ago

Hi @FrancMunoz

Now it works perfectly!! you are the one... 🥇 by the way, what should I write if I would like to do the same in gprs connection?

Thank you very much

Best

Eduard

FrancMunoz commented 6 years ago

Hi @eduardcabanas !

I'm happy to know it worked for you. For GPRS we could use the following:

Remember to create private property _processMillis as we did in GSM.

int GPRS::ready()
{
  int ready = MODEM.ready();

  if (ready == 0) {
    return 0;
  }

// CHECK wether begin lasts more than 30 seconds.
  if(_state!=GPRS_STATE_ATTACH && _state!=GPRS_STATE_IDLE && millis()-_processMillis>30000) {
    _state = GPRS_STATE_IDLE;
    _status = ERROR;
    return ready;
  }

  switch (_state) {
    case GPRS_STATE_IDLE:
    default: {
      break;
    }

    case GPRS_STATE_ATTACH: {
      _processMillis=millis(); // Set start time.
      MODEM.send("AT+CGATT=1");
...

This code, should work. Remember i'm unable to test. Also GPRS_STATE_ACTIVATE_IP sends AT+UPSDA=0,3 that can last a lot up to 3 minutes...

eduardcabanas commented 6 years ago

Hi @FrancMunoz

Thank you very much for your help, but I found a problem ;-) I'm using the following sketch to simulate the process powering on and off the led. `///////////////////////////////////////////////////////////////Libraries definition//////////////////////////////////////////////////////

include

include

// PIN Number const char PINNUMBER[] = ""; //blank if no pin // APN data: check settings on mobile for sim provider or contact carrier for network settings const char GPRS_APN[] = "internet.easym2m.eu"; const char GPRS_LOGIN[] = ""; const char GPRS_PASSWORD[] = "";

GSMClient client; GPRS gprsAccess; GSM gsmAccess;//(true);

// Various variables used in this Sketch String response = ""; String okmsg = "ok"; String errormsg = "error"; void setup() { delay(3000); pinMode(LED_BUILTIN, OUTPUT); LowPower.attachInterruptWakeup(RTC_ALARM_WAKEUP, dummy, CHANGE); // initialize serial communications and wait for port to open: Serial.begin(9600); } void loop() { digitalWrite(LED_BUILTIN, HIGH); int i = 0; Serial.println(i); while (i < 5) { i++; if (gsmAccess.begin(PINNUMBER) != GSM_READY) { Serial.println("GSM Error"); break; }

else {
  Serial.println("GSM OK");
  // Attach GPRS and check if it works
  if (Serial) Serial.println("Attaching to GPRS with your APN...");
  if (gprsAccess.attachGPRS(GPRS_APN, GPRS_LOGIN, GPRS_PASSWORD) != GPRS_READY) {
    if (Serial) Serial.println(errormsg);

    gsmAccess.shutdown();

  } else {
    // GPRS successfully attached
    if (Serial) Serial.println(okmsg);
  }
}

}

digitalWrite(LED_BUILTIN, LOW);

LowPower.sleep(20000);

} void dummy() { volatile int aaa = 0; }`

What's happen? the first cycle it work's, but the following not, the arduino only wakeup switch on the led for approx 2 seconds and goes to sleep

you know why???

Thank's Eduard

FrancMunoz commented 6 years ago

Hi @eduardcabanas !

I don't use ArduinoLowPower and can't help you right now: don't have enough time to learn about it.

I would like to help you but I'm only sharing my knowledge and experience and since I don't have it about this library, I mean: the impact in MKRGSM and what to do or its state when it awakes... can't really help you.

A litle thing I would do first is not shutdown modem instead I'd put in low power mode and see after awake. This changes the logic of your sketch but it can be arranged for testing.

Good Luck!

eduardcabanas commented 6 years ago

Hi @FrancMunoz

Thank's and sorry to be so bored...but the problem is the same when I put a delay and not a sleep.

the first cycle is ok, but next allways give error....I think the problem could be with the millis and _processtime that are not reset after cycle

Thank's

eduardcabanas commented 6 years ago

Hi @FrancMunoz At last, all works perfect, it was my fault, now is working with your library modifications and with low-power library

thank's again

Eduard

FrancMunoz commented 6 years ago

Hi @eduardcabanas ,

I'm happy to know you succeeded with your project, congratulations!

heligone commented 6 years ago

Hi @FrancMunoz and @eduardcabanas ,

Thank you for these valuable insights !

I am facing a freeze problem during th eestablishment of a gsm/GPRS connection.

cf. a thread at arduino forum https://forum.arduino.cc/index.php?topic=545464.0

I am now trying to replicate your tests : I modified gsm.h, gsm.cpp, gprs.h and gprs.cpp form mkrgsm lib accordingly.

But the very simple following test code copied from @eduardcabanas hangs at gsmAccess.shutdown();

did I do anything wrong ?

Thanks again !

mkrgsm modifications.zip

include

// PIN Number const char PINNUMBER[] = ""; //blank if no pin // APN data: check settings on mobile for sim provider or contact carrier for network settings const char GPRS_APN[] = "soracom.io"; const char GPRS_LOGIN[] = "sora"; const char GPRS_PASSWORD[] = "sora";

GSMClient client; GPRS gprsAccess; GSM gsmAccess;//(true); // Various variables used in this Sketch String response = ""; String okmsg = "ok"; String errormsg = "error";

void setup() { Serial.begin(9600); }

void loop() {

delay(3000); int i = 0; Serial.println(i); while (i < 5) { i++; if (gsmAccess.begin(PINNUMBER) != GSM_READY) { Serial.println("GSM Error");} // if (gprsAccess.attachGPRS(GPRS_APN, GPRS_LOGIN, GPRS_PASSWORD) == GPRS_READY) { // // Do something. // break; // } // else { // Serial.println("GPRS Error"); // } else { Serial.println("GSM OK"); }

gsmAccess.shutdown();
// Sleep...
Serial.println(gsmAccess.isAccessAlive());

delay(1000);

} }

eduardcabanas commented 6 years ago

Hi @holomorph Try to use my code (following) at the end of the loop you could choos to put the arduino in sleep mode or in delay mode

Let me know if it works

I hope it goes well, you have to change the APN and server address

Best

Eduard

`///////////////////////////////////////////////////////////////Libraries definition//////////////////////////////////////////////////////

include

include

include

include

// PIN Number const char PINNUMBER[] = ""; //blank if no pin // APN data: check settings on mobile for sim provider or contact carrier for network settings const char GPRS_APN[] = "**.com"; const char GPRS_LOGIN[] = ""; const char GPRS_PASSWORD[] = "";

// get this from the wia dashboard. it should start with d_sk const char* device_secret_key = "01234567890";

GSMClient client; GPRS gprsAccess; GSM gsmAccess;

// Thebluedots API parameters char server[] = "XXXXXX.com"; char path[] = "/api/v2/gprs"; int port = 80;

HttpClient httpClient = HttpClient(client, server, port); String response; int statusCode = 0; String dataStr; String okmsg = "ok"; String errormsg = "error"; void setup() { delay(3000); pinMode(LED_BUILTIN, OUTPUT); LowPower.attachInterruptWakeup(RTC_ALARM_WAKEUP, dummy, CHANGE); // initialize serial communications and wait for port to open: Serial.begin(9600); } void loop() {

digitalWrite(LED_BUILTIN, HIGH);

if (gsmAccess.begin(PINNUMBER) != GSM_READY) { Serial.println("GSM Error"); }

else { Serial.println("GSM OK"); // Attach GPRS and check if it works if (Serial) Serial.print("Attaching to GPRS with your APN..."); if (gprsAccess.attachGPRS(GPRS_APN, GPRS_LOGIN, GPRS_PASSWORD) != GPRS_READY) { if (Serial) Serial.println(errormsg); gsmAccess.shutdown();

} else {
  // GPRS successfully attached
  if (Serial) Serial.println(okmsg);
  float shttemp = 0;

  StaticJsonBuffer<300> jsonBuffer;
  JsonObject& root = jsonBuffer.createObject();
  root["dot"] = "gsm001";
  JsonObject& data = root.createNestedObject("data");
  data.set("5", shttemp);

  // Generate the JSON string to Serial
  root.printTo(Serial);
  postToWia(root);
  // read the status code and body of the response
  statusCode = httpClient.responseStatusCode();
  response = httpClient.responseBody();
  Serial.print("Status code: ");
  Serial.println(statusCode);
  Serial.print("Response: ");
  Serial.println(response);
}

} digitalWrite(LED_BUILTIN, LOW); // delay(300000); LowPower.sleep(15 * 60000);

} void dummy() { volatile int aaa = 0; } void postToWia(JsonObject& data) { dataStr = ""; data.printTo(dataStr); httpClient.beginRequest(); httpClient.post(path); httpClient.sendHeader("Content-Type", "application/json"); httpClient.sendHeader("Content-Length", data.measureLength()); httpClient.sendHeader("Authorization", "Bearer " + String(device_secret_key)); httpClient.beginBody(); httpClient.print(dataStr); httpClient.endRequest();

}`

heligone commented 6 years ago

Hi @eduardcabanas

Thanks for this sketch.

It seems that this is working if I use the delay option, and not working if I use the sleep option . But this is not an issue for me.

I need now to test if for a project that require 6 months uninterruptible service with periodic reconnexion to GSM network, with a solar panel power supply AND an attached battery

I have encountered 5 freezes during my last 2 month testing campaign and i fear that those freeze are caused by battery that cannot furnish enough current at GSM reconnection, even though my battery never went below 4V

I have noticed during some tests at the workshop that even a with battery 18650 ,1S3P 3.7V 7800mAh showing good voltage >4V , the problem can occur for example if the battery was not charged during several days (But having voltage >4V anyway) . The problem never occurs with new batteries, or batteries that were recently charged with a genuine Charger

I dont know if :

See also my question here : cf. https://forum.arduino.cc/index.php?topic=565177.0

NOTE : I have tried the asynchronous implementation of francmunoz https://github.com/FrancMunoz/MKRGSM/tree/master/src

It is interesting because in case of a problem with the SARA modem, the rest of the board is not frozen....but the modem still is ...

FrancMunoz commented 6 years ago

Hi!

I did some modifications to MKRGSM library using timeouts to avoid entering in infinite loops and worked fine but it was so difficult to recover modem. I'm still investigating about that.

Also, I'm still working in the asynchronous implementation to be able to recover when signal is lost or wathever... my main is the loop should never be affected by modem status (like a mobile when looses connectivity). When I got news will post or update PR.

All in all @heligone , you have to be careful with current also, not only voltage... the modem (through MKR) can require up to 150mA if it has bad coverage or defective antenna or no antenna. (I've measured 180mA during few milliseconds with sensors). So, my board without antenna connects GPRS and works (I've very good coverage) but requires more power.

So, If source can't draw enough current for modem it does not reply to AT commands and MKR fall into an infinite loop (the fist part above). Now I'm playing with that... hanging loops and modem recovery to achieve an stable-non-modem-dependent loop.

About your project, I used a 24mAh - 3.7V LiPo from a portable power bank and worked well... but the implementation is not the same as yours, but maybe can help.

Hope it helps.

heligone commented 6 years ago

Oh yes indeed you do help ! a lot :)

I have tested your MKRGSM modifications, where your put a 30sec timeout on the AT+CREG? : indeed, it avoids the infinite loop if you put the board in a faraday cage, remove the antenna or if there is no network available. This is great !

I have also tested your asyncrhonous version . It works fine too, and the main loop does not get affected whenever the connection routine enters a loop. But : the modem still is not accessible... A reboot would be required. i was thinking of 2 solutions :

I completely agree with you about the importance of the current that can be drawn from the battery. In the project I am considering, the mkrGSM1400 is powered by a solar panel , which powers the board and also recharges a battery, which is in also connected in parallel to the board to provide extra current during peaks at GSM connexion time. I Can only monitor its voltage .During a 2 month test run last months, I have been sending GPRS messages every 15 minutes . The board was reset every night to avoid memory leak problems. The battery never went under 4V. Yet the board froze 5 times ... at the times of reconnexion.

That is why I say (just like you) : the standard run of the board should not be affected by modem problems, whether because no network or because the battery cannot provide current for peak of consumption.

With the asynchronous mode : From what I saw in your code, the only piece lacking now with your asynchronous library is the modem recovery part :)

In synchronous mode (with your modifications in MKRGSM) : I dont know how to avoid a freeze in AT+CREG if this freeze happens before the 30 seconds time ou that you implemented. I suspect that this freeze is a hardware problem indeed an cannot be solved programmatically

Thanks again for our insights

sandeepmistry commented 6 years ago

@FrancMunoz do you think the latest changes in master and PR #52 make things more stable?

Josca commented 6 years ago

Hi, I want to use MKR 1400 to permanently send data to web server using GSMClient. I want to build real IoT device.

Data from sensor are send as param of GET request. It works well several minutes (even with battery) but I don't know how to recover from fail state (disconnection). Please provide clear and well described example for it as a part of this project (not only in this issue). Current just-one-time request example (GsmWebClient.ino) is useless. Nobody needs to do only one request and exit. Provide better examples to support people when creating real word projects.

@FrancMunoz, thanks for sketch but I don't like it. Methods aren't described, there are unused methods (enterLowPower, exitLowPower), confusing names (remoto, IMEI, retorno). Why you write doReset method as while (true);?

Sorry, I am angry a little bit because it's very hard to do basic IoT long-term running routine with this stuff.

eduardcabanas commented 6 years ago

Hi Josef

I was with your similiar situation and I was angry too, but this is not an end product, it’s a developing product and you have to test, investigate and test again.

The guys who develop things with these devices we spend a lot of months, mails and overall a lot of thank’s

I aim to you to keep pushing and if you don’t understand some words, use google translator that works in these cases

There are a lot of examples in arduino’s forum, there are a lot of examples that you could use, combine and test. I’m sure you will find the solution

Best

Eduard

El 12 oct 2018, a las 0:11, Josef Hák notifications@github.com escribió:

Hi, I want to use MKR 1400 to permanently send data to web server using GSMClient. I want to build real IoT device.

Data from sensor are send as param of GET request. It works well several minutes (even with battery) but I don't know how to recover from fail state (disconnection). Please provide clear and well described example for it as a part of this project (not only in this issue). Current just-one-time request example (GsmWebClient.ino) is useless. Nobody needs to do only one request and exit. Provide better examples to support people when creating real word projects.

@FrancMunoz, thanks for sketch but I don't like it. Methods aren't described, there are unused methods (enterLowPower, exitLowPower), confusing names (remoto, IMEI, retorno). Why you write doReset method as while (true);?

Sorry, I am angry a little bit because it's very hard to do basic IoT long-term running routine with this stuff.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.