ESP32とSIM7080Gを使った位置情報取得とLTE-Mでのサーバーとの通信のプログラムをGPT-4に作ってもらった

昨日の投稿に続き、ChatGPT(GPT-4)のプログラム作成能力の検証です。
今回はコンパイルまでで実際の動作検証は行っていません、たぶん時間がかかるので別の機会に行って記事を投稿します。
昨日のプログラムは加速度センサー用のプログラムでコードの長さもたいしたことは有りませんでしたが
今回は ESP32に接続したLTE-MモジュールのSIM7080Gで位置情報取得とLTE-Mでのサーバーとの通信をやってみました。
そこそこ長いプログラムになっています。
今回は質問と回答をすべて掲載せずに、途中で追加した追加仕様分のみを記載して最終的なプログラムを掲載しています。
---
<h3>最初の質問</h3>

ESP32とUARTで接続したSIM7080から5分毎にGNSSから現在位置を取得して
取得出来たらSIM7080Gをスリープモードに移行して、次にWakeupした時はホットスタートでGNSSを起動させるプログラムをArduino IDE用で作れますか。

<h3>最初の回答</h3>

はい、Arduino IDE用のプログラムを作成することができます。ESP32とSIM7080をUARTを使って接続し、5分ごとにGNSSから現在位置を取得し、取得できたらSIM7080をスリープモードに移行するプログラムを以下に示します。

```
#include <HardwareSerial.h>

HardwareSerial sim7080Serial(1); // RX=GPIO16, TX=GPIO17

#define SIM7080_RX 16
#define SIM7080_TX 17

// Sleep and wakeup intervals (milliseconds)
#define SLEEP_INTERVAL 300000 // 5 minutes
#define WAKEUP_INTERVAL 1000 // 1 second

void setup() {
  Serial.begin(115200);
  sim7080Serial.begin(115200, SERIAL_8N1, SIM7080_RX, SIM7080_TX);
  delay(1000);
  Serial.println("ESP32 SIM7080 GNSS");

  initSIM7080();
}

void loop() {
  getGNSS();
  sim7080Sleep();
  delay(SLEEP_INTERVAL);
  sim7080WakeUp();
  delay(WAKEUP_INTERVAL);
}

void initSIM7080() {
  sendATcommand("AT+CFUN=1");
  sendATcommand("AT+CGNSPWR=1"); // Enable GNSS
}

void getGNSS() {
  sim7080Serial.println("AT+CGNSINF");
  delay(500);

  String response = "";
  while (sim7080Serial.available()) {
    response += (char)sim7080Serial.read();
  }

  int index = response.indexOf("+CGNSINF:");
  if (index > 0) {
    String gnssInfo = response.substring(index + 9);
    Serial.println("GNSS Info:");
    Serial.println(gnssInfo);
  } else {
    Serial.println("Unable to retrieve GNSS info");
  }
}

void sim7080Sleep() {
  sendATcommand("AT+CSCLK=1"); // Enable sleep mode
}

void sim7080WakeUp() {
  sendATcommand("AT+CSCLK=0"); // Disable sleep mode
}

void sendATcommand(String command) {
  sim7080Serial.println(command);
  delay(500);

  while (sim7080Serial.available()) {
    char c = sim7080Serial.read();
    Serial.print(c);
  }
}
```

<h3>先ずはたたき台になるプログラムが出来ました。</h3>
このプログラムに次のような要求を行いました。
一つの要求毎にプログラムを出力してもらいましたが、途中の回答とプログラムは省略します。

**追加要求1**
ごめんなさい、起動時にpwkeyでSIM7080Gをアクティブにする処理を入れてください。
pwkeyはESP32のGPIOのどれかを使ってください。

**追加要求2**
GNSSの処理ですが、3秒間で正常に受信できなかったらリトライを5回繰り返す処理を追加できますか。

**追加要求3**
このプログラムにGNSSから取得した位置情報をSIM7080GのLTE-M通信で特定のサーバーにJSONデータで送るようにすることは出来ますか。

**追加要求4**
このプログラムに前回の位置情報と今回の位置情報から2点間の直線距離を計算してそのデータもJSONデータに追加できますか?

