0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

今更ながらAntigravity使ってみた。せっかくなのでESP-IDFの環境構築から丸投げしてみる

Posted at

はじめに

GoogleのAI統合開発環境(Project IDX や Antigravityなどと呼ばれる一連のプロジェクト)、以前から気になっていたので触ってみました。

Webアプリのボイラープレートを生成するだけでは面白みに欠けるため、今回はあえて 「環境構築の複雑さ」に定評のある組み込み開発 をテーマに選びました。ターゲットは ESP32 (ESP-IDF) です。

Renesas, STM32, NXP, そしてEspressif。マイコンごとに異なるツールチェーン、パス設定、依存ライブラリのバージョン整合性……。これらは初学者の心を折る最初の障壁であり、ベテランにとっても「生産性を生まない時間」です。

今回は、この厄介な環境構築から実装までを、GoogleのAIエージェントに「丸投げ」した場合、どこまで通用するのかを検証しました。

1. 検証環境とゴール

  • Host: Windows (PowerShell)
  • Target: ESP32 (DevKit)
  • Tool: Google Antigravity (AI Agent)
  • Goal:
  1. ESP-IDF v5.3 のビルド環境をコマンドラインベースで構築する。
  2. Deep SleepとWi-Fi送信を行うアプリケーションを実装する。
  3. 実機で動作させ、PC上のサーバーでデータを受信する。

2. 環境構築の自律化 (Phase 1)

通常、ESP-IDFのセットアップには公式インストーラーを使用するか、ドキュメントを読みながらGit, Python, CMake, Ninjaなどを順に導入し、環境変数を設定する手順が必要です。

今回はターミナル上のエージェントに対し、自然言語で以下の指示を出しました。

User Request:
Windows環境 (PowerShell) で作業しています。ESP32 (ESP-IDF v5.3) の開発ができるように、環境構築をゼロから行ってください。
必要なツール (Git, Python, Ninja等) がなければインストールし、最後にバージョン確認まで行ってください。

エージェントの挙動

エージェントは即座にシェルコマンドの実行を開始しました。

1. 依存ツールの欠如検知とインストール

まず、ビルドに必要なツール群の存在確認が行われます。

# [STEP 1] SYSTEM: (Checking required tools)
COMMAND: git --version; python --version; cmake --version; ninja --version
OUTPUT: 
git version 2.46.0...
Python 3.10.11
ninja: The term 'ninja' is not recognized... 

ここで ninja(ビルドシステム)が未インストールであることを検知すると、ユーザーに許可を求めることもなく、自律的にインストールコマンドを発行しました。

# [STEP 2] SYSTEM: (Installing Ninja)
COMMAND: pip install ninja
OUTPUT: Successfully installed ninja-1.13.0

2. ツールチェーンのセットアップ

続いて、ESP-IDF本体の git clone、インストールスクリプト install.bat の実行へと進みます。数分後、パス設定スクリプトの実行まで完了しました。

# [STEP 5] SYSTEM: (Verifying installation)
COMMAND: . C:\esp\esp-idf\export.ps1; idf.py --version
OUTPUT: ESP-IDF v5.3

特筆すべきは、私が一度もブラウザで「ESP-IDF install guide」を検索していないという点です。泥臭い環境構築作業が、チャット一言で完結しました。

3. 実装と「自己修復機能」 (Phase 2)

環境が整ったため、アプリケーションの実装を指示しました。

仕様:

  • Deep Sleepから復帰する。
  • ダミーのセンサー値をRTCメモリ(Deep Sleep中も保持される領域)に蓄積する。
  • データが10個溜まった時点でWi-Fiに接続し、HTTP POSTでサーバーへ送信する。
  • 送信完了後、再びSleepに入る。

エージェントは main.cCMakeLists.txt を生成しましたが、初回のビルド実行時にエラーが発生しました。ここからの挙動が今回のハイライトです。

ビルドエラーと自律修正

# [STEP 60] 初回ビルド
COMMAND: idf.py build
OUTPUT: FAILED (CMake Error) ... Component `cjson` was requested but not found.

