Синхронизация времени для SIM800 (NTP)

При работе IoT устройств важно, чтобы время было синхронизировано с NTP серверами или сервером времени оператора сотовой связи. Если после загрузки SIM800 выполнить команду, она вернет дату и время по-умолчанию, вместо текущего.

AT+CCLK?
+CCLK: "04/01/01,00:00:40+12"
OK

Проверим автоматическую синхронизацию времени
AT+CLTS?
+CLTS: 0
OK

Активируем автоматическую синхронизацию времени командой AT+CLTS:

AT+CLTS=1
OK

Сохраним настройку в памяти для синхронизации времени при перезагрузке. 
AT&W 

Можно использовать короткую команду
AT+CLTS=1;&W
OK

AT+CLTS?
+CLTS: 1
OK

Перезагружаем модем командой AT+CFUN=1,1 (не всегда корректно отрабатывает) или AT+CFUN:

AT+CFUN=0
+SAPBR 1: DEACT
+PDP: DEACT
+CPIN: NOT READY
OK

AT+CFUN=1
+CPIN: READY
OK
SMS Ready
Call Ready
*PSUTTZ: 2019,5,25,18,46,18,"+12",0
DST: 0

Проверяем повторно время

AT+CCLK?
+CCLK: "19/05/25,21:50:06+12"
OK

Теперь время возвращается правильное. Оно возвращается в формате: «yy/mm/dd,hh:mm:ss+/-zz».

Синхронизация SIM800 с NTP Server-ом

Синхронизация SIM800 с NTP сервером у меня работает через раз. Причину не выяснил. Нередко получаю такую ошибку после запуска.

AT+CNTPCID=1
OK
AT+CNTP="pool.ntp.org",3
OK
AT+CNTP
OK
+CNTP: 62 - если команда с ошибкой
+CNTP: 1 - если команда отработала без ошибки

Команда AT+CNTP для синхронизации с NTP сервером возвращает 62 ошибку.

1 - Network time synchronization is successful
61 - Network error
62 - DNS resolution error
63 - Connection error
64 - Service response error
65 - Service response timeout

Код для библиотеки TinyGSM для синхронизации времени на SIM800 с NTP сервером. Мой GitHub pull request.

boolean isValidNumber(String str)
{
   boolean isNum = false;
   if(!(str.charAt(0) == '+' || str.charAt(0) == '-' || isDigit(str.charAt(0)))) return false;

   for(byte i=1;i < str.length(); i++)
   {
       if(!(isDigit(str.charAt(i)) || str.charAt(i) == '.')) return false;
   }
   return true;
}

String ShowNTPError(byte error)
{
  switch (error)
  {
    case 1:
      return "Network time synchronization is successful";
    case 61:
      return "Network error";
    case 62:
      return "DNS resolution error";
    case 63:
      return "Connection error";
    case 64:
      return "Service response error";
    case 65:
      return "Service response timeout";
    default:
      return "Unknown error";
  }
}

byte NTPServerSync(String server = "pool.ntp.org", byte TimeZone = 3)
{
  Serial.println("Sync time with NTP server.");
  sendAT(GF("+CNTPCID=1"));
  if (waitResponse(10000L) != 1) 
  {
      Serial.println("AT command \"AT+CNTPCID=1\" executing fault.");
  }

  String CNTP = "+CNTP=" + server + "," + String(TimeZone);   
  sendAT(GF(CNTP));
  if (waitResponse(10000L) != 1) 
  {
      Serial.println("AT command \"" + CNTP + "\" executing fault.");
  }

  sendAT(GF("+CNTP"));
  if (waitResponse(10000L, GF(GSM_NL "+CNTP:")))
  {
      String result = stream.readStringUntil('\n');
      result.trim();
      if (isValidNumber(result))
      {
        return result.toInt();
      }
  }
  else
  {
    Serial.println("AT command \"+CNTP\" executing fault.");
  }  
  return -1;
}

Cделаем разрешение (resolve) доменного имени:

AT+CDNSGIP="ya.ru"
OK
+CDNSGIP: 0,8 - parsing error
+CDNSGIP: 1,"ya.ru","87.250.250.242" - корректный результат