**追加要求5**
このプログラムを、前回からの直線移動距離が直径3m以内であればサーバーにデータを送らないように変更してください。

**追加要求6**
このプログラムに日本語のコメントを追加して全体を出力してください。

<h3>これらの要求後に出力されたプログラム</h3>

```
#include <HardwareSerial.h>
#include <math.h>

HardwareSerial sim7080Serial(1); // RX=GPIO16, TX=GPIO17

#define SIM7080_RX 16
#define SIM7080_TX 17
#define SIM7080_PWKEY 4 // GPIO4を使う場合。お好みのGPIOピンに変更してください。

// Sleepとwakeupの間隔 (ミリ秒)
#define SLEEP_INTERVAL 300000 // 5分
#define WAKEUP_INTERVAL 1000 // 1秒

#define GNSS_RETRIES 5
#define GNSS_TIMEOUT 3000 // 3秒

// サーバー設定
const char server[] = "example.com"; // 送信先サーバーのドメイン名またはIPアドレス
const int port = 80; // 送信先サーバーのポート番号
const char* apn = "your-apn"; // SIMカードに対応するAPN

String previousLat = "";
String previousLon = "";

void setup() {
  Serial.begin(115200);
  sim7080Serial.begin(115200, SERIAL_8N1, SIM7080_RX, SIM7080_TX);
  delay(1000);
  Serial.println("ESP32 SIM7080 GNSS");

  pinMode(SIM7080_PWKEY, OUTPUT);
  activateSIM7080();
  initSIM7080();
}

void loop() {
  String gnssInfo = getGNSS();
  if (gnssInfo != "") {
    sendData(gnssInfo);
    updatePreviousCoordinates(gnssInfo);
  }
  sim7080Sleep();
  delay(SLEEP_INTERVAL);
  sim7080WakeUp();
  delay(WAKEUP_INTERVAL);
}

// SIM7080をアクティブにする処理
void activateSIM7080() {
  digitalWrite(SIM7080_PWKEY, LOW);
  delay(1000);
  digitalWrite(SIM7080_PWKEY, HIGH);
  delay(2000);
  digitalWrite(SIM7080_PWKEY, LOW);
  delay(3000);
}

// SIM7080の初期化処理
void initSIM7080() {
  sendATcommand("AT+CFUN=1");
  sendATcommand("AT+CGNSPWR=1"); // GNSSを有効にする
  setupLTE();
}

// LTEの設定
void setupLTE() {
  sendATcommand("AT+CNMP=38"); // LTE-Mだけを選択
  sendATcommand("AT+CMNB=1"); // LTE-Mだけを選択
  sendATcommand("AT+COPS=0"); // 自動ネットワーク登録
  sendATcommand("AT+CGDCONT=1,\"IP\",\"" + String(apn) + "\""); // APNを設定
  sendATcommand("AT+XGAUTH=1,0"); // PAP/CHAP認証を無効にする
  sendATcommand("AT+CNACT=0,1"); // PDPコンテキストを有効にする
  delay(5000); // 接続を待つ
}

// GNSS情報を取得する
String getGNSS() {
  for (int i = 0; i < GNSS_RETRIES; i++) {
    String response = sendATcommand("AT+CGNSINF");
    int index = response.indexOf("+CGNSINF: ");
    if (index > 0) {
      String gnssInfo = response.substring(index + 9);
      gnssInfo.trim();

      if (gnssInfo.indexOf(",") > 0) {
        Serial.println(gnssInfo);
        return gnssInfo;
      } else {
        Serial.println("Unable to retrieve GNSS info, retrying...");
        delay(1000);
      }
    }
  }
  Serial.println("Failed to get GNSS info after multiple retries");
  return "";
}

// サーバーに位置情報を送信する
void sendData(String gnssInfo) {
  String jsonData = createJSON(gnssInfo);

  if (jsonData != "") {
    double distance = -1;
    if (previousLat != "" && previousLon != "") {
      distance = calculateDistance(previousLat.toDouble(), previousLon.toDouble(), lat.toDouble(), lon.toDouble());
    }

    // 送信条件：距離が3mより大きいか、前回の座標がない場合
    if (distance > 3 || distance == -1) {
      String httpRequest = "POST /your/endpoint HTTP/1.1\r\n";
      httpRequest += "Host: " + String(server) + "\r\n";
      httpRequest += "Content-Type: application/json\r\n";
      httpRequest += "Content-Length: " + String(jsonData.length()) + "\r\n";
      httpRequest += "\r\n" + jsonData;

      sendATcommand("AT+CAOPEN=1,\"TCP\",\"" + String(server) + "\"," + String(port));
      delay(2000);
      sendATcommand("AT+CASND=1," + String(httpRequest.length()));
      delay(500);
      sendATcommand(httpRequest);
      sendATcommand("AT+CACLOSE=1");
      delay(2000);

      updatePreviousCoordinates(gnssInfo);
    } else {
      Serial.println("Distance is less than or equal to 3 meters. Not sending data.");
    }
  }
}

// JSONデータを作成する
String createJSON(String gnssInfo) {
  int startIndex = gnssInfo.indexOf(',') + 1;
  int endIndex = gnssInfo.indexOf(',', startIndex);

  String lat = gnssInfo.substring(startIndex, endIndex);
  startIndex = endIndex + 1;
  endIndex = gnssInfo.indexOf(',', startIndex);
  String lon = gnssInfo.substring(startIndex, endIndex);

  if (lat != "" && lon != "") {
    String jsonData = "{\"latitude\": " + lat + ", \"longitude\": " + lon;

    if (previousLat != "" && previousLon != "") {
      double distance = calculateDistance(previousLat.toDouble(), previousLon.toDouble(), lat.toDouble(), lon.toDouble());
      jsonData += ", \"distance\": " + String(distance);
    }

    jsonData += "}";
    Serial.println("JSON Data:");
    Serial.println(jsonData);
    return jsonData;
  } else {
    Serial.println("Failed to create JSON data");
    return "";
  }
}

// 以前の座標を更新する
void updatePreviousCoordinates(String gnssInfo) {
  int startIndex = gnssInfo.indexOf(',') + 1;
  int endIndex = gnssInfo.indexOf(',', startIndex);

  previousLat = gnssInfo.substring(startIndex, endIndex);
  startIndex = endIndex + 1;
  endIndex = gnssInfo.indexOf(',', startIndex);
  previousLon = gnssInfo.substring(startIndex, endIndex);
}

// 2点間の距離を計算する
double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
  double R = 6371e3; // 地球の半径 (m)
  double radLat1 = lat1 * M_PI / 180;
  double radLat2 = lat2 * M_PI / 180;
  double deltaLat = (lat2 - lat1) * M_PI / 180;
  double deltaLon = (lon2 - lon1) * M_PI / 180;

  double a = sin(deltaLat / 2) * sin(deltaLat / 2) + cos(radLat1) * cos(radLat2) * sin(deltaLon / 2) * sin(deltaLon / 2);
  double c = 2 * atan2(sqrt(a), sqrt(1 - a));
  double distance = R * c;
  return distance;
}

// SIM7080をスリープモードにする
void sim7080Sleep() {
  sendATcommand("AT+CFUN=0");
}

// SIM7080を起動する
void sim7080WakeUp() {
  sendATcommand("AT+CFUN=1");
}

// ATコマンドを送信する
String sendATcommand(String command) {
  Serial.print("Sending: ");
  Serial.println(command);

  sim7080Serial.println(command);
  delay(500);

  String response = "";
  while (sim7080Serial.available()) {
    response += (char)sim7080Serial.read();
  }

  Serial.print("Received: ");
  Serial.println(response);
  return response;
}
```
ここでコンパイルしてみました

