text: #ifndef BLINKER_QRCODE_SIM7020_H
  #define BLINKER_QRCODE_SIM7020_H
  
  #if ARDUINO >= 100
      #include <Arduino.h>
  #else
      #include <WProgram.h>
  #endif
  
  #include "../Blinker/BlinkerConfig.h"
  #include "../Blinker/BlinkerDebug.h"
  #include "../Blinker/BlinkerStream.h"
  #include "../Blinker/BlinkerUtility.h"
  #include "../Functions/BlinkerSIM7020.h"
  #include "../Functions/BlinkerHTTPSIM7020.h"
  #ifndef ARDUINOJSON_VERSION_MAJOR
  #include "../modules/ArduinoJson/ArduinoJson.h"
  #endif
  #include "../Functions/BlinkerMQTTSIM7020.h"
  
  char*       MQTT_HOST_NBIoT;
  char*       MQTT_ID_NBIoT;
  char*       MQTT_NAME_NBIoT;
  char*       MQTT_KEY_NBIoT;
  char*       MQTT_PRODUCTINFO_NBIoT;
  char*       UUID_NBIoT;
  char*       AUTHKEY_NBIoT;
  char*       MQTT_DEVICEID_NBIoT;
  char*       DEVICE_NAME_NBIoT;
  char*       BLINKER_PUB_TOPIC_NBIoT;
  char*       BLINKER_SUB_TOPIC_NBIoT;
  uint16_t    MQTT_PORT_NBIoT;
  
  BlinkerMQTTSIM7020* mqtt_NBIoT;
  
  class BlinkerQRCodeSIM7020 : public BlinkerStream
  {
      public :
          BlinkerQRCodeSIM7020()
              : stream(NULL), isConnect(false)
          {}
  
          int connect();
          int connected();
          int mConnected();
          void disconnect();
          void ping();
          int available();
          int aligenieAvail();
          int duerAvail();
          void subscribe();
          int timedRead();
          char * lastRead() { if (isFresh_NBIoT) return msgBuf_NBIoT; return ""; }
          void flush();
          // int print(const String & s, bool needCheck = true);
          int print(char * data, bool needCheck = true);
          int toServer(char * data);
          int bPrint(char * name, const String & data);
          int aliPrint(const String & data);
          int  duerPrint(const String & data, bool report = false);
          void aliType(const String & type);
          void duerType(const String & type);
          void begin(const char* _auth, const char* _type, String _imei);
          void initStream(Stream& s, bool state, blinker_callback_t func);
          int autoPrint(unsigned long id);
          char * deviceName();
          char * authKey() { return AUTHKEY_NBIoT; }
          char * token() { if (!isMQTTinit) return ""; else return MQTT_KEY_GPRS; }
          int init() { return isMQTTinit; }
          int reRegister() { return connectServer(); }
          int deviceRegister() { return connectServer(); }
          // int authCheck();
          void freshAlive() { kaTime = millis(); isAlive = true; }
          void sharers(const String & data);
          int  needFreshShare() {
              if (_needCheckShare)
              {
                  BLINKER_LOG_ALL(BLINKER_F("needFreshShare"));
                  _needCheckShare = false;
                  return true;
              }
              else
              {
                  return false;
              }
          }
  
      private :
          bool        isMQTTinit = false;
  
          int connectServer();
          void checkKA();
          int checkAliKA();
          int checkDuerKA();
          int checkCanPrint();
          int checkCanBprint();
          int checkPrintSpan();
          int checkAliPrintSpan();
          int checkDuerPrintSpan();
  
      protected :
          BlinkerSharer * _sharers[BLINKER_MQTT_MAX_SHARERS_NUM];
          uint8_t     _sharerCount = 0;
          uint8_t     _sharerFrom = BLINKER_MQTT_FROM_AUTHER;
          Stream*     stream;
          // char*       streamData;
          char*       msgBuf_NBIoT;
          // bool        isFresh = false;
          bool        isFresh_NBIoT = false;
          bool        isConnect;
          bool        isHWS = false;
          char*       imei;
          // uint8_t     respTimes = 0;
          // uint32_t    respTime = 0;
          bool        isAvail_NBIoT = false;
          uint8_t     dataFrom_NBIoT = BLINKER_MSG_FROM_MQTT;
  
          // uint8_t     _sharerFrom = BLINKER_MQTT_FROM_AUTHER;
          const char* _deviceType;//_authKey
          const char* _authKey;
          char*       _aliType;
          char*       _duerType;
          bool        isAlive = false;
          // bool        isBavail = false;
          bool        _needCheckShare = false;
          uint32_t    latestTime;
          uint32_t    printTime = 0;
          uint32_t    bPrintTime = 0;
          uint32_t    kaTime = 0;
          uint32_t    linkTime = 0;
          uint8_t     respTimes = 0;
          uint32_t    respTime = 0;
          uint8_t     respAliTimes = 0;
          uint32_t    respAliTime = 0;
          uint8_t     respDuerTimes = 0;
          uint32_t    respDuerTime = 0;
  
          uint32_t    aliKaTime = 0;
          bool        isAliAlive = false;
          bool        isAliAvail = false;
          uint32_t    duerKaTime = 0;
          bool        isDuerAlive = false;
          bool        isDuerAvail = false;
          char*       mqtt_broker;
  
          int isJson(const String & data);
  
          uint8_t     reconnect_time = 0;
          blinker_callback_t listenFunc = NULL;
  };
  
  int BlinkerQRCodeSIM7020::connect()
  {
      BLINKER_LOG_ALL(BLINKER_F(">>>>>> mqtt connect <<<<<<"));
  
      if (!isMQTTinit) return false;
  
      // if (mqtt_NBIoT->connected()) return true;
      if (isConnect) return true;
  
      BLINKER_LOG_ALL(BLINKER_F(">>>>>> mqtt connect failed <<<<<<"));
  
      disconnect();
  
      // if ((millis() - latestTime) < BLINKER_MQTT_CONNECT_TIMESLOT && latestTime > 0)
      // {
      //     yield();
      //     return false;
      // }
  
      BLINKER_LOG(BLINKER_F("Connecting to MQTT... "));
  
      BLINKER_LOG_FreeHeap_ALL();
  
      if (!mqtt_NBIoT->connect())
      {
          BLINKER_LOG(BLINKER_F("Retrying MQTT connection in "), \
                      BLINKER_MQTT_CONNECT_TIMESLOT/1000, \
                      BLINKER_F(" seconds..."));
  
          this->latestTime = millis();
          reconnect_time += 1;
          if (reconnect_time >= 12)
          {
              reRegister();
              reconnect_time = 0;
          }
          return false;
      }
  
      reconnect_time = 0;
      
      BLINKER_LOG(BLINKER_F("MQTT Connected!"));
      BLINKER_LOG_FreeHeap();
  
      isConnect = true;
  
      this->latestTime = millis();
  
      return true;
  }
  
  int BlinkerQRCodeSIM7020::connected()
  {
      if (!isMQTTinit) return false;
  
      return isConnect;//mqtt_NBIoT->connected();
  }
  
  int BlinkerQRCodeSIM7020::mConnected()
  {
      if (!isMQTTinit) return false;
      else return mqtt_NBIoT->connected();
  }
  
  void BlinkerQRCodeSIM7020::disconnect()
  {
      if (isMQTTinit) {
          isConnect = false;
          mqtt_NBIoT->disconnect();
      }
  }
  
  void BlinkerQRCodeSIM7020::ping()
  {
      BLINKER_LOG_ALL(BLINKER_F("MQTT Ping!"));
  
      BLINKER_LOG_FreeHeap_ALL();
  
      if (!isMQTTinit) return;
  
      if (!isConnect)
      {
          // disconnect();
  
          delay(100);
  
          connect();
          // delay(100);
  
          // connect();
      }
      else
      {
          this->latestTime = millis();
      }
  
      BlinkerSIM7020 BLINKER_SIM7020;
      BLINKER_SIM7020.setStream(*stream, isHWS, listenFunc);  
  
      BLINKER_LOG_ALL(BLINKER_F("check isReboot"));
  
      if (BLINKER_SIM7020.isReboot())
      {
          BLINKER_SIM7020.powerCheck();
  
          // disconnect();
      }
  }
  
  int BlinkerQRCodeSIM7020::available()
  {
      if (isMQTTinit) {
          checkKA();
  
          // if (!mqtt_PRO->connected() || \
          //     (millis() - this->latestTime) > BLINKER_MQTT_PING_TIMEOUT)
          if ((millis() - this->latestTime) > 30000)
          {
              ping();
          }
          else
          {
              subscribe();
          }
      }
  
      if (isAvail_NBIoT)
      {
          isAvail_NBIoT = false;
          return true;
      }
      else {
          return false;
      }
  }
  
  int BlinkerQRCodeSIM7020::aligenieAvail()
  {
      if (!isMQTTinit) return false;
  
      if (isAliAvail)
      {
          isAliAvail = false;
          return true;
      }
      else {
          return false;
      }
  }
  
  int BlinkerQRCodeSIM7020::duerAvail()
  {
      if (!isMQTTinit) return false;
  
      if (isDuerAvail)
      {
          isDuerAvail = false;
          return true;
      }
      else {
          return false;
      }
  }
  
  void BlinkerQRCodeSIM7020::subscribe()
  {
      if (!isMQTTinit) return;
  
      if (mqtt_NBIoT->readSubscription())
      {
          BLINKER_LOG_ALL(BLINKER_F("Got: "), mqtt_NBIoT->lastRead);
  
          // DynamicJsonBuffer jsonBuffer;
          // JsonObject& root = jsonBuffer.parseObject(String(mqtt_NBIoT->lastRead));
          DynamicJsonDocument jsonBuffer(1024);
          DeserializationError error = deserializeJson(jsonBuffer, String(mqtt_NBIoT->lastRead));
          JsonObject root = jsonBuffer.as<JsonObject>();
  
          String _uuid = root["fromDevice"];
          String dataGet = root["data"];
          
          BLINKER_LOG_ALL(BLINKER_F("data: "), dataGet);
          BLINKER_LOG_ALL(BLINKER_F("fromDevice: "), _uuid);
          
          if (strcmp(_uuid.c_str(), UUID_NBIoT) == 0)
          {
              BLINKER_LOG_ALL(BLINKER_F("Authority uuid"));
              
              kaTime = millis();
              isAvail_NBIoT = true;
              isAlive = true;
  
              _sharerFrom = BLINKER_MQTT_FROM_AUTHER;
          }
          else if (_uuid == BLINKER_CMD_ALIGENIE)
          {
              BLINKER_LOG_ALL(BLINKER_F("form AliGenie"));
  
              aliKaTime = millis();
              isAliAlive = true;
              isAliAvail = true;
          }
          else if (_uuid == BLINKER_CMD_DUEROS)
          {
              BLINKER_LOG_ALL(BLINKER_F("form DuerOS"));
  
              duerKaTime = millis();
              isDuerAlive = true;
              isDuerAvail = true;
          }
          else if (_uuid == BLINKER_CMD_SERVERCLIENT)
          {
              BLINKER_LOG_ALL(BLINKER_F("form Sever"));
  
              isAvail_NBIoT = true;
              isAlive = true;
  
              _sharerFrom = BLINKER_MQTT_FROM_AUTHER;
          }
          else
          {
              if (_sharerCount)
              {
                  for (uint8_t num = 0; num < _sharerCount; num++)
                  {
                      if (strcmp(_uuid.c_str(), _sharers[num]->uuid()) == 0)
                      {
                          _sharerFrom = num;
  
                          kaTime = millis();
  
                          BLINKER_LOG_ALL(BLINKER_F("From sharer: "), _uuid);
                          BLINKER_LOG_ALL(BLINKER_F("sharer num: "), num);
                          
                          _needCheckShare = false;
  
                          break;
                      }
                      else
                      {
                          BLINKER_ERR_LOG_ALL(BLINKER_F("No authority uuid found, \
                                      check is from bridge/share device, \
                                      data: "), dataGet);
  
                          _needCheckShare = true;
                      }
                  }
              }
              // root.printTo(dataGet);
              serializeJson(root, dataGet);
  
              isAvail_NBIoT = true;
              isAlive = true;
          }
  
          if (isFresh_NBIoT) free(msgBuf_NBIoT);
          msgBuf_NBIoT = (char*)malloc((dataGet.length()+1)*sizeof(char));
          strcpy(msgBuf_NBIoT, dataGet.c_str());
          isFresh_NBIoT = true;
          
          this->latestTime = millis();
  
          dataFrom_NBIoT = BLINKER_MSG_FROM_MQTT;
      }
  }
  
  void BlinkerQRCodeSIM7020::flush()
  {
      if (isFresh_NBIoT)
      {
          free(msgBuf_NBIoT); isFresh_NBIoT = false; isAvail_NBIoT = false;
          isAliAvail = false; //isBavail = false;
      }
  }
  
  int BlinkerQRCodeSIM7020::print(char * data, bool needCheck)
  {
      if (!isMQTTinit) return false;
  
      BLINKER_LOG_ALL(BLINKER_F("data: "), data);
  
      uint16_t num = strlen(data);
  
      data[num+8] = '\0';
  
      for(uint16_t c_num = num; c_num > 0; c_num--)
      {
          data[c_num+7] = data[c_num-1];
      }
  
      // String data_add = BLINKER_F("{\"data\":");
      char data_add[20] = "{\"data\":";
      for(uint16_t c_num = 0; c_num < 8; c_num++)
      {
          data[c_num] = data_add[c_num];
      }
  
      // data_add = BLINKER_F(",\"fromDevice\":\"");
      // strcat(data, data_add.c_str());
      strcat(data, ",\"fromDevice\":\"");
      strcat(data, MQTT_ID_NBIoT);
      // strcat(data, MQTT_DEVICEID_NBIoT); //PRO
      // data_add = BLINKER_F("\",\"toDevice\":\"");
      // strcat(data, data_add.c_str());
      strcat(data, "\",\"toDevice\":\"");
      if (_sharerFrom < BLINKER_MQTT_MAX_SHARERS_NUM)
      {
          strcat(data, _sharers[_sharerFrom]->uuid());
      }
      else
      {
          strcat(data, UUID_NBIoT);
      }
      // data_add = BLINKER_F("\",\"deviceType\":\"OwnApp\"}");
  
      _sharerFrom = BLINKER_MQTT_FROM_AUTHER;
      // strcat(data, data_add.c_str());
      strcat(data, "\",\"deviceType\":\"OwnApp\"}");
  
      // data_add = STRING_format(data);
  
      if (!isJson(STRING_format(data))) return false;
  
      BLINKER_LOG_ALL(BLINKER_F("MQTT Publish..."));
      BLINKER_LOG_FreeHeap_ALL();
      
      bool _alive = isAlive;
      
      if (needCheck)
      {
          if (!checkPrintSpan())
          {
              return false;
          }
          respTime = millis();
      }
      
      if (connected())
      {
          if (needCheck)
          {
          if (!checkCanPrint())
              {
                  if (!_alive)
                  {
                      isAlive = false;
                  }
                  return false;
              }
          }
          // if (! mqtt_NBIoT->publish(BLINKER_PUB_TOPIC_NBIoT, msg_data.c_str()))
          if (! mqtt_NBIoT->publish(BLINKER_PUB_TOPIC_NBIoT, data))
          {
              BLINKER_LOG_ALL(data);
              BLINKER_LOG_ALL(BLINKER_F("...Failed"));
              BLINKER_LOG_FreeHeap_ALL();
  
              disconnect();
              
              if (!_alive)
              {
                  isAlive = false;
              }
              return false;
          }
          else
          {
              BLINKER_LOG_ALL(data);
              BLINKER_LOG_ALL(BLINKER_F("...OK!"));
              BLINKER_LOG_FreeHeap_ALL();
              
              if (needCheck) printTime = millis();
  
              if (!_alive)
              {
                  isAlive = false;
              }
  
              this->latestTime = millis();
  
              return true;
          }            
      }
      else
      {
          BLINKER_ERR_LOG(BLINKER_F("MQTT Disconnected"));
          isAlive = false;
          return false;
      }
  }
  
  
  int BlinkerQRCodeSIM7020::toServer(char * data)
  {
      if (!isMQTTinit) return false;    
  
      if (!isJson(STRING_format(data))) return false;
  
      BLINKER_LOG_ALL(BLINKER_F("MQTT Publish to server..."));
      BLINKER_LOG_FreeHeap_ALL();
  
      bool _alive = isAlive;
  
      if (mqtt_NBIoT->connected())
      {
          if (! mqtt_NBIoT->publish(BLINKER_PUB_TOPIC_NBIoT, data))
          {
              BLINKER_LOG_ALL(data);
              BLINKER_LOG_ALL(BLINKER_F("...Failed"));
              BLINKER_LOG_FreeHeap_ALL();
              
              return false;
          }
          else
          {
              BLINKER_LOG_ALL(data);
              BLINKER_LOG_ALL(BLINKER_F("...OK!"));
              BLINKER_LOG_FreeHeap_ALL();
              
              return true;
          }
      }
      else
      {
          BLINKER_ERR_LOG(BLINKER_F("MQTT Disconnected"));
          isAlive = false;
          return false;
      }
  }
  
  int BlinkerQRCodeSIM7020::bPrint(char * name, const String & data)
  {
      if (!isMQTTinit) return false;
  
      char data_add[1024] = { '\0' };
  
      strcpy(data_add, "{\"data\":");
      strcat(data_add, data.c_str());
      strcat(data_add, ",\"fromDevice\":\"");
      strcat(data_add, MQTT_ID_NBIoT);
      strcat(data_add, ",\"toDevice\":\"");
      strcat(data_add, name);
      strcat(data_add, "\",\"deviceType\":\"DiyBridge\"}");
      // String data_add = BLINKER_F("{\"data\":");
  
      // data_add += data;
      // data_add += BLINKER_F(",\"fromDevice\":\"");
      // data_add += MQTT_ID_MQTT;
      // data_add += BLINKER_F("\",\"toDevice\":\"");
      // data_add += name;
      // data_add += BLINKER_F("\",\"deviceType\":\"DiyBridge\"}");
  
      if (!isJson(STRING_format(data_add))) return false;
  
      BLINKER_LOG_ALL(BLINKER_F("MQTT Bridge Publish..."));
  
      // bool _alive = isAlive;
      // bool state = STRING_contains_string(data, BLINKER_CMD_NOTICE);
  
      // if (!state) {
      //     state = (STRING_contains_string(data, BLINKER_CMD_STATE)
      //         && STRING_contains_string(data, BLINKER_CMD_ONLINE));
      // }
  
      if (mqtt_NBIoT->connected()) {
          // if (!state) {
          if (!checkCanBprint()) {
              // if (!_alive) {
              //     isAlive = false;
              // }
              return false;
          }
          // }
  
          // Adafruit_MQTT_Publish iotPub = Adafruit_MQTT_Publish(mqtt_MQTT, BLINKER_PUB_TOPIC_MQTT);
  
          // if (! iotPub.publish(payload.c_str())) {
  
          // String bPubTopic = BLINKER_F("");
          // char bPubTopic[60] = { '\0' };
  
          // if (strcmp(mqtt_broker, BLINKER_MQTT_BORKER_ONENET) == 0)
          // {
          //     // bPubTopic = MQTT_PRODUCTINFO_MQTT;
          //     // bPubTopic += BLINKER_F("/");
          //     // bPubTopic += name;
          //     // bPubTopic += BLINKER_F("/r");
          // }
          // else
          // {
              // strcpy(bPubTopic, BLINKER_PUB_TOPIC_MQTT);
              // bPubTopic = BLINKER_PUB_TOPIC_MQTT;
          // }
  
          if (! mqtt_NBIoT->publish(BLINKER_PUB_TOPIC_NBIoT, data_add))
          {
              BLINKER_LOG_ALL(data_add);
              BLINKER_LOG_ALL(BLINKER_F("...Failed"));
  
              // if (!_alive) {
              //     isAlive = false;
              // }
              return false;
          }
          else
          {
              BLINKER_LOG_ALL(data_add);
              BLINKER_LOG_ALL(BLINKER_F("...OK!"));
  
              bPrintTime = millis();
  
              // if (!_alive) {
              //     isAlive = false;
              // }
  
              this->latestTime = millis();
  
              return true;
          }
      }
      else
      {
          BLINKER_ERR_LOG(BLINKER_F("MQTT Disconnected"));
          // isAlive = false;
          return false;
      }
      // }
  }
  
  int BlinkerQRCodeSIM7020::aliPrint(const String & data)
  {
      if (!isMQTTinit) return false;
  
      char data_add[1024] = { '\0' };
  
      strcpy(data_add, "{\"data\":");
      strcat(data_add, data.c_str());
      strcat(data_add, ",\"fromDevice\":\"");
      strcat(data_add, MQTT_ID_NBIoT);
      strcat(data_add, "\",\"toDevice\":\"AliGenie_r\"");
      strcat(data_add, "\",\"deviceType\":\"vAssistant\"}");
  
      // String data_add = BLINKER_F("{\"data\":");
  
      // data_add += data;
      // data_add += BLINKER_F(",\"fromDevice\":\"");
      // data_add += MQTT_ID_MQTT;
      // data_add += BLINKER_F("\",\"toDevice\":\"AliGenie_r\"");
      // data_add += BLINKER_F(",\"deviceType\":\"vAssistant\"}");
  
      if (!isJson(STRING_format(data_add))) return false;
  
      BLINKER_LOG_ALL(BLINKER_F("MQTT AliGenie Publish..."));
      BLINKER_LOG_FreeHeap_ALL();
  
      if (mqtt_NBIoT->connected())
      {
          if (!checkAliKA())
          {
              return false;
          }
  
          if (!checkAliPrintSpan())
          {
              respAliTime = millis();
              return false;
          }
          respAliTime = millis();
  
          if (! mqtt_NBIoT->publish(BLINKER_PUB_TOPIC_NBIoT, data_add))
          {
              BLINKER_LOG_ALL(data_add);
              BLINKER_LOG_ALL(BLINKER_F("...Failed"));
              BLINKER_LOG_FreeHeap_ALL();
  
              isAliAlive = false;
              return false;
          }
          else
          {
              BLINKER_LOG_ALL(data_add);
              BLINKER_LOG_ALL(BLINKER_F("...OK!"));
              BLINKER_LOG_FreeHeap_ALL();
  
              isAliAlive = false;
  
              this->latestTime = millis();
  
              return true;
          }
      }
      else
      {
          BLINKER_ERR_LOG(BLINKER_F("MQTT Disconnected"));
          return false;
      }
  }
  
  int BlinkerQRCodeSIM7020::duerPrint(const String & data, bool report)
  {
      if (!isMQTTinit) return false;
  
      char data_add[1024] = { '\0' };
  
      strcpy(data_add, "{\"data\":");
      strcat(data_add, data.c_str());
      strcat(data_add, ",\"fromDevice\":\"");
      strcat(data_add, MQTT_ID_NBIoT);
      strcat(data_add, "\",\"toDevice\":\"DuerOS_r\"");
      strcat(data_add, "\",\"deviceType\":\"vAssistant\"}");
  
      // String data_add = BLINKER_F("{\"data\":");
  
      // data_add += data;
      // data_add += BLINKER_F(",\"fromDevice\":\"");
      // data_add += MQTT_ID_MQTT;
      // data_add += BLINKER_F("\",\"toDevice\":\"DuerOS_r\"");
      // data_add += BLINKER_F(",\"deviceType\":\"vAssistant\"}");
  
      if (!isJson(STRING_format(data_add))) return false;
  
      BLINKER_LOG_ALL(BLINKER_F("MQTT DuerOS Publish..."));
      BLINKER_LOG_FreeHeap_ALL();
  
      if (mqtt_NBIoT->connected())
      {
          if (!checkDuerKA())
          {
              return false;
          }
  
          if (!checkDuerPrintSpan())
          {
              respDuerTime = millis();
              return false;
          }
          respDuerTime = millis();
  
          if (! mqtt_NBIoT->publish(BLINKER_PUB_TOPIC_NBIoT, data_add))
          {
              BLINKER_LOG_ALL(data_add);
              BLINKER_LOG_ALL(BLINKER_F("...Failed"));
              BLINKER_LOG_FreeHeap_ALL();
  
              isDuerAlive = false;
              return false;
          }
          else
          {
              BLINKER_LOG_ALL(data_add);
              BLINKER_LOG_ALL(BLINKER_F("...OK!"));
              BLINKER_LOG_FreeHeap_ALL();
  
              isDuerAlive = false;
  
              this->latestTime = millis();
  
              return true;
          }
      }
      else
      {
          BLINKER_ERR_LOG(BLINKER_F("MQTT Disconnected"));
          return false;
      }
  }
  
  void BlinkerQRCodeSIM7020::aliType(const String & type)
  {
      _aliType = (char*)malloc((type.length()+1)*sizeof(char));
      strcpy(_aliType, type.c_str());
      BLINKER_LOG_ALL(BLINKER_F("_aliType: "), _aliType);
  }
  
  void BlinkerQRCodeSIM7020::duerType(const String & type)
  {
      _duerType = (char*)malloc((type.length()+1)*sizeof(char));
      strcpy(_duerType, type.c_str());
      BLINKER_LOG_ALL(BLINKER_F("_duerType: "), _duerType);
  }
  
  void BlinkerQRCodeSIM7020::begin(const char* _auth, const char* _type, String _imei)
  {
      _authKey = _auth;
      _deviceType = _type;
      
      BLINKER_LOG_ALL(BLINKER_F("PRO deviceType: "), _type);
  
      // stream = &s;
      // stream->setTimeout(BLINKER_STREAM_TIMEOUT);
      // isHWS = state;
  
      imei = (char*)malloc((_imei.length() + 1)*sizeof(char));
      strcpy(imei, _imei.c_str());
  
      AUTHKEY_NBIoT = (char*)malloc((strlen(_deviceType) + 1)*sizeof(char));
      strcpy(AUTHKEY_NBIoT, _deviceType);
  }
  
  void BlinkerQRCodeSIM7020::initStream(Stream& s, bool state, blinker_callback_t func)
  {
      // _deviceType = _type;
      
      // BLINKER_LOG_ALL(BLINKER_F("PRO deviceType: "), _type);
  
      stream = &s;
      stream->setTimeout(BLINKER_STREAM_TIMEOUT);
      isHWS = state;
  
      listenFunc = func;
  
      // streamPrint(BLINKER_CMD_AT);
      // ::delay(200);
      // streamPrint(BLINKER_CMD_CRESET_RESQ);
  
      // _imei = (char*)malloc(imei.length()*sizeof(char));
      // strcpy(_imei, imei.c_str());
  }
  
  int BlinkerQRCodeSIM7020::autoPrint(unsigned long id)
  {
      String payload = BLINKER_F("{\"data\":{\"set\":{");
      payload += BLINKER_F("\"trigged\":true,\"autoData\":{");
      payload += BLINKER_F("\"autoId\":");
      payload += STRING_format(id);
      payload += BLINKER_F("}}}");
      payload += BLINKER_F(",\"fromDevice\":\"");
      payload += STRING_format(MQTT_ID_NBIoT);
      payload += BLINKER_F("\",\"toDevice\":\"autoManager\"}");
          // "\",\"deviceType\":\"" + "type" + "\"}";
  
      BLINKER_LOG_ALL(BLINKER_F("autoPrint..."));
  
      if (mqtt_NBIoT->connected())
      {
          if ((millis() - linkTime) > BLINKER_LINK_MSG_LIMIT || \
              linkTime == 0)
          {
              // linkTime = millis();
  
              // Adafruit_MQTT_Publish iotPub = Adafruit_MQTT_Publish(mqtt_MQTT, BLINKER_PUB_TOPIC_MQTT);
  
              // if (! iotPub.publish(payload.c_str())) {
  
              if (! mqtt_NBIoT->publish(BLINKER_PUB_TOPIC_NBIoT, payload.c_str()))
              {
                  BLINKER_LOG_ALL(payload);
                  BLINKER_LOG_ALL(BLINKER_F("...Failed"));
  
                  return false;
              }
              else
              {
                  BLINKER_LOG_ALL(payload);
                  BLINKER_LOG_ALL(BLINKER_F("...OK!"));
  
                  linkTime = millis();
  
                  this->latestTime = millis();
  
                  return true;
              }
          }
          else
          {
              BLINKER_ERR_LOG(BLINKER_F("MQTT NOT ALIVE OR MSG LIMIT "), linkTime);
  
              return false;
          }
      }
      else
      {
          BLINKER_ERR_LOG(BLINKER_F("MQTT Disconnected"));
          return false;
      }
  }
  
  char * BlinkerQRCodeSIM7020::deviceName()
  {
      BLINKER_LOG_ALL(BLINKER_F("BlinkerQRCodeSIM7020::deviceName: "), MQTT_ID_NBIoT);
      return MQTT_ID_NBIoT;/*MQTT_ID_PRO;*/
  }
  
  void BlinkerQRCodeSIM7020::sharers(const String & data)
  {
      BLINKER_LOG_ALL(BLINKER_F("sharers data: "), data);
  
      // DynamicJsonBuffer jsonBuffer;
      // JsonObject& root = jsonBuffer.parseObject(data);
      DynamicJsonDocument jsonBuffer(1024);
      DeserializationError error = deserializeJson(jsonBuffer, data);
      JsonObject root = jsonBuffer.as<JsonObject>();
  
      // if (!root.success()) return;
      if (error) return;
  
      String user_name = "";
  
      if (_sharerCount)
      {
          for (uint8_t num = _sharerCount; num > 0; num--)
          {
              delete _sharers[num - 1];
          }
      }
  
      _sharerCount = 0;
  
      for (uint8_t num = 0; num < BLINKER_MQTT_MAX_SHARERS_NUM; num++)
      {
          user_name = root["users"][num].as<String>();
  
          if (user_name.length() >= BLINKER_MQTT_USER_UUID_SIZE)
          {
              BLINKER_LOG_ALL(BLINKER_F("sharer uuid: "), user_name, BLINKER_F(", length: "), user_name.length());
  
              _sharerCount++;
  
              _sharers[num] = new BlinkerSharer(user_name);
          }
          else
          {
              break;
          }
      }
  }
  
  int BlinkerQRCodeSIM7020::connectServer()
  {
      String host = BLINKER_F(BLINKER_SERVER_HTTPS);
      String uri = "";
      // uri += BLINKER_F("/api/v1/user/device/register?deviceType=");
      // uri += _deviceType;
      // uri += BLINKER_F("&deviceName=");
      // uri += imei;
      uri += BLINKER_F("/api/v1/user/device/scancode/add");
      // uri += _deviceType;
      // uri += _aliType;
      // uri += _duerType;
  
      BLINKER_LOG_ALL(BLINKER_F("HTTPS begin: "), host + uri);
  
      BlinkerHTTPSIM7020 http(*stream, isHWS, listenFunc);
  
      http.begin(host, uri);
  
      String post_data = "{\"deviceType\":\"";
      post_data += STRING_format(_deviceType);
      post_data += "\",\"typeKey\":\"";
      post_data += STRING_format(_authKey);
      post_data += "\",\"registerKey\":\"";
      post_data += imei;
      post_data += "\",\"expiresIn\": 60}";
  
      String payload;
  
      if (http.POST(post_data, "", ""))
      {
          BLINKER_LOG(BLINKER_F("[HTTP] POST... success"));
  
          payload = http.getString();
  
          BLINKER_LOG(payload);
  
          return true;
      }
      else
      {
          BLINKER_LOG(BLINKER_F("[HTTP] POST... failed"));
  
          return false;
      }
  
      BLINKER_LOG_ALL(BLINKER_F("reply was:"));
      BLINKER_LOG_ALL(BLINKER_F("=============================="));
      BLINKER_LOG_ALL(payload);
      BLINKER_LOG_ALL(BLINKER_F("=============================="));
  
      // DynamicJsonBuffer jsonBuffer;
      // JsonObject& root = jsonBuffer.parseObject(payload);
      DynamicJsonDocument jsonBuffer(1024);
      DeserializationError error = deserializeJson(jsonBuffer, payload);
      JsonObject root = jsonBuffer.as<JsonObject>();
  
      if (STRING_contains_string(payload, BLINKER_CMD_NOTFOUND) || error ||
          !STRING_contains_string(payload, BLINKER_CMD_IOTID)) {
          // while(1) {
              BLINKER_ERR_LOG(("Please make sure you have register this device!"));
              // ::delay(60000);
  
              return false;
          // }
      }
  
      String _userID = root[BLINKER_CMD_DETAIL][BLINKER_CMD_DEVICENAME];
      String _userName = root[BLINKER_CMD_DETAIL][BLINKER_CMD_IOTID];
      String _key = root[BLINKER_CMD_DETAIL][BLINKER_CMD_IOTTOKEN];
      String _productInfo = root[BLINKER_CMD_DETAIL][BLINKER_CMD_PRODUCTKEY];
      String _broker = root[BLINKER_CMD_DETAIL][BLINKER_CMD_BROKER];
      String _uuid = root[BLINKER_CMD_DETAIL][BLINKER_CMD_UUID];
  
      if (isMQTTinit)
      {
          free(MQTT_HOST_NBIoT);
          free(MQTT_ID_NBIoT);
          free(MQTT_NAME_NBIoT);
          free(MQTT_KEY_NBIoT);
          free(MQTT_PRODUCTINFO_NBIoT);
          free(UUID_NBIoT);
          free(DEVICE_NAME_NBIoT);
          free(BLINKER_PUB_TOPIC_NBIoT);
          free(BLINKER_SUB_TOPIC_NBIoT);
          free(mqtt_NBIoT);
          // free(iotSub_NBIoT);
  
          isMQTTinit = false;
      }
  
      if (_broker == BLINKER_MQTT_BORKER_ALIYUN) {
          // memcpy(DEVICE_NAME_MQTT, _userID.c_str(), 12);
          DEVICE_NAME_NBIoT = (char*)malloc((_userID.length()+1)*sizeof(char));
          strcpy(DEVICE_NAME_NBIoT, _userID.c_str());
          MQTT_ID_NBIoT = (char*)malloc((_userID.length()+1)*sizeof(char));
          strcpy(MQTT_ID_NBIoT, _userID.c_str());
          MQTT_NAME_NBIoT = (char*)malloc((_userName.length()+1)*sizeof(char));
          strcpy(MQTT_NAME_NBIoT, _userName.c_str());
          MQTT_KEY_NBIoT = (char*)malloc((_key.length()+1)*sizeof(char));
          strcpy(MQTT_KEY_NBIoT, _key.c_str());
          MQTT_PRODUCTINFO_NBIoT = (char*)malloc((_productInfo.length()+1)*sizeof(char));
          strcpy(MQTT_PRODUCTINFO_NBIoT, _productInfo.c_str());
          MQTT_HOST_NBIoT = (char*)malloc((strlen(BLINKER_MQTT_ALIYUN_HOST)+1)*sizeof(char));
          strcpy(MQTT_HOST_NBIoT, BLINKER_MQTT_ALIYUN_HOST);
          MQTT_PORT_NBIoT = BLINKER_MQTT_ALIYUN_PORT;
      }
  
      UUID_NBIoT = (char*)malloc((_uuid.length()+1)*sizeof(char));
      strcpy(UUID_NBIoT, _uuid.c_str());
  
      BLINKER_LOG_ALL(BLINKER_F("===================="));
      BLINKER_LOG_ALL(BLINKER_F("DEVICE_NAME_NBIoT: "), DEVICE_NAME_NBIoT);
      BLINKER_LOG_ALL(BLINKER_F("MQTT_PRODUCTINFO_NBIoT: "), MQTT_PRODUCTINFO_NBIoT);
      BLINKER_LOG_ALL(BLINKER_F("MQTT_ID_NBIoT: "), MQTT_ID_NBIoT);
      BLINKER_LOG_ALL(BLINKER_F("MQTT_NAME_NBIoT: "), MQTT_NAME_NBIoT);
      BLINKER_LOG_ALL(BLINKER_F("MQTT_KEY_NBIoT: "), MQTT_KEY_NBIoT);
      BLINKER_LOG_ALL(BLINKER_F("MQTT_BROKER: "), _broker);
      BLINKER_LOG_ALL(BLINKER_F("HOST: "), MQTT_HOST_NBIoT);
      BLINKER_LOG_ALL(BLINKER_F("PORT: "), MQTT_PORT_NBIoT);
      BLINKER_LOG_ALL(BLINKER_F("UUID_NBIoT: "), UUID_NBIoT);
      BLINKER_LOG_ALL(BLINKER_F("===================="));
  
      if (_broker == BLINKER_MQTT_BORKER_ALIYUN) {
          String PUB_TOPIC_STR = BLINKER_F("/");
          PUB_TOPIC_STR += MQTT_PRODUCTINFO_NBIoT;
          PUB_TOPIC_STR += BLINKER_F("/");
          PUB_TOPIC_STR += MQTT_ID_NBIoT;
          PUB_TOPIC_STR += BLINKER_F("/s");
  
          BLINKER_PUB_TOPIC_NBIoT = (char*)malloc((PUB_TOPIC_STR.length() + 1)*sizeof(char));
          // memcpy(BLINKER_PUB_TOPIC_NBIoT, PUB_TOPIC_STR.c_str(), str_len);
          strcpy(BLINKER_PUB_TOPIC_NBIoT, PUB_TOPIC_STR.c_str());
  
          BLINKER_LOG_ALL(BLINKER_F("BLINKER_PUB_TOPIC_NBIoT: "), BLINKER_PUB_TOPIC_NBIoT);
  
          String SUB_TOPIC_STR = BLINKER_F("/");
          SUB_TOPIC_STR += MQTT_PRODUCTINFO_NBIoT;
          SUB_TOPIC_STR += BLINKER_F("/");
          SUB_TOPIC_STR += MQTT_ID_NBIoT;
          SUB_TOPIC_STR += BLINKER_F("/r");
  
          BLINKER_SUB_TOPIC_NBIoT = (char*)malloc((SUB_TOPIC_STR.length() + 1)*sizeof(char));
          // memcpy(BLINKER_SUB_TOPIC_NBIoT, SUB_TOPIC_STR.c_str(), str_len);
          strcpy(BLINKER_SUB_TOPIC_NBIoT, SUB_TOPIC_STR.c_str());
  
          BLINKER_LOG_ALL(BLINKER_F("BLINKER_SUB_TOPIC_NBIoT: "), BLINKER_SUB_TOPIC_NBIoT);
      }
  
      // if (_broker == BLINKER_MQTT_BORKER_ALIYUN) {
          mqtt_NBIoT = new BlinkerMQTTSIM7020(*stream, isHWS, MQTT_HOST_NBIoT, MQTT_PORT_NBIoT, 
                                          MQTT_ID_NBIoT, MQTT_NAME_NBIoT, MQTT_KEY_NBIoT, listenFunc);
      // }
  
      this->latestTime = millis();
      isMQTTinit = true;
      mqtt_NBIoT->subscribe(BLINKER_SUB_TOPIC_NBIoT);
  
      return true;
  }
  
  void BlinkerQRCodeSIM7020::checkKA()
  {
      if (millis() - kaTime >= BLINKER_MQTT_KEEPALIVE)
          isAlive = false;
  }
  
  int BlinkerQRCodeSIM7020::checkAliKA() {
      if (millis() - aliKaTime >= 10000)
          return false;
      else
          return true;
  }
  
  int BlinkerQRCodeSIM7020::checkDuerKA() {
      if (millis() - duerKaTime >= 10000)
          return false;
      else
          return true;
  }
  
  int BlinkerQRCodeSIM7020::checkCanPrint() {
      if ((millis() - printTime >= BLINKER_PRO_MSG_LIMIT && isAlive) || printTime == 0) {
          return true;
      }
      else {
          BLINKER_ERR_LOG(BLINKER_F("MQTT NOT ALIVE OR MSG LIMIT"));
          
          checkKA();
  
          return false;
      }
  }
  
  int BlinkerQRCodeSIM7020::checkCanBprint() {
      if ((millis() - bPrintTime >= BLINKER_BRIDGE_MSG_LIMIT) || bPrintTime == 0) {
          return true;
      }
      else {
          BLINKER_ERR_LOG(BLINKER_F("MQTT NOT ALIVE OR MSG LIMIT"));
  
          return false;
      }
  }
  
  int BlinkerQRCodeSIM7020::checkPrintSpan()
  {
      if (millis() - respTime < BLINKER_PRINT_MSG_LIMIT)
      {
          if (respTimes > BLINKER_PRINT_MSG_LIMIT)
          {
              BLINKER_ERR_LOG(BLINKER_F("DEVICE NOT CONNECT OR MSG LIMIT"));
              
              return false;
          }
          else
          {
              respTimes++;
              return true;
          }
      }
      else
      {
          respTimes = 0;
          return true;
      }
  }
  
  int BlinkerQRCodeSIM7020::checkAliPrintSpan()
  {
      if (millis() - respAliTime < BLINKER_PRINT_MSG_LIMIT/2)
      {
          if (respAliTimes > BLINKER_PRINT_MSG_LIMIT/2)
          {
              BLINKER_ERR_LOG(BLINKER_F("ALIGENIE NOT ALIVE OR MSG LIMIT"));
  
              return false;
          }
          else
          {
              respAliTimes++;
              return true;
          }
      }
      else
      {
          respAliTimes = 0;
          return true;
      }
  }
  
  int BlinkerQRCodeSIM7020::checkDuerPrintSpan()
  {
      if (millis() - respDuerTime < BLINKER_PRINT_MSG_LIMIT/2)
      {
          if (respDuerTimes > BLINKER_PRINT_MSG_LIMIT/2)
          {
              BLINKER_ERR_LOG(BLINKER_F("DUEROS NOT ALIVE OR MSG LIMIT"));
  
              return false;
          }
          else
          {
              respDuerTimes++;
              return true;
          }
      }
      else
      {
          respDuerTimes = 0;
          return true;
      }
  }
  
  int BlinkerQRCodeSIM7020::isJson(const String & data)
  {
      BLINKER_LOG_ALL(BLINKER_F("isJson: "), data);
  
      // DynamicJsonBuffer jsonBuffer;
      // JsonObject& root = jsonBuffer.parseObject(data);
      DynamicJsonDocument jsonBuffer(1024);
      DeserializationError error = deserializeJson(jsonBuffer, data);
      JsonObject root = jsonBuffer.as<JsonObject>();
  
      // if (!root.success())
      if (error)
      {
          BLINKER_ERR_LOG("Print data is not Json! ", data);
          return false;
      }
      return true;
  }
  
  
  #endif
  