備忘錄_20160105(定位) 修改 回首頁

程式 2026-05-18 09:26:20 1779067580 100
lilygo t-sim7600g-h (此方案需要插入有網路可用的sim卡)

lilygo t-sim7600g-h (此方案需要插入有網路可用的sim卡)

gpio圖
4g sim 卡方案
盒裝實品照

取得訊息範例

2407.565181,N,12041.025767,E,170526,231638.0,44.4,11.6,266.2

  1. 北緯 24度07.565181分
  2. 東經 120度41.025767分
  3. 日期 2026年05月17日
  4. 時間 23點16分38.0秒
  5. 速度 44.4公里/小時
  6. 方向 11.6度 (朝北偏東 11.6°)
  7. 高度 海拔266.2公尺


#define iModemRx 26
#define iModemTx 27
#define iCharArrayMax 1024
#define iLed 12

#include <ctype.h>

HardwareSerial SerialAT(1);

char caATCmd[]="AT+CGPSINFO";
char caATKeyword[]="+CGPSINFO:";
char caPhp[]="cgpsinfo.php";

int iLedVal=0;

void qa(const char caQuestion[], char caAnswer[])
{
  caAnswer[0]='\0';

  while(SerialAT.available()) { SerialAT.read(); } // clear leftover data
  
  SerialAT.write(caQuestion);
  SerialAT.write("\r\n");
  
  delay(50);
  
  int iLen=0;
  unsigned long ulLast=millis();
  int iMaxMilliSecond=10000;
  
  // maybe multi lines
  while((millis()-ulLast)<iMaxMilliSecond)
  {
    while(SerialAT.available())
    {
      char c1=SerialAT.read();
      ulLast=millis();
      if(iLen<(iCharArrayMax-1))
      {
        caAnswer[iLen]=c1;
        iLen++;
        caAnswer[iLen]='\0';
      }
    }
    
    if(strstr(caAnswer, "\r\nOK\r\n") || strstr(caAnswer, "\r\nERROR\r\n")) { iMaxMilliSecond=300; }
    
    delay(1);
  }
}

bool getBooQA(const char caQuestion[], const char caKeyword[])
{
  static char caAnswerBuffer[iCharArrayMax];
  qa(caQuestion, caAnswerBuffer);
  
  if(strstr(caAnswerBuffer, caKeyword)!=NULL) { return true; }
  return false;
}

void setup()
{
  static char caAnswer[iCharArrayMax];

  pinMode(iLed, OUTPUT);
  digitalWrite(iLed, 1);

  Serial.begin(115200); // esp32
  
  SerialAT.begin(115200, SERIAL_8N1, iModemRx, iModemTx); // sim7600g-h
  pinMode(4, OUTPUT);
  digitalWrite(4, HIGH);
  delay(3000);
  while(SerialAT.available()) SerialAT.read();
  
  while(getBooQA("AT", "OK")==false) { delay(1000); } // wait until AT is OK
  
  // enable GPS via AT+CGPS=1
  qa("AT+CGPS=0", caAnswer);
  delay(3000);
  qa("AT+CGPS=1", caAnswer);
  
  // network registration
  int iRetry=0;
  while(getBooQA("AT+CGDCONT=1,\"IP\",\"internet\"", "\r\nOK\r\n")==false)
  {
    delay(1000); 
    iRetry++;
    if(iRetry>10) { break; }
  }
  
  if(iRetry>10)
  {
    digitalWrite(4, LOW);
    delay(500);
    digitalWrite(4, HIGH);
    delay(8000);
    
    ESP.restart();
  }
  
  digitalWrite(iLed, 0);
}

bool getBooExtractGPSDataOk(char caInput[], char caOutput[], int iOutputMax, const char caKeyword[])
{
  int iIdxInput=0;
  int iIdxOutput=0;
  bool booFound=false;

  caOutput[0]='\0';
  
  while(true)
  {
    if(caInput[iIdxInput]=='\0') { break; }
    if(strncmp(&caInput[iIdxInput], caKeyword, strlen(caKeyword))==0)
    {
      booFound=true;
      iIdxInput+=strlen(caKeyword);
      while(caInput[iIdxInput]==' ') { iIdxInput++; } // remove spaces
      break;
    }
    iIdxInput++;
    if(iIdxInput>=(iCharArrayMax-1)) { return false; }
  }
  if(booFound==false) { return false; }
  
  while(true)
  {
    if(caInput[iIdxInput]=='\0') { break; }
    if(caInput[iIdxInput]=='\r' || caInput[iIdxInput]=='\n') { break; }
    if(iIdxOutput<(iOutputMax-1))
    {
      caOutput[iIdxOutput]=caInput[iIdxInput];
      iIdxOutput++;
    }
    else
    {
      break;
    }
    iIdxInput++;
    if(iIdxInput>=iCharArrayMax) { break; }
  }
  caOutput[iIdxOutput]='\0';
  
  if(iIdxOutput<=28) { return false; }
  if(caOutput[11]!=',' || caOutput[13]!=',' || caOutput[26]!=',' || caOutput[28]!=',') { return false; }
  
  return true;
}