JSONライブラリの指定において、コンポーネント名の不一致(cjson vs json)が発生しました。通常であれば人間がエラーログを読み解き修正するところですが、エージェントはエラー出力を解析し、即座に修正案を適用しました。

# [STEP 85] SYSTEM: (Diagnosis and Fix)
Diagnosis: CMake failed. Component `cjson` was requested but not found.
Fix: Updated `main/CMakeLists.txt` to require `json` instead of `cjson`.

# [STEP 97] 再ビルド
COMMAND: idf.py build
OUTPUT: Project build complete.

この「自己修復(Self-Healing)」プロセスにより、人間はエラーが発生したことすら意識せずにビルド成功まで到達しました。

最終的に作られたコードが以下になります

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_sleep.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_http_client.h"
#include "cJSON.h"

// Configuration
#define EXAMPLE_ESP_WIFI_SSID      "YOUR_SSID"
#define EXAMPLE_ESP_WIFI_PASS      "YOUR_PASSWORD"
#define TARGET_URL                 "http://example.com/api/data"
#define DEEP_SLEEP_DURATION_SEC    5
// Use ADC1 Channel 6 (GPIO34 on many ESP32 boards, check your specific board pinout!)
#define ADC_UNIT                   ADC_UNIT_1
#define ADC_CHANNEL                ADC_CHANNEL_6 

// Global tags and objects
static const char *TAG = "DEEP_SLEEP_TEST";

// RTC Memory Storage (Preserved in Deep Sleep)
// Note: RTC_DATA_ATTR puts this variable in RTC Slow Memory
typedef struct {
    int samples[10];
    int count;
} rtc_store_t;

RTC_DATA_ATTR static rtc_store_t s_rtc_data;

// Event Group for Wi-Fi
static EventGroupHandle_t s_wifi_event_group;
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT      BIT1

// -------------------------------------------------------------------------
// Wi-Fi Handling
// -------------------------------------------------------------------------
static void event_handler(void* arg, esp_event_base_t event_base,
                                int32_t event_id, void* event_data) {
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        esp_wifi_connect();
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        // Simple retry logic could be added here, but for this deep sleep example
        // we might just want to fail quickly to save power.
        // For robustness, let's try a few times or set FAIL bit.
        xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
        ESP_LOGE(TAG, "Failed to connect to AP");
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

static bool wifi_init_sta(void) {
    s_wifi_event_group = xEventGroupCreate();

    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    esp_event_handler_instance_t instance_any_id;
    esp_event_handler_instance_t instance_got_ip;
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_any_id));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_got_ip));

    wifi_config_t wifi_config = {
        .sta = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .password = EXAMPLE_ESP_WIFI_PASS,
            // Setting a password implies security.
            .threshold.authmode = WIFI_AUTH_WPA2_PSK,
            .sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "wifi_init_sta finished. Waiting for connection...");

    /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
     * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
            WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
            pdFALSE,
            pdFALSE,
            pdMS_TO_TICKS(10000)); // 10 second timeout

    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG, "connected to ap SSID:%s", EXAMPLE_ESP_WIFI_SSID);
        return true;
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGI(TAG, "Failed to connect to SSID:%s", EXAMPLE_ESP_WIFI_SSID);
        return false;
    } else {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
        return false;
    }
}

static void wifi_stop_cleanup(void) {
    esp_wifi_stop();
    esp_wifi_deinit();
    // In a real deep sleep loop, we might not need to clean up netif/event loop carefully
    // because the chip will reset, but it's good practice for non-reset sleep.
}