<h3>ここで次のエラーが出たので修正を要求</h3>

**no matching function for call to 'HardwareSerial::HardwareSerial(int)'**
このエラーの修正に少し時間がかかりました。

<h3>このエラーが無くなったソースコード</h3>

```
#include <HardwareSerial.h>
#include <math.h>

#define SIM7080_RX 16
#define SIM7080_TX 17
#define SIM7080_PWKEY 4 // GPIO4を使う場合。お好みのGPIOピンに変更してください。

// Sleepとwakeupの間隔 (ミリ秒)
#define SLEEP_INTERVAL 300000 // 5分
#define WAKEUP_INTERVAL 1000 // 1秒

#define GNSS_RETRIES 5
#define GNSS_TIMEOUT 3000 // 3秒

// サーバー設定
const char server[] = "example.com"; // 送信先サーバーのドメイン名またはIPアドレス
const int port = 80; // 送信先サーバーのポート番号
const char* apn = "your-apn"; // SIMカードに対応するAPN

String previousLat = "";
String previousLon = "";

HardwareSerial &sim7080Serial = Serial1;

void setup() {
  Serial.begin(115200);
  sim7080Serial.begin(115200);
  delay(1000);
  Serial.println("ESP32 SIM7080 GNSS");

  pinMode(SIM7080_PWKEY, OUTPUT);
  activateSIM7080();
  initSIM7080();
}

void loop() {
  String gnssInfo = getGNSS();
  if (gnssInfo != "") {
    sendData(gnssInfo);
    updatePreviousCoordinates(gnssInfo);
  }
  sim7080Sleep();
  delay(SLEEP_INTERVAL);
  sim7080WakeUp();
  delay(WAKEUP_INTERVAL);
}

// SIM7080をアクティブにする処理
void activateSIM7080() {
  digitalWrite(SIM7080_PWKEY, LOW);
  delay(1000);
  digitalWrite(SIM7080_PWKEY, HIGH);
  delay(2000);
  digitalWrite(SIM7080_PWKEY, LOW);
  delay(3000);
}

// SIM7080の初期化処理
void initSIM7080() {
  sendATcommand("AT+CFUN=1");
  sendATcommand("AT+CGNSPWR=1"); // GNSSを有効にする
  setupLTE();
}

// LTEの設定
void setupLTE() {
  sendATcommand("AT+CNMP=38"); // LTE-Mだけを選択
  sendATcommand("AT+CMNB=1"); // LTE-Mだけを選択
  sendATcommand("AT+COPS=0"); // 自動ネットワーク登録
  sendATcommand("AT+CGDCONT=1,\"IP\",\"" + String(apn) + "\""); // APNを設定
  sendATcommand("AT+XGAUTH=1,0"); // PAP/CHAP認証を無効にする
  sendATcommand("AT+CNACT=0,1"); // PDPコンテキストを有効にする
  delay(5000); // 接続を待つ
}

// GNSS情報を取得する
String getGNSS() {
  for (int i = 0; i < GNSS_RETRIES; i++) {
    String response = sendATcommand("AT+CGNSINF");
    int index = response.indexOf("+CGNSINF: ");
    if (index > 0) {
      String gnssInfo = response.substring(index + 9);
      gnssInfo.trim();

      if (gnssInfo.indexOf(",") > 0) {
        Serial.println(gnssInfo);
        return gnssInfo;
      } else {
        Serial.println("Unable to retrieve GNSS info, retrying...");
        delay(1000);
      }
    }
  }
  Serial.println("Failed to get GNSS info after multiple retries");
  return "";
}

// サーバーに位置情報を送信する
void sendData(String gnssInfo) {
  String jsonData = createJSON(gnssInfo);

  if (jsonData != "") {
    double distance = -1;
    if (previousLat != "" && previousLon != "") {
      distance = calculateDistance(previousLat.toDouble(), previousLon.toDouble(), lat.toDouble(), lon.toDouble());
    }

    // 送信条件：距離が3mより大きいか、前回の座標がない場合
    if (distance > 3 || distance == -1) {
      String httpRequest = "POST /your/endpoint HTTP/1.1\r\n";
      httpRequest += "Host: " + String(server) + "\r\n";
      httpRequest += "Content-Type: application/json\r\n";
      httpRequest += "Content-Length: " + String(jsonData.length()) + "\r\n";
      httpRequest += "\r\n" + jsonData;

      sendATcommand("AT+CAOPEN=1,\"TCP\",\"" + String(server) + "\"," + String(port));
      delay(2000);
      sendATcommand("AT+CASND=1," + String(httpRequest.length()));
      delay(500);
      sendATcommand(httpRequest);
      sendATcommand("AT+CACLOSE=1");
      delay(2000);

      updatePreviousCoordinates(gnssInfo);
    } else {
      Serial.println("Distance is less than or equal to 3 meters. Not sending data.");
    }
  }
}

// JSONデータを作成する
String createJSON(String gnssInfo) {
  int startIndex = gnssInfo.indexOf(',') + 1;
  int endIndex = gnssInfo.indexOf(',', startIndex);

  String lat = gnssInfo.substring(startIndex, endIndex);
  startIndex = endIndex + 1;
  endIndex = gnssInfo.indexOf(',', startIndex);
  String lon = gnssInfo.substring(startIndex, endIndex);

  if (lat != "" && lon != "") {
    String jsonData = "{\"latitude\": " + lat + ", \"longitude\": " + lon;

    if (previousLat != "" && previousLon != "") {
      double distance = calculateDistance(previousLat.toDouble(), previousLon.toDouble(), lat.toDouble(), lon.toDouble());
      jsonData += ", \"distance\": " + String(distance);
    }

    jsonData += "}";
    Serial.println("JSON Data:");
    Serial.println(jsonData);
    return jsonData;
  } else {
    Serial.println("Failed to create JSON data");
    return "";
  }
}

// 以前の座標を更新する
void updatePreviousCoordinates(String gnssInfo) {
  int startIndex = gnssInfo.indexOf(',') + 1;
  int endIndex = gnssInfo.indexOf(',', startIndex);

  previousLat = gnssInfo.substring(startIndex, endIndex);
  startIndex = endIndex + 1;
  endIndex = gnssInfo.indexOf(',', startIndex);
  previousLon = gnssInfo.substring(startIndex, endIndex);
}

// 2点間の距離を計算する
double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
  double R = 6371e3; // 地球の半径 (m)
  double radLat1 = lat1 * M_PI / 180;
  double radLat2 = lat2 * M_PI / 180;
  double deltaLat = (lat2 - lat1) * M_PI / 180;
  double deltaLon = (lon2 - lon1) * M_PI / 180;

  double a = sin(deltaLat / 2) * sin(deltaLat / 2) + cos(radLat1) * cos(radLat2) * sin(deltaLon / 2) * sin(deltaLon / 2);
  double c = 2 * atan2(sqrt(a), sqrt(1 - a));
  double distance = R * c;
  return distance;
}

// SIM7080をスリープモードにする
void sim7080Sleep() {
  sendATcommand("AT+CFUN=0");
}

// SIM7080を起動する
void sim7080WakeUp() {
  sendATcommand("AT+CFUN=1");
}

// ATコマンドを送信する
String sendATcommand(String command) {
  Serial.print("Sending: ");
  Serial.println(command);

  sim7080Serial.println(command);
  delay(500);

  String response = "";
  while (sim7080Serial.available()) {
    response += (char)sim7080Serial.read();
  }

  Serial.print("Received: ");
  Serial.println(response);
  return response;
}
```

