vshymanskyy / TinyGSM

A small Arduino library for GSM modules, that just works
GNU Lesser General Public License v3.0
1.91k stars 708 forks source link

SIM7000 UDP functions with NTP example #710

Open star297 opened 1 year ago

star297 commented 1 year ago

What type of issues is this?

Temporary solution to no UDP support on tinyGSM.

NTP demo using UDP functions on a SIM7000 modem if anyone interested. I use this to overcome the unreliable modem built in NTP function. It's a hybrid of tinyGSM and external AT command functions. Works on 2G and CAT-M1, probably NB-IOT but not tested. NTP response typical within 1 second. SIM7000 is not very user friendly compared to SIM7600 so every UDP function needs different AT result testing. You can use the UDP functions for anything but watch the buffer limit as tinyGSM uses multiple connections. So the data is buffered (1460 bytes max) per send/receive rather that direct output to the UART. I do use this with simultaneous multiple connections with MQTT without issues. Don't forget the CAT-M1 data rate is very slooooow.

What are you working with?

Modem: SIM7000 (PCI-e module), but any version will do Main processor board: ESP32 TinyGSM version: current

Code:

#include <Arduino.h>
#define TINY_GSM_MODEM_SIM7000
#include <TinyGsmClient.h>

const char gprsUser[] =   "";     //  Usser name
const char gprsPass[] =   "";     //  password
char SIMapn[8]        =   "em";   //  default set the APN from the SIM card supplier (emnify)

const char ntpURL[]   = "time.nist.gov";
//const char ntpURL[]   = "pool.ntp.org";
const char ntpPort[]  = "123";
int connection_id     = 3;        //  connection ID 0.. 7

#define PCIeMODEM_TX  27          //  to gsm module RX connection
#define PCIeMODEM_RX  26          //  to gsm module TX connection
#define ESPled        12          //  ESP led
#define GSMpower      25          //  GSM dc converter control, High on, Low off

#define debug          0          //  enable debug message output

TinyGsm modem(Serial1);           // using Serial 1 port for modem

#define LTEM           1          //  0 = 2G, 1 LTE-CAT-m1

String cRes;
int iRes;

/*************** UDP functions ***************/
int UDPstart(int connection, const char* url, const char* port);
int UDPsend(int connection, char* data, int datalen);
int UDPrecv(int connection, char * recvBuffer ,int datalen);
int UDPclose(int connection);

void setup()
{
  // Modem power control
  pinMode(GSMpower, OUTPUT);    // PCI-e modem power control
  digitalWrite(GSMpower, LOW);  // PCI-e power OFF
  // Initialize LED
  pinMode(ESPled, OUTPUT);
  digitalWrite(ESPled, LOW);
  delay(1000);
  digitalWrite(GSMpower, HIGH);  // PCI-e modem power control ON

  // initialise serial debug port
  Serial.begin(115200);
  delay(10);
  Serial.printf("\nStarting... \n");

   // initialise modem serial port, start at default 115200
  Serial1.begin(115200, SERIAL_8N1, PCIeMODEM_RX, PCIeMODEM_TX, false);

  delay(10000);  //  wait for the modem to initialise
  Serial.printf("   +++++ SIM7000 NTP +++++\n");
  Serial.printf("Initialise modem... ");
  if (!modem.init()) {
    Serial.printf("fail!!\nCan't connect to Modem or SIM not registered\n");
    //while(1);       // stop    
    ESP.restart();  // restart esp32
  }
  Serial.printf("ok\n");
  Serial.printf("Starting network connection\n");

  if (LTEM){
    iRes = modem.setNetworkMode(38); //  2-Automatic, 13-GSM(2G), 38-LTE(CAT-M1),
    Serial.printf("Set network MODE to LTE CAT-M1: %s\n", iRes ? "ok" : "fail"); 
    delay(50);
    modem.sendAT(GF("+CMNB=1")); // 1 - CAT-M, 2 - NB-IOT, 3 - auto NB-IOT and CAT-M
    if (modem.waitResponse(1000L)) {
      Serial.printf("Set band to CAT-M1: ok\n");
    }else{
      Serial.printf("Set band to CAT-M1: fail!\n");
    }
  }else{
    iRes = modem.setNetworkMode(13); //  2-Automatic, 13-GSM(2G), 38-LTE(4G CAT-M1),
    Serial.printf("Set network MODE GSM(2G): %s\n", iRes ? "ok" : "fail");
  }
  if (!modem.waitForNetwork(60000)) {   // wait upto 60 seconds to aquire network
    Serial.printf("Can't connect to network or sim not registered\n"); 
    ESP.restart();  
  }
  Serial.printf("Connected to network\n");
  if (!modem.waitForNetwork(20000)) {   // wait 20 seconds for network data connection
    Serial.printf("Can't connect to network or sim not registered\n");
    ESP.restart();
  }
  if (!modem.gprsConnect(SIMapn, gprsUser, gprsPass)) {
    Serial.printf("Can't connect to data\n");
    ESP.restart();
  }
  Serial.printf("GSM connected\n");

  int timeout = millis() + 2000;
  while (millis() < timeout) {   // print and clear buffer modem
    while (Serial1.available()) { 
      char c = Serial1.read();
      if (debug){Serial.printf("%c",c);}
    }
  }  
}