Как и в случае с AT+CNTP периодически функция возвращает ошибку. Мне не удалось найти способа обеспечить 100% стабильную работу. Настройки DNS серверов принятых SIM800 корректные, даже когда AT команда AT+CDNSGIP возвращает неверный результат:

AT+CDNSCFG?

PrimaryDns: 8.8.8.8
SecondaryDns: 8.8.4.4

OK

C DNS серверами все в порядке, однако, возникает проблема при синхронизации времени с NTP сервером, либо разрешения символического имени.

Код для TinyGSM для активации автоматической синхронизации времени

При выполнении команды перезагрузки (restart()) в TinyGSM выполняется команда AT+CLTS=1:

  bool restart() {
    if (!testAT()) {
      return false;
    }
    //Enable Local Time Stamp for getting network time
    // TODO: Find a better place for this
    sendAT(GF("+CLTS=1"));
    if (waitResponse(10000L) != 1) {
      return false;
    }
    sendAT(GF("&W"));
    waitResponse();
    sendAT(GF("+CFUN=0"));
    if (waitResponse(10000L) != 1) {
      return false;
    }
    sendAT(GF("+CFUN=1,1"));
    if (waitResponse(10000L) != 1) {
      return false;
    }
    delay(3000);
    return init();
  }

Однако код отрабатывает некорректно. Если после restart() выполнить команду AT+CCLK?, то она вернет время по умолчанию. Для тестирования команд GSM модема SIM800 я написал скетч.

После тестирования различных вариантов получил нормально работающий код:

void SetAutoTimeSync()
{
  Serial.println("Enable GSM RTC network time sync.");
  sendAT(GF("+CLTS=1"));
  if (waitResponse(10000L) != 1) 
  {
      Serial.println("AT command \"AT+CLTS=1\" executing fault.");;
  }
  
  sendAT(GF("&W"));
  waitResponse();

  sendAT(GF("+CFUN=0"));
  if (waitResponse(10000L) != 1) 
  {
      Serial.println("AT command \"AT+CFUN=0\" executing fault.");;
  }
  
  //delay(3000);
  sendAT(GF("+CFUN=1"));
  //if (modemGSM.waitResponse(10000L) != 1) 
  if (waitResponse(10000L, GF(GSM_NL "DST:")) != 1)
  {
    Serial.println("AT command \"AT+CFUN=1\" executing fault.");;
  }

  Serial.println("DateTime: "+ GetDateTime());
}

String GetDateTime()
{
  sendAT(GF("+CCLK?"));
  if (waitResponse(10000L, GF(GSM_NL "+CCLK:")))
  {
      String body = stream.readStringUntil('\n');
      return body;
  }
  else
  {
    Serial.println("AT command \"+CCLK?\" executing fault.");
  } 
  return "";
}

Отправил в github.

Для парсинга строки DateTime возвращаемой GSM модемом SIM800 я написал следующую функцию:

//Parse GSM modem DateTime string "yy/mm/dd,hh:mm:ss+/-zz"
bool ParseDateTimeString(String datetime, byte &year, byte &month, byte &day, byte &hours, byte &minutes, byte &seconds, byte &timezone)
{ 
   return sscanf(datetime.c_str(), "%d/%d/%d,%d:%d:%d+%d", &year, &month, &day, &hours, &minutes, &seconds, &timezone) == 7;
}

Вызов функции:

  byte year = 0;
  byte month = 0;
  byte day = 0; 
  byte hours = 0;
  byte minutes = 0;
  byte seconds = 0;
  byte timezone = 0;
  ParseDateTimeString("19/05/25,21:50:06+12", year, month, day, hours, minutes, seconds, timezone);
  char str[25];
  sprintf(str, "%d/%d/%d,%d:%d:%d+%d", year, month, day, hours, minutes, seconds, timezone);
  Serial.println(str);

Полезные ссылки

Spread the love
Запись опубликована в рубрике IT рецепты с метками , . Добавьте в закладки постоянную ссылку.