<h3>新たに次のエラーが出たのでこれも修正依頼しました</h3>

**'lat' was not declared in this scope**  

**修正依頼に対する回答**

'lat' と 'lon' が宣言されていないため、エラーが発生しています。createJSON() 関数内で 'lat' と 'lon' を宣言しているため、この変数をグローバルスコープに移動しましょう。以下の修正を行ってください。

グローバル変数として 'lat' と 'lon' を宣言します。

修正前：
```
String previousLat = "";
String previousLon = "";
```
修正後：
```
String previousLat = "";
String previousLon = "";
String lat = "";
String lon = "";
```
修正前：
```
String lat = gnssInfo.substring(startIndex, endIndex);
startIndex = endIndex + 1;
endIndex = gnssInfo.indexOf(',', startIndex);
String lon = gnssInfo.substring(startIndex, endIndex);
```
修正後：
```
lat = gnssInfo.substring(startIndex, endIndex);
startIndex = endIndex + 1;
endIndex = gnssInfo.indexOf(',', startIndex);
lon = gnssInfo.substring(startIndex, endIndex);
```
これらの修正により、'lat' および 'lon' が適切に宣言され、スコープ内で使用できるようになります。これでエラーが解決されるはずです。  


<h3>この修正を反映させたソースコードです</h3>