// -------------------------------------------------------------------------
// HTTP Client Handling
// -------------------------------------------------------------------------
static void wifi_post_data(void) {
    ESP_LOGI(TAG, "Preparing to POST data...");

    // Create JSON
    cJSON *root = cJSON_CreateObject();
    cJSON *array = cJSON_CreateArray();
    cJSON_AddItemToObject(root, "samples", array);

    for (int i = 0; i < s_rtc_data.count; i++) {
        cJSON_AddItemToArray(array, cJSON_CreateNumber(s_rtc_data.samples[i]));
    }
    
    char *json_str = cJSON_PrintUnformatted(root);
    ESP_LOGI(TAG, "JSON Payload: %s", json_str);

    esp_http_client_config_t config = {
        .url = TARGET_URL,
    };
    esp_http_client_handle_t client = esp_http_client_init(&config);

    esp_http_client_set_method(client, HTTP_METHOD_POST);
    esp_http_client_set_header(client, "Content-Type", "application/json");
    esp_http_client_set_post_field(client, json_str, strlen(json_str));

    esp_err_t err = esp_http_client_perform(client);
    if (err == ESP_OK) {
        ESP_LOGI(TAG, "HTTP POST Status = %d, content_length = %lld",
                (int)esp_http_client_get_status_code(client),
                esp_http_client_get_content_length(client));
    } else {
        ESP_LOGE(TAG, "HTTP POST request failed: %s", esp_err_to_name(err));
    }

    esp_http_client_cleanup(client);
    cJSON_Delete(root);
    free(json_str);
}

// -------------------------------------------------------------------------
// ADC Handling
// -------------------------------------------------------------------------
static int read_adc_val(void) {
    adc_oneshot_unit_handle_t adc1_handle;
    adc_oneshot_unit_init_cfg_t init_config1 = {
        .unit_id = ADC_UNIT,
    };
    ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config1, &adc1_handle));

    adc_oneshot_chan_cfg_t config = {
        .bitwidth = ADC_BITWIDTH_DEFAULT,
        .atten = ADC_ATTEN_DB_12,
    };
    ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, ADC_CHANNEL, &config));

    int adc_raw;
    ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, ADC_CHANNEL, &adc_raw));
    ESP_LOGI(TAG, "ADC Raw: %d", adc_raw);

    ESP_ERROR_CHECK(adc_oneshot_del_unit(adc1_handle));
    return adc_raw;
}

// -------------------------------------------------------------------------
// Main
// -------------------------------------------------------------------------
void app_main(void) {
    // Determine wake cause
    esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
    if (cause != ESP_SLEEP_WAKEUP_TIMER) {
        ESP_LOGI(TAG, "Not a timer wakeup (Power On Reset?), initializing storage.");
        s_rtc_data.count = 0;
    } else {
        ESP_LOGI(TAG, "Woke up from Deep Sleep.");
    }

    // Read Sensor
    int val = read_adc_val();

    // Store Data
    if (s_rtc_data.count < 10) {
        s_rtc_data.samples[s_rtc_data.count] = val;
        s_rtc_data.count++;
        ESP_LOGI(TAG, "Stored sample %d. Total count: %d", val, s_rtc_data.count);
    }

    // Check if we need to transmit
    if (s_rtc_data.count >= 10) {
        ESP_LOGI(TAG, "Buffer full (10 samples). Starting Wi-Fi...");
        
        // Initialize NVS (required for Wi-Fi)
        esp_err_t ret = nvs_flash_init();
        if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
          ESP_ERROR_CHECK(nvs_flash_erase());
          ret = nvs_flash_init();
        }
        ESP_ERROR_CHECK(ret);

        if (wifi_init_sta()) {
            wifi_post_data();
            // Reset counter after successful (or attempted) send
            // Whether to reset on fail is a design choice. Here we resets on attempt to avoid stuck loop.
            s_rtc_data.count = 0; 
            
            // Clean up to save power before sleeping? 
            // Deep sleep resets everything anyway, so strict cleanup is optional but good practice.
            wifi_stop_cleanup();
        } else {
            ESP_LOGE(TAG, "Wi-Fi connection failed. Skipping upload.");
            // Keep data? Or reset? Let's keep it to try again next time? 
            // But if we keep it, we might just loop failing Wi-Fi.
            // Let's keep at 10 and try again next loop? Or simple: just reset.
            // Requirement says "Data transmission done... OR data < 10 ... go to sleep".
            // Let's reset count to avoid getting stuck if Wi-Fi permanently fails, 
            // or we could keep trying. Let's just reset for simple demo flow.
             s_rtc_data.count = 0; 
        }
    }

    // Deep Sleep
    ESP_LOGI(TAG, "Entering Deep Sleep for %d seconds...", DEEP_SLEEP_DURATION_SEC);
    // Needed to ensure log is flushed
    fflush(stdout); 
    
    // Set wakeup time
    ESP_ERROR_CHECK(esp_sleep_enable_timer_wakeup(DEEP_SLEEP_DURATION_SEC * 1000000));
    esp_deep_sleep_start();
}