void loop()
{
  uint32_t NTP_PACKET_SIZE = 48;            // NTP packet size bytes
  uint32_t NTP_OFFSET = 2208988800;         // for UTC time
  char packetBuffer[NTP_PACKET_SIZE + 2];
  memset(packetBuffer, 0x00, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  packetBuffer[0]  = 0xE3;
  packetBuffer[1]  = 0;
  packetBuffer[2]  = 6;
  packetBuffer[3]  = 0xEC;
  packetBuffer[12] = 49;
  packetBuffer[13] = 0x4E;
  packetBuffer[14] = 49;
  packetBuffer[15] = 52;

  int send_len = 0;
  int recv_len = 0;

  Serial.printf("\nUDP connect to NTP server... "); 
  int conID = UDPstart(connection_id, ntpURL, ntpPort);  
  if (conID > -1){
    Serial.printf("ok on id %d\n", conID);
  }else{
    Serial.printf("fail!\n");
    // while(1);
    // return;
    ESP.restart();
  }

  send_len = UDPsend(connection_id, packetBuffer, NTP_PACKET_SIZE); 
  if (send_len == 48){
    Serial.printf("sent NTP packet\n");   
    recv_len = UDPrecv(connection_id, packetBuffer ,send_len);
    if (recv_len == 48){
      Serial.printf("received NTP response\n"); 
    }
  }

  if (UDPclose(connection_id)){
    Serial.printf("UDP connection %d closed\n", connection_id);
  }

  if(recv_len == 48){  
    uint32_t secsSince1970=((packetBuffer[40]<<24)|(packetBuffer[41]<<16)|(packetBuffer[42]<<8)|packetBuffer[43])-NTP_OFFSET;
    if (debug){Serial.printf("UTC seconds: %d\n", secsSince1970);}
    time_t UTCtime = (secsSince1970);
    char timebuff[100];
    strftime(timebuff,100,"UTC Time %H:%M:%S %a %d %b %Y", localtime(&UTCtime));
    Serial.println(timebuff);
    // get NTP again in 10 minutes or stop
    delay(60000);
    //while (1);
  }else{
    Serial.printf("NTP failed!\n");     
    // failed, try in 10 seconds or stop
    delay(10000);
    //while (1);
  }
}

/*++++++++++++++++++++ UDP functions ++++++++++++++++*/

/*--------------------- UDP connect ----------------*/
int UDPstart(int connection, const char* url, const char* port){
  char c = ' ';
  char ID =' ';
  int nchr = 0;
  int idx = 0;
  int end = 0;
  char result[32];
  memset(result, 0x00, sizeof(result));
  int timeout = millis() + 5000;      // wait up to 5 seconds to connect
  modem.sendAT("+CIPSTART=",connection,',',"\"UDP","\",\"",url,"\",",port);
  while (millis() < timeout && !end) {
    while (Serial1.available()) {
      c = Serial1.read();
      //Serial.printf("%c", c);
      nchr ++;
      if (nchr > 12 && c == 0x0D){  // end timeout at the last CR character
          end = 1;
        }
      if (nchr >= 9){
        if (nchr == 9){   // connection id is char 9, don't realy need this
          ID = c;
        }
        if (nchr >= 12){    // response start is always char 12 if no error's
          result[idx]= c;
          idx++;
        }
      }
    }
  }
  if (end){
    if (debug){Serial.printf("result: %s\n", result);}
    if(strcmp(result,"CONNECT OK")){
      return ID - 48;;
    }
    if(strcmp(result,"ALREADY CONNECT")){
      return ID - 48;;
    }
    if(strcmp(result,"CONNECT FAIL")){
      return -1;
    }
  }
  // anything else is error
  return -1;
}

/*--------------------- UDP send ----------------*/
int UDPsend(int connection, char* data, int datalen){
  int gotid = 0;
  int sent_len = 0;
  int idx = 0;
  int end = 0;
  int ready = 0;
  char c = ' ';
  char ID =' ';
  char data_len[32];    
  memset(data_len, 0x00, sizeof(data_len));
  int timeout = millis() + 500;
  modem.sendAT("+CIPSEND=",connection,',', datalen);
  while (millis() < timeout && c != '>') {  // wait up to 500ms for start of data packet request
    while (Serial1.available()) {
      c = Serial1.read();  
    }
  }
  if (c != '>'){ 
    return 0;
  }  
  for (int i = 0; i < datalen; i++){ // send data
    Serial1.write(data[i]);
  }   
  timeout = millis() + 500;
  while (millis() < timeout && !end) {   // wait up to 500ms for "DATA ACCEPT" confirmation
    while (Serial1.available()) {
      c = Serial1.read(); 
      //Serial.printf("%c", c);     
      if (ready){
        if (c == 0x0D || c == 0X0A){    // stop at CR/LF characters
          end = 1;
        }
        if(gotid && c != ','){           // ignor comma           
          data_len[idx] = c;
          idx++;            
        }
        if(!gotid){
          ID = c;
          gotid = 1;
        }
      }
      if (ready == 0 && c == ':'){    // Skip the "DATA ACCEPT:"
        ready = 1;
      }
    }
  }
  if (gotid) {
    sent_len = atoi(data_len);
    if (debug) {Serial.printf("sent %d bytes on connection: %c\n", sent_len, ID);}
    return sent_len;
  }
  return 0;
}

/*--------------------- UDP receive ----------------*/
int UDPrecv(int connection, char * recvBuffer ,int datalen)
{  
  int idx = 0;
  int end = 0;
  int ready = 0;
  int skip = 0;
  char c = ' ';
  char ID =' ';
  int id = -1;
  char incoming = '0';
  char data_rem[8];
  char data_len[8];
  char mode = ' ';   
  int recv_len = 0;      
  int gotid = 0;   
  int gotmode = 0;
  int gotdata = 0;
  int gotdata_rem = 0;  
  memset(data_rem, 0x00, sizeof(data_rem));
  memset(data_len, 0x00, sizeof(data_len));
  int timeout = millis() + 500;
  while (millis() < timeout && !end) {     // wait up to 500ms for incoming data confirmation
    while (Serial1.available()) {
      c = Serial1.read();
      //Serial.printf("%c", c);
      if (ready){         
        if(incoming == '1' && c != ','){    // ignor the comma           
          ID = c;
          id = ID - 48;
          end = 1;                       
        }
        if(incoming == '0'){
          incoming = c;
        }
      }
      if (ready == 0){     // Skip the "+CIPRXGET: " part
        if (c == ':'){
          skip = 1;
        }
        if (skip && c == 0X20){
          ready = 1;
        }
      }
    }
  }  
  if (incoming == '1' && id == connection) {
    if (debug) {Serial.printf("data in on connection: %d\n", id);}
    ID =' ';
    idx = 0;
    ready = 0;
    skip = 0;
    modem.sendAT(GF("+CIPRXGET=2,"), connection, ',',datalen);
    timeout = millis() + 500;
    while (millis() < timeout) {      // wait 500ms for receive confirmation
      while (Serial1.available()) {
        c = Serial1.read();
        //Serial.printf("%c", c);
        if (ready){
          if(gotdata_rem && idx < datalen){
            recvBuffer[idx] = c;
            idx ++;
          }else{
            if(gotdata && !gotdata_rem && c != ','){                  
              if(c == 0X0A){                        
                gotdata_rem = 1;
                idx = 0;
              }else{
                data_rem[idx] = c;
                idx++;
              }                            
            }
            if(gotid && !gotdata){
              if (c == ','){
                recv_len = atoi(data_len);
                gotdata = 1;
                idx = 0;                                                      
              }else{
                data_len[idx] = c;
                idx++;
              }                           
            }
            if(gotmode && !gotid){                 
              if (c == ','){
                gotid = 1;
              }else{                  
                ID = c;
              }
            }
            if(!gotmode){ 
              if (c == ','){             
                gotmode = 1;
              }else{
                mode = c;
              }
            }
          }
        }
        if (ready == 0){      // Skip the "+CIPRXGET: " part
          if (c == ':'){
            skip = 1;
          }
          if (skip && c == 0X20){
            ready = 1;
          }
        }
        timeout = millis() + 20;    // serial data in, reduce timeout
      }
    }     
    if (recv_len == datalen) {
      if (debug){
        Serial.printf("mode:  %c\n", mode);
        Serial.printf("connection_id: %c\n", ID);
        Serial.printf("data receive:   %s\n", data_len);
        Serial.printf("data remaining: %s\n", data_rem);
      }
      return (recv_len);
    }    
  }  
  return 0;    
}

/*--------------------- UDP close connection ----------------*/
int UDPclose(int connection){
  modem.sendAT("+CIPCLOSE=", connection, ',', 1);
  if (modem.waitResponse(5000l, cRes)) {
    //Serial.println(cRes);
    return 1;
  }
  return 0;
}