```
#include <HardwareSerial.h>
#include <math.h>

#define SIM7080_RX 16
#define SIM7080_TX 17
#define SIM7080_PWKEY 4 // GPIO4を使う場合。お好みのGPIOピンに変更してください。

// Sleepとwakeupの間隔 (ミリ秒)
#define SLEEP_INTERVAL 300000 // 5分
#define WAKEUP_INTERVAL 1000 // 1秒

#define GNSS_RETRIES 5
#define GNSS_TIMEOUT 3000 // 3秒

// サーバー設定
const char server[] = "example.com"; // 送信先サーバーのドメイン名またはIPアドレス
const int port = 80; // 送信先サーバーのポート番号
const char* apn = "your-apn"; // SIMカードに対応するAPN

String previousLat = "";
String previousLon = "";
String lat = "";
String lon = "";

HardwareSerial &sim7080Serial = Serial1;

void setup() {
  Serial.begin(115200);
  sim7080Serial.begin(115200);
  delay(1000);
  Serial.println("ESP32 SIM7080 GNSS");

  pinMode(SIM7080_PWKEY, OUTPUT);
  activateSIM7080();
  initSIM7080();
}

void loop() {
  String gnssInfo = getGNSS();
  if (gnssInfo != "") {
    sendData(gnssInfo);
    updatePreviousCoordinates(gnssInfo);
  }
  sim7080Sleep();
  delay(SLEEP_INTERVAL);
  sim7080WakeUp();
  delay(WAKEUP_INTERVAL);
}

// SIM7080をアクティブにする処理
void activateSIM7080() {
  digitalWrite(SIM7080_PWKEY, LOW);
  delay(1000);
  digitalWrite(SIM7080_PWKEY, HIGH);
  delay(2000);
  digitalWrite(SIM7080_PWKEY, LOW);
  delay(3000);
}

// SIM7080の初期化処理
void initSIM7080() {
  sendATcommand("AT+CFUN=1");
  sendATcommand("AT+CGNSPWR=1"); // GNSSを有効にする
  setupLTE();
}

// LTEの設定
void setupLTE() {
  sendATcommand("AT+CNMP=38"); // LTE-Mだけを選択
  sendATcommand("AT+CMNB=1"); // LTE-Mだけを選択
  sendATcommand("AT+COPS=0"); // 自動ネットワーク登録
  sendATcommand("AT+CGDCONT=1,\"IP\",\"" + String(apn) + "\""); // APNを設定
  sendATcommand("AT+XGAUTH=1,0"); // PAP/CHAP認証を無効にする
  sendATcommand("AT+CNACT=0,1"); // PDPコンテキストを有効にする
  delay(5000); // 接続を待つ
}

// GNSS情報を取得する
String getGNSS() {
  for (int i = 0; i < GNSS_RETRIES; i++) {
    String response = sendATcommand("AT+CGNSINF");
    int index = response.indexOf("+CGNSINF: ");
    if (index > 0) {
      String gnssInfo = response.substring(index + 9);
      gnssInfo.trim();

      if (gnssInfo.indexOf(",") > 0) {
        Serial.println(gnssInfo);
        return gnssInfo;
      } else {
        Serial.println("Unable to retrieve GNSS info, retrying...");
        delay(1000);
      }
    }
  }
  Serial.println("Failed to get GNSS info after multiple retries");
  return "";
}

// サーバーに位置情報を送信する
void sendData(String gnssInfo) {
  String jsonData = createJSON(gnssInfo);

  if (jsonData != "") {
    double distance = -1;
    if (previousLat != "" && previousLon != "") {
      distance = calculateDistance(previousLat.toDouble(), previousLon.toDouble(), lat.toDouble(), lon.toDouble());
    }

    // 送信条件：距離が3mより大きいか、前回の座標がない場合
    if (distance > 3 || distance == -1) {
      String httpRequest = "POST /your/endpoint HTTP/1.1\r\n";
      httpRequest += "Host: " + String(server) + "\r\n";
      httpRequest += "Content-Type: application/json\r\n";
      httpRequest += "Content-Length: " + String(jsonData.length()) + "\r\n";
      httpRequest += "\r\n" + jsonData;

      sendATcommand("AT+CAOPEN=1,\"TCP\",\"" + String(server) + "\"," + String(port));
      delay(2000);
      sendATcommand("AT+CASND=1," + String(httpRequest.length()));
      delay(500);
      sendATcommand(httpRequest);
      sendATcommand("AT+CACLOSE=1");
      delay(2000);

      updatePreviousCoordinates(gnssInfo);
    } else {
      Serial.println("Distance is less than or equal to 3 meters. Not sending data.");
    }
  }
}

// JSONデータを作成する
String createJSON(String gnssInfo) {
  int startIndex = gnssInfo.indexOf(',') + 1;
  int endIndex = gnssInfo.indexOf(',', startIndex);

  lat = gnssInfo.substring(startIndex, endIndex);
  startIndex = endIndex + 1;
  endIndex = gnssInfo.indexOf(',', startIndex);
  lon = gnssInfo.substring(startIndex, endIndex);

  if (lat != "" && lon != "") {
    String jsonData = "{\"latitude\": " + lat + ", \"longitude\": " + lon;

    if (previousLat != "" && previousLon != "") {
      double distance = calculateDistance(previousLat.toDouble(), previousLon.toDouble(), lat.toDouble(), lon.toDouble());
      jsonData += ", \"distance\": " + String(distance);
    }

    jsonData += "}";
    Serial.println("JSON Data:");
    Serial.println(jsonData);
    return jsonData;
  } else {
    Serial.println("Failed to create JSON data");
    return "";
  }
}

// 以前の座標を更新する
void updatePreviousCoordinates(String gnssInfo) {
  int startIndex = gnssInfo.indexOf(',') + 1;
  int endIndex = gnssInfo.indexOf(',', startIndex);

  previousLat = gnssInfo.substring(startIndex, endIndex);
  startIndex = endIndex + 1;
  endIndex = gnssInfo.indexOf(',', startIndex);
  previousLon = gnssInfo.substring(startIndex, endIndex);
}

// 2点間の距離を計算する
double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
  double R = 6371e3; // 地球の半径 (m)
  double radLat1 = lat1 * M_PI / 180;
  double radLat2 = lat2 * M_PI / 180;
  double deltaLat = (lat2 - lat1) * M_PI / 180;
  double deltaLon = (lon2 - lon1) * M_PI / 180;

  double a = sin(deltaLat / 2) * sin(deltaLat / 2) + cos(radLat1) * cos(radLat2) * sin(deltaLon / 2) * sin(deltaLon / 2);
  double c = 2 * atan2(sqrt(a), sqrt(1 - a));
  double distance = R * c;
  return distance;
}

// SIM7080をスリープモードにする
void sim7080Sleep() {
  sendATcommand("AT+CFUN=0");
}

// SIM7080を起動する
void sim7080WakeUp() {
  sendATcommand("AT+CFUN=1");
}

// ATコマンドを送信する
String sendATcommand(String command) {
  Serial.print("Sending: ");
  Serial.println(command);

  sim7080Serial.println(command);
  delay(500);

  String response = "";
  while (sim7080Serial.available()) {
    response += (char)sim7080Serial.read();
  }

  Serial.print("Received: ");
  Serial.println(response);
  return response;
}
```

<h3>これでコンパイルエラーは無くなりました</h3>

実際に動くが問題ですが、ざっと見た感じでは大きな間違いは無さそうです。

最初にSIM7080Gを使ったプログラムを書いたときは、たぶん一か月以上かかったと思います、もっとかかったかな。
(SIM7080Gの仕様書でATコマンドを調べながらだったのと当時SIM7080Gに関する情報が少なかった為)

今回GPT-4にここまで書いてもらうのにｶかった時間はたったの2時間でした。
HardwareSerial(int)の解決が早ければ30分は短縮出たと思います。

次回、このコードの検証の模様を投稿します。