実装上の注意点(技術メモ)

※ Wi-Fi設定は環境依存が強く、WPA2/WPA3の組み合わせやルータ設定によっては接続に失敗する場合があります。実運用では menuconfig での設定や接続条件の整理が必要です。
※ 本サンプルでは Deep Sleep 復帰がリセット相当のため、厳密なリソース解放は省略しています(Wi-Fi停止のみ実施)。

ESP-IDF / Wi-Fi / Deep Sleep 実装上の注意点

本サンプルは Deep Sleep を前提としているため、起床時には事実上「リセット」が発生します。そのため、esp_event_loop_create_default()esp_netif_init() といった初期化関数を毎回呼び出しても問題になりません。

ただし、Light Sleep やアプリケーションを継続動作させる設計では、これらの API を複数回呼ぶと ESP_ERR_INVALID_STATE を返す場合があります。
実運用コードでは、初期化済みかどうかの状態管理が必要になる点には注意が必要です。

今回は「Deep Sleep 前提の単純なループ」という条件が成立しているため、AI が生成した初期化コードでも破綻しませんでした。

RTC_DATA_ATTR の信頼性について

RTC_DATA_ATTR を用いた変数は Deep Sleep 中も保持されますが、これは「完全に安全な永続領域」ではありません。

以下の条件ではデータが失われる可能性があります。

  • 電源の完全断(USB 抜去、バッテリー交換など)
  • ブラウンアウト検出によるリセット
  • チップファミリ差異(ESP32 / ESP32-S3 / C3 など)

今回の検証では問題になりませんが、製品設計では
NVS(Flash)との併用データ破損検出用のマジックナンバー を組み込むことが一般的です。

AI が生成したコードが「どの前提条件で成立しているか」を理解していないと、ここがブラックボックスになり得る点は注意が必要です。

ADC 初期化のオーバーヘッドについて

本コードでは、Deep Sleep 復帰ごとに ADC を初期化・解放しています。

  • adc_oneshot_new_unit();
  • adc_oneshot_del_unit();

動作上は問題ありませんが、起動時間や消費電力を最適化する場合には改善余地があります。

実運用では、

  • ADC ハンドルを静的に保持する
  • ULP(Ultra Low Power Coprocessor)でサンプリングする

といった設計が検討対象になります。

今回は「動作検証」と「AI の実装能力評価」を優先しており、シンプルさを重視した構成としています。

4. 実機での動作検証

ビルドされたバイナリをESP32に書き込み、動作を確認します。
受信側として、Python標準ライブラリのみを使用した簡易サーバーを用意しました。

テスト用サーバー (server.py)

from http.server import HTTPServer, BaseHTTPRequestHandler
import sys

HOST = "0.0.0.0"
PORT = 8000

class SimplePostHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        content_length = int(self.headers['Content-Length'])
        post_data = self.rfile.read(content_length)
        
        print("\n--- 受信データ (POST) ---")
        print(post_data.decode('utf-8'))
        print("-------------------------\n")
        
        self.send_response(200)
        self.end_headers()

if __name__ == "__main__":
    server = HTTPServer((HOST, PORT), SimplePostHandler)
    print(f"サーバー起動: http://{HOST}:{PORT}")
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        sys.exit()

実行結果

PCのファイアウォール設定(ポート8000の許可)を手動で行った後、ESP32を起動しました。
ログからは、Deep SleepとWakeupを繰り返し、バッファが満たされたタイミングでWi-Fi接続とデータ送信が行われていることが確認できます。

ESP32側のログ:

I (595) DEEP_SLEEP_TEST: Woke up from Deep Sleep.
I (595) DEEP_SLEEP_TEST: Stored sample 0. Total count: 10
I (605) DEEP_SLEEP_TEST: Buffer full. Starting Wi-Fi...
...
I (6145) DEEP_SLEEP_TEST: connected to ap SSID:aterm-xxxx
I (6155) DEEP_SLEEP_TEST: Preparing to POST data...

サーバー側のログ:

サーバー起動: http://0.0.0.0:8000

--- 受信データ (POST) ---
{"samples":[0,0,0,0,0,0,0,0,0,0]}
-------------------------

192.168.0.39 - - [28/Dec/2025 13:36:32] "POST / HTTP/1.1" 200 -

HTTP 通信の堅牢性について(あえてのツッコミ)

AI が生成した通信処理は「動作する最短経路」を選んでいますが、エンジニア視点ではいくつか改善点が見えます。

  • HTTPS(TLS)ではなく HTTP(平文通信)
  • 通信タイムアウト設定がない
  • リトライ戦略がない
  • POST 成否に関わらずデータをリセットしている

今回はローカルネットワーク内の検証用途のため問題になりませんが、実運用では致命的になり得るポイントです。

ここにこそ「AI が書いたコードをそのまま信じてはいけない理由」が現れています。
AI は 正しさ ではなく 成功率 を最大化するため、人間によるレビューと設計判断は依然として不可欠です。

5. 考察:AI時代の組み込みエンジニアに求められるもの

Antigravity / Project IDX に対する技術者の評価

※ ここでいう「評価」は、公式発表というより、実際に触った開発者の投稿やコミュニティ上の反応を観察した印象に基づきます。

現時点での Antigravity(Project IDX 上の AI Agent 群)に対する評価は、概ね以下のように分かれているように感じています。
肯定的な評価

  • 開発環境構築(SDK, Toolchain, Python, CMake 等)の自動化が非常に強力
  • Web フロントエンド以外の領域にも適用範囲が広がりつつある
  • CI を人間の代わりに回しているような感覚

懸念・否定的な意見

  • 何が実行されたのか分かりにくい(ブラックボックス性)
  • ローカル環境での再現性が低い
  • ベンダーロックインの可能性
  • 組み込み分野では「たまたま動いた」ケースもまだ多い

今回 ESP-IDF で成功した例は、まだ一般解ではありませんが、
少なくとも「不可能ではない段階」に到達しつつあることは確かだと感じました。

今回、AIエージェントを活用することで、環境構築から実装までを驚くほどスムーズに行うことができました。しかし、その便利さを享受すると同時に、技術者として一抹の「危うさ」も感じました。

「環境構築」のブラックボックス化について

組み込み開発において、ベンダーごとに異なる開発環境の構築は大きな参入障壁です。ここをAIが担うことで、開発の間口が広がることは間違いなくポジティブな側面です。

一方で、「すべてを任せてしまった結果、何が行われたかを理解していない」 という状況はリスクでもあります。
今回は成功しましたが、もしAIが解決できない深層のエラーが発生した場合、背後で動いているツールチェーンの仕組みを知らないユーザーは、手も足も出なくなるでしょう。「動かない時に調べる力」が、AIへの依存によって削がれてしまう懸念があります。

コードの安全性とエンジニアの責務

生成されたコードについても同様です。
AIは「動くコード」を提示してくれましたが、そのメモリ管理は正しいか? エッジケースでの挙動は安全か? これらを最終的に監査(レビュー)し、担保するのは人間の役割です。

「AIが書いたから大丈夫」ではなく、「AIが書いたコードを疑い、検証する」姿勢。
皮肉なことに、AIにコーディングを任せる時代だからこそ、私たちエンジニアにはこれまで以上に基礎的な原理原則への理解と、コードの良し悪しを判断する知識が求められているのだと痛感しました。

このコードをプロダクション品質に直すなら何を変えるか

今回のコードは「動作確認の最短ルート」としては十分ですが、製品に載せるとなると設計判断がいくつも必要になります。AIが生成したコードをベースにするとしても、最低限以下は手で直したいポイントです。