bool getBooSafeChar(char c1)
{
  if ((c1 >= 'a' && c1 <= 'z') ||
      (c1 >= 'A' && c1 <= 'Z') ||
      (c1 >= '0' && c1 <= '9'))
  {
    return true;
  }

  if (c1 == '-' || 
      c1 == '_' || 
      c1 == '.' || 
      c1 == '~')
  {
    return true;
  }

  return false;
}

void encodeURIComponent(const char caInput[], char caOutput[], int iOutputMax)
{
  static const char caHex[] = "0123456789ABCDEF";  
  
  int iIdxInput=0;
  int iIdxOutput=0;

  while(caInput[iIdxInput]!='\0' && iIdxOutput<(iOutputMax-1))
  {
    char c1=caInput[iIdxInput];
    
    if(c1=='\r' || c1=='\n')
    {
    }
    else if(getBooSafeChar(c1)==true)
    {
      caOutput[iIdxOutput]=c1;
      iIdxOutput++;
    }
    else
    {
      if((iIdxOutput+3)>=iOutputMax) { break; }
      
      caOutput[iIdxOutput+0] = '%';
      caOutput[iIdxOutput+1] = caHex[(c1 >> 4) & 0xF];
      caOutput[iIdxOutput+2] = caHex[c1 & 0xF];      
      
      iIdxOutput+=3;
    }
    
    iIdxInput++;
    if(iIdxInput>=iCharArrayMax) { break; }
  }
  
  caOutput[iIdxOutput]='\0';
}

void sendGpsData(const char caPhp[], const char caGpsData[])
{
  static char caEncodedGpsData[iCharArrayMax];
  static char caCommand[1024];
  caEncodedGpsData[0]='\0';
  caCommand[0]='\0';
  
  while(true)
  {
    if(getBooQA("AT+CPIN?", "+CPIN: READY")==false) { break; }
    if(getBooQA("AT+CGATT=1", "\r\nOK\r\n")==false) { break; }
    if(getBooQA("AT+CGATT?", "+CGATT: 1")==false) { break; }
    if(getBooQA("AT+HTTPINIT", "\r\nOK\r\n")==false) { break; }
    
    // parameters
    encodeURIComponent(caGpsData, caEncodedGpsData, iCharArrayMax);
    snprintf(
      caCommand,
      sizeof(caCommand),
      "AT+HTTPPARA=\"URL\",\"http://liujiaje.com/resources/20260513_lilygo_t_sim7600g_h/%s?id=%s&data=%s\"",
      caPhp, "lilygo1", caEncodedGpsData);
    if(getBooQA(caCommand, "\r\nOK\r\n")==false) { break; }
    
    if(getBooQA("AT+HTTPACTION=0", "+HTTPACTION:")==true)
    {
      delay(50);
      getBooQA("AT+HTTPREAD=0,200", "+HTTPREAD:"); // read 200 bytes
      delay(50);
    }
    
    int iRetry=0;
    while(getBooQA("AT+HTTPTERM", "\r\nOK\r\n")==false)
    {
      delay(1000); 
      iRetry++;
      if(iRetry>30)
      {
        // modem might be stuck
        digitalWrite(4, LOW);
        delay(500);
        digitalWrite(4, HIGH);
        delay(8000);
        break;
      }
    }
    
    break;
  }

}

void loop()
{
  char caAnswer[iCharArrayMax];
  char caGpsData[iCharArrayMax];
  
  iLedVal=(iLedVal+1) % 2;
  digitalWrite(iLed, iLedVal);
  
  while(true)
  {
    if(getBooQA("AT+CGPS?", "+CGPS: 1,1")==false) { break; }
    delay(10);
    
    qa(caATCmd, caAnswer);
    caAnswer[iCharArrayMax-1]='\0';
    
    if(getBooExtractGPSDataOk(caAnswer, caGpsData, sizeof(caGpsData), caATKeyword)==false) { break; }
    if(strlen(caGpsData)<=14) { break; }
    
    sendGpsData(caPhp, caGpsData);
    break;
  }
  
  delay(1000);
  
  /*
  while (Serial.available()) { SerialAT.write(Serial.read()); }
  while (SerialAT.available()) { Serial.write(SerialAT.read()); }
  */  
}