1) 電力・起動時間の最適化

  • Wi-Fi 接続は最も電力を食う処理なので、送信頻度(10サンプルごと)が妥当か再検討する
  • ADC は毎回 init/deinit せず、要件次第では ULP でサンプリングする
  • Deep Sleep 復帰後に毎回ログを大量に吐くと消費電力・起動時間に影響するため、ログレベル設計を行う

2) RTC データの堅牢化

  • RTC_DATA_ATTR は便利だが 電源断やブラウンアウトで消える前提で設計する
  • count のみで正当性を信じず、マジックナンバー / CRC / バージョン番号を持たせて破損検出する
  • “送信済み扱い”の条件を曖昧にせず、ACK を受けたら消す / 失敗なら保持など運用ルールを決める

3) 通信の信頼性(最重要)

  • HTTP 平文ではなく HTTPS(証明書検証) を使う
  • esp_http_clientタイムアウトと **リトライ(指数バックオフ)**を入れる
  • サーバー応答コード(200系以外)をエラーとして扱い、送信失敗時はデータを保持する
  • 可能ならキューイングして スプール(Flash/NVS)に退避し、あとで送る

4) Wi-Fi 実装の実務対応

  • 現状は切断時に即 FAIL にしているが、現場では

    • 一定回数の再接続
    • チャンネルスキャン抑制
    • 接続成功までの上限時間
      を設計して “電池が溶ける無限ループ” を防ぐ
  • SSID/PASS をソースに直書きせず、NVS に保存してプロビジョニング手段(SmartConfig / BLE / APモード)を用意する

5) テストと観測性

  • 送信ペイロードや wake reason をログに出すだけでなく、
    **エラーカウンタ(接続失敗回数、送信失敗回数)**を RTC/NVS に保持し、現場での診断性を上げる
  • “動いた” の次に必要なのは “壊れたときに直せる” 設計

結局、AIが作るのは「動く雛形」であって、
製品品質にするには 電力・永続化・通信信頼性・運用設計が入ってきます。
ここは今後も人間側の責務として残り続ける領域だと感じました。


おまけ:AIに“プロダクション品質”で書かせるにはプロンプトをどう変えるか

今回は「動くものを作る」指示だったため、AIは最短経路を選びました。
最初から品質要件を入れておくと、生成コードの方向性がかなり変わります。

例えば次のように指示します。

改善プロンプト例(要件を先に固定する)

  • ESP-IDF v5.3 / ESP32 DevKit
  • Deep Sleep 復帰でサンプルを蓄積し、10件ごとに送信
  • 送信は HTTPS、タイムアウトと指数バックオフ付きリトライ
  • 送信成功(2xx)時のみバッファをクリア、失敗時は保持して次回再送
  • RTCデータには マジックナンバー+CRC をつけて破損検出
  • Wi-Fi は最大 N 回まで再試行、上限時間を超えたら sleep
  • SSID/PASS はソース直書き禁止。NVS保存+初回のみ設定(暫定で menuconfig でも可)
  • 省電力優先:不要な init/deinit を避け、ログは INFO 最小限
  • main.c と CMakeLists.txt のほか、動作確認手順とテスト観点も出力する

こういう “非機能要件の縛り” を最初に入れると、AIは「動く」だけでなく「運用できる」方向に寄せてくれます。

まとめ

Antigravityによる開発体験は、面倒な定型作業を劇的に圧縮してくれる素晴らしいものでした。
しかし、それは「人間が何も知らなくて良い」ことを意味しません。

AIが得意なのは、環境構築・ビルド・修正といった「手順化できる作業」を高速に回すことです。
一方で、要件の定義、失敗時のふるまい、セキュリティ、電力、運用――つまり「設計判断」を引き受けられるのは、まだ人間側です。

Manual Config is Dead.
だからこそ、私たちは空いた手と時間を使って、ログの読み方や原因切り分けといった基礎体力、そして「このコードを採用して良いか」を判断できる設計眼を鍛え続ける必要があるのではないでしょうか。

次は、同じテーマで「プロダクション品質の要件」を最初からプロンプトに埋め込んだ場合、AIのアウトプットがどこまで変わるのかも試してみたいと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?