時代はAIコーディング
ソフトウェア(アプリ)はもう完全にAIが人間のコーディングを取って代わるようになりました。そうは言っても現時点ではまだVibe Codingのレベルまでは至らず、本当に正しいコードを書いているかを人間がちゃんとチェックする必要があります。
ところでハードウェアやファームウェアに関しては一向にその手の話を聞きません。正確にはもう形が決まっている評価ボードに関してはLチカくらいなら余裕でできるみたいですが、メーカーが出す製品は評価ボードではありませんし、マルチファンクションであるCPUのI/Oをどう使うかは設計者が全部考え、場合によっては配線の都合でピンを入れ替えるなど、そんなに単純なものではありません。そもそも教師データが少なくて吐き出すソースコードそのものが変であることも普通です。
ということで
Cursorを使ってどこまでAIがファームウェアを書くことができるのかを試してみようと思います。ただ、現在は無課金での使用ですので、使用するモデルはGPT-o4.1のみです。
正解を自分で作る
先にも書いたように、そもそもファームウェアの世界においては正しいコードを吐き出してくれることがまだ少ないです。ですので、あらかじめ自分で正しく動くプロジェクトを構築しておきます。
ちなみにSDK 3.0.xからはアドバタイズの自動リピートはなくなったみたいで切断されたら再起動する必要があります。
(BT_LE_ADV_OPT_ONE_TIMEがDeprecatedになっていて最初は意味が分かりませんでした)
この時点でVibe Codingとは真逆の方向性ですが、現状はやむなしということで
ソースコードはこちらです。
#include <bluetooth/services/nus.h>
#include <dk_buttons_and_leds.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/services/bas.h>
#include <zephyr/bluetooth/services/dis.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/settings/settings.h>
#include <zephyr/sys/util.h>
LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
// For bluetooth advertising
#if defined(CONFIG_BT_EXT_ADV)
static const struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, (sizeof(CONFIG_BT_DEVICE_NAME) - 1)),
BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_BAS_VAL), BT_UUID_16_ENCODE(BT_UUID_DIS_VAL)),
BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_NUS_VAL),
// SMP
BT_DATA_BYTES(BT_DATA_UUID128_ALL, 0x84, 0xaa, 0x60, 0x74, 0x52, 0x8a, 0x8b, 0x86, 0xd3, 0x4c, 0xb7, 0x1d, 0x1d, 0xdc, 0x53, 0x8d),
};
#else
static const struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, (sizeof(CONFIG_BT_DEVICE_NAME) - 1)),
};
static const struct bt_data sd[] = {
BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_BAS_VAL), BT_UUID_16_ENCODE(BT_UUID_DIS_VAL)),
BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_NUS_VAL),
// SMP
BT_DATA_BYTES(BT_DATA_UUID128_ALL, 0x84, 0xaa, 0x60, 0x74, 0x52, 0x8a, 0x8b, 0x86, 0xd3, 0x4c, 0xb7, 0x1d, 0x1d, 0xdc, 0x53, 0x8d),
};
#endif
static struct bt_conn *conn_object;
static struct bt_gatt_exchange_params mtu_exchange_params;
static enum bt_nus_send_status nus_send_status = BT_NUS_SEND_STATUS_DISABLED;
/* ----- Advertising Function Start ----- */
static void start_advertising_work_handler(struct k_work *work)
{
int err;
// Advertising
#ifdef CONFIG_BT_EXT_ADV
static struct bt_le_ext_adv *ext_adv;
struct bt_le_ext_adv_start_param ext_adv_start_param = {0};
struct bt_le_adv_param adv_param = *BT_LE_ADV_PARAM(
BT_LE_ADV_OPT_CONN | BT_LE_ADV_OPT_EXT_ADV,
BT_GAP_ADV_FAST_INT_MIN_1, // 30msec
BT_GAP_ADV_FAST_INT_MAX_1, // 60msec
NULL
);
// Undirected Extended Advertising
LOG_INF("Start undirected advertising with extension.");
// Stop
if (ext_adv) {
err = bt_le_ext_adv_stop(ext_adv);
if (err) {
LOG_ERR("Failed to stop extended advertising(%d)", err);
return;
}
err = bt_le_ext_adv_delete(ext_adv);
if (err) {
LOG_ERR("Failed to delete advertising set(%d)", err);
return;
}
}
err = bt_le_ext_adv_create(&adv_param, NULL, &ext_adv);
if (err) {
LOG_ERR("Failed to create advertiser set.(%d)", err);
return;
}
err = bt_le_ext_adv_set_data(ext_adv, ad, ARRAY_SIZE(ad), NULL, 0);
if (err) {
LOG_ERR("Failed to set advertising data.(%d)", err);
return;
}
err = bt_le_ext_adv_start(ext_adv, &ext_adv_start_param);
if (err) {
LOG_ERR("Failed to start advertising.(%d)", err);
return;
}
#else
struct bt_le_adv_param adv_param = *BT_LE_ADV_PARAM(
BT_LE_ADV_OPT_CONN,
BT_GAP_ADV_FAST_INT_MIN_1, // 30msec
BT_GAP_ADV_FAST_INT_MAX_1, // 60msec
NULL
);
// Undirected Advertising
LOG_INF("Start undirected advertising.");
err = bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
if (err) {
LOG_ERR("Advertising failed to start.(%d)", err);
return;
}
#endif
}
K_WORK_DELAYABLE_DEFINE(start_advertising, start_advertising_work_handler);
/* ----- Advertising Function End ----- */
/* ----- NUS Function Start ----- */
static void bt_sent_cb(struct bt_conn *conn)
{
LOG_DBG("Sent.");
}
static void bt_send_enabled_cb(enum bt_nus_send_status status)
{
if (status == BT_NUS_SEND_STATUS_ENABLED) {
LOG_INF("NUS notification has been enabled.");
nus_send_status = BT_NUS_SEND_STATUS_ENABLED;
} else {
LOG_INF("NUS notification has been disabled.");
nus_send_status = BT_NUS_SEND_STATUS_DISABLED;
}
}
static void bt_received_cb(struct bt_conn *conn, const uint8_t *const data, uint16_t len)
{
LOG_INF("Received.");
}
struct bt_nus_cb nus_cb = {
.received = bt_received_cb,
.send_enabled = bt_send_enabled_cb,
.sent = bt_sent_cb,
};
/* ----- NUS Function End ----- */
/* ----- Bluetooth Function Start ----- */
static void mtu_exchange_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params)
{
LOG_INF("MTU exchange %s (%u)", err == 0U ? "successful" : "failed", bt_gatt_get_mtu(conn));
}
static int mtu_exchange(struct bt_conn *conn)
{
int err;
LOG_INF("Current MTU = %u", bt_gatt_get_mtu(conn));
mtu_exchange_params.func = mtu_exchange_cb;
LOG_INF("Exchange MTU...");
err = bt_gatt_exchange_mtu(conn, &mtu_exchange_params);
if (err) {
LOG_INF("%s: MTU exchange failed (err %d)", __func__, err);
}
return err;
}
static void le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t latency, uint16_t timeout)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
LOG_INF("LE conn param updated: %s int 0x%04x lat %d to %d", addr, interval, latency, timeout);
}
static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
LOG_INF("LE conn param req: %s int (0x%04x, 0x%04x) lat %d to %d", addr, param->interval_min, param->interval_max, param->latency, param->timeout);
return true;
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
LOG_INF("Disconnected.");
if (conn_object) {
bt_conn_unref(conn_object);
conn_object = NULL;
// Start advertising
k_work_schedule(&start_advertising, K_NO_WAIT);
}
}
static void connected(struct bt_conn *conn, uint8_t err)
{
if (err) {
LOG_ERR("Failed to connect.(%d)", err);
return;
}
LOG_INF("Connected.");
conn_object = bt_conn_ref(conn);
(void)mtu_exchange(conn);
}
BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected,
.disconnected = disconnected,
.le_param_req = le_param_req,
.le_param_updated = le_param_updated,
};
/* ----- Bluetooth Function End ----- */
/* ----- Pairing Function Start ----- */
static void pairing_complete(struct bt_conn *conn, bool bonded)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
LOG_INF("Pairing completed: %s, bonded: %d", addr, bonded);
}
static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
LOG_INF("Pairing failed conn: %s, reason %d %s", addr, reason, bt_security_err_to_str(reason));
}
static struct bt_conn_auth_info_cb conn_auth_info_callbacks = {
.pairing_complete = pairing_complete,
.pairing_failed = pairing_failed,
};
/* ----- Pairing Function End ----- */
/* ----- Auth Function Start ----- */
static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
LOG_INF("Pairing cancelled: %s", addr);
LOG_INF("Passkey for %s: %06u", addr, passkey);
}
static void auth_cancel(struct bt_conn *conn)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
LOG_INF("Pairing cancelled: %s", addr);
}
static struct bt_conn_auth_cb conn_auth_callbacks = {
.passkey_display = auth_passkey_display,
.cancel = auth_cancel,
};
/* ----- Auth Function End ----- */
/* ----- Main Function Start ----- */
void button_changed(uint32_t button_state, uint32_t has_changed)
{
if (has_changed & 0x01) {
if (button_state & has_changed) {
LOG_DBG("button 0 changes to on");
} else {
LOG_DBG("button 0 changes to off.");
}
}
#if DT_NODE_EXISTS(DT_ALIAS(sw1))
if (has_changed & 0x02) {
if (button_state & has_changed) {
LOG_DBG("button 1 changes to on");
} else {
LOG_DBG("button 1 changes to off.");
}
}
#endif
#if DT_NODE_EXISTS(DT_ALIAS(sw2))
if (has_changed & 0x04) {
if (button_state & has_changed) {
LOG_DBG("button 2 changes to on");
} else {
LOG_DBG("button 2 changes to off.");
}
}
#endif
#if DT_NODE_EXISTS(DT_ALIAS(sw3))
if (has_changed & 0x08) {
if (button_state & has_changed) {
LOG_DBG("button 3 changes to on");
} else {
LOG_DBG("button 3 changes to off.");
}
}
#endif
}
int main(void)
{
int err;
LOG_INF("Starting Bluetooth NUS Advertising sample\n");
// Initialize Buttons and LEDs
err = dk_buttons_init(button_changed);
if (err) {
LOG_ERR("Failed to initialize button[s].(%d)", err);
return 0;
}
err = dk_leds_init();
if (err) {
LOG_ERR("Failed to initialize led[s].(%d)", err);
return 0;
}
if (IS_ENABLED(CONFIG_BT_SMP)) {
err = bt_conn_auth_cb_register(&conn_auth_callbacks);
if (err) {
LOG_ERR("Failed to register authorization callbacks.");
return 0;
}
err = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks);
if (err) {
LOG_ERR("Failed to register authorization info callbacks.");
return 0;
}
}
err = bt_enable(NULL);
if (err) {
LOG_ERR("BLE enable failed (err: %d)", err);
return 0;
}
if (IS_ENABLED(CONFIG_SETTINGS)) {
settings_load();
}
err = bt_nus_init(&nus_cb);
if (err) {
LOG_ERR("Failed to initialize NUS service.(%d)", err);
return -1;
}
// Start advertising
k_work_schedule(&start_advertising, K_NO_WAIT);
while (true) {
if (conn_object == NULL) {
dk_set_led_on(0);
k_sleep(K_MSEC(200));
dk_set_led_off(0);
k_sleep(K_MSEC(200));
} else {
dk_set_led_on(0);
k_sleep(K_SECONDS(1));
}
}
}
/* ----- Main Function End ----- */
Kconfigはこちらです。
CONFIG_LOG=y
CONFIG_BT_BUF_ACL_TX_SIZE=251
CONFIG_BT_BUF_ACL_RX_SIZE=251
CONFIG_BT_L2CAP_TX_MTU=247
CONFIG_NVS=y
CONFIG_BT_NUS=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_EXT_ADV=y
CONFIG_BT_DIS=y
CONFIG_BT_DIS_SETTINGS=y
CONFIG_BT_DIS_STR_MAX=32
CONFIG_BT_DIS_MODEL="nRF52840DK"
CONFIG_BT_DIS_MANUF="Xenoma Inc."
CONFIG_BT_DIS_FW_REV=y
CONFIG_BT_DIS_FW_REV_STR="0.0.1-alpha"
CONFIG_BT_DIS_HW_REV=y
CONFIG_BT_DIS_HW_REV_STR="Rev. A"
CONFIG_BT_DIS_SW_REV=y
CONFIG_BT_DIS_SW_REV_STR="nRF Connect SDK 3.0.1"
CONFIG_BT_BAS=y
CONFIG_BT_SETTINGS=y
CONFIG_BT_SMP=y
CONFIG_BT_GATT_CLIENT=y
CONFIG_BT_PERIPHERAL_PREF_MIN_INT=6
CONFIG_BT_PERIPHERAL_PREF_MAX_INT=12
CONFIG_BT_DEVICE_NAME="NUS Device"
CONFIG_DK_LIBRARY=y
CONFIG_FLASH=y
CONFIG_BT=y
CONFIG_FLASH_MAP=y
ここまでのコードをGitに記録し、初期状態に戻してCursorでソースコードを書かせてみることにします。
いざ!(笑)
まずはベースとなるBLEから

ほう、まあまあいい感じですね。でも全然足りません。

ちょっとよくなってきました。

まあでもここまでやって思ったのは、そもそもBLEのことをちゃんと知らないとプロンプト書けないじゃん。connectイベントやdisconnectイベント(コールバック)ってなんだよ、どういうときにそれが発生してどうなるんだよということが理解できていないとちんぷんかんぷんです。
そんなのソフトウェアでも一緒だろと言えなくはないですが、もっと具体的にこういうゲームを作るとかで指示を出せばコールバックとか知らなくたってできてしまう可能性はあります。
というか、「接続したときにこうしたい」「切断した時にこうしたい」みたいなプロンプトを書くべきでしたね。ここまで書いて反省です……。
なまじ分かっているせいで分かっているプロンプトを書いてしまいます……
続いてBASとDISを実装します。このプロンプトを入れたら1分以上考えていましたがなんとなくそれっぽいモノは追加されました。

が、どう見ても余計なものがあるのでこれは削除してもらいます。

余計なコードを消すのくらい自分でやれよ!(笑)と思いつつもやらせます。これできっとAIは学習するんですよね?(自信なし)

と、まあここまでは割と順調でしたが、プロンプトがダメなのかよく分からないコードやKconfigが追加されることもしばしば(笑)
結局、格闘に格闘を重ねてできあがったのがこちらです。
AIに記述させたソースコードはこちらです。
#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include <bluetooth/services/nus.h>
#include <zephyr/bluetooth/services/bas.h>
#include <zephyr/bluetooth/services/dis.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(ble_nus, CONFIG_LOG_DEFAULT_LEVEL);
#define DEVICE_NAME CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
static const struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
};
static const struct bt_data sd[] = {
BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_NUS_VAL),
BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_BAS_VAL), BT_UUID_16_ENCODE(BT_UUID_DIS_VAL)),
};
static void nus_received(struct bt_conn *conn, const uint8_t *data, uint16_t len)
{
char addr[BT_ADDR_LE_STR_LEN];
const uint8_t *buf = data;
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
LOG_INF("Received data from %s:", addr);
for (uint16_t i = 0; i < len; i++) {
LOG_INF("%02x", buf[i]);
}
}
static void nus_sent(struct bt_conn *conn)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
LOG_INF("Data sent to %s", addr);
}
static void nus_send_enabled(enum bt_nus_send_status status)
{
LOG_INF("NUS send status: %d", status);
}
static struct bt_nus_cb nus_cb = {
.received = nus_received,
.sent = nus_sent,
.send_enabled = nus_send_enabled,
};
static void connected(struct bt_conn *conn, uint8_t err)
{
if (err) {
LOG_ERR("Connection failed (err %u)", err);
} else {
LOG_INF("Connected");
}
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
LOG_INF("Disconnected (reason %u)", reason);
}
static struct bt_conn_cb conn_callbacks = {
.connected = connected,
.disconnected = disconnected,
};
int main(void)
{
int err;
uint8_t battery_level = 100;
#if defined(CONFIG_BT_EXT_ADV)
struct bt_le_ext_adv *adv;
#endif
LOG_INF("Starting BLE NUS application");
err = bt_enable(NULL);
if (err) {
LOG_ERR("Bluetooth init failed (err %d)", err);
return err;
}
LOG_INF("Bluetooth initialized");
bt_conn_cb_register(&conn_callbacks);
err = bt_nus_init(&nus_cb);
if (err) {
LOG_ERR("Failed to init NUS (err %d)", err);
return err;
}
#if defined(CONFIG_BT_EXT_ADV)
/* Create extended advertising instance */
err = bt_le_ext_adv_create(BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONN,
BT_GAP_ADV_FAST_INT_MIN_2,
BT_GAP_ADV_FAST_INT_MAX_2,
NULL),
NULL, &adv);
if (err) {
LOG_ERR("Failed to create advertising set (err %d)", err);
return err;
}
/* Set advertising data */
err = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
if (err) {
LOG_ERR("Failed to set advertising data (err %d)", err);
return err;
}
/* Start advertising */
err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
if (err) {
LOG_ERR("Failed to start advertising (err %d)", err);
return err;
}
#else
err = bt_le_adv_start(BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONN,
BT_GAP_ADV_FAST_INT_MIN_2,
BT_GAP_ADV_FAST_INT_MAX_2,
NULL),
ad, ARRAY_SIZE(ad),
sd, ARRAY_SIZE(sd));
if (err) {
LOG_ERR("Advertising failed to start (err %d)", err);
return err;
}
#endif
LOG_INF("Advertising started");
while (1) {
/* バッテリーレベルの更新(デモ用) */
battery_level--;
if (battery_level < 0) {
battery_level = 100;
}
bt_bas_set_battery_level(battery_level);
LOG_INF("Battery level: %d%%", battery_level);
k_sleep(K_SECONDS(10));
}
return 0;
}
AIに記述させたKconfigはこちらです。
# Enable Bluetooth
CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DEVICE_NAME="NUS Device"
CONFIG_SETTINGS=y
CONFIG_BT_SETTINGS=y
CONFIG_BT_DIS_SETTINGS=y
# Enable Extended Advertising
CONFIG_BT_EXT_ADV=y
CONFIG_BT_EXT_ADV_MAX_ADV_SET=1
CONFIG_BT_CTLR_ADV_EXT=y
CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=1650
# Connection Parameters
CONFIG_BT_PERIPHERAL_PREF_MIN_INT=6
CONFIG_BT_PERIPHERAL_PREF_MAX_INT=12
CONFIG_BT_PERIPHERAL_PREF_LATENCY=0
CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400
# Enable Nordic UART Service
CONFIG_BT_NUS=y
# Enable Battery Service
CONFIG_BT_BAS=y
# Enable Device Information Service
CONFIG_BT_DIS=y
CONFIG_BT_DIS_SERIAL_NUMBER=y
CONFIG_BT_DIS_FW_REV=y
CONFIG_BT_DIS_HW_REV=y
CONFIG_BT_DIS_MANUF="Nordic Semiconductor"
CONFIG_BT_DIS_MODEL="NUS Device"
CONFIG_BT_DIS_SERIAL_NUMBER_STR="123456789"
CONFIG_BT_DIS_FW_REV_STR="1.0.0"
CONFIG_BT_DIS_HW_REV_STR="1.0.0"
# Enable logging
CONFIG_LOG=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_LOG_BACKEND_RTT=y
CONFIG_LOG_BACKEND_UART=n
# Enable NVS for settings storage
CONFIG_NVS=y
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
途中で飽きました(笑)
なぜ途中で飽きたのか
僕の出すプロンプトが悪いのかは分かりませんが、「正解を前提としたプロンプト」を記述しないとまともに記述できないからです。この状態だと言うまでもなく「自分で書いたほうが早い」になります。
例えばCONFIG_BT_DISを使いたい、というプロンプトを出すと
CONFIG_BT_DIS_FW_REV_STR="1.0.0"
CONFIG_BT_DIS_HW_REV_STR="1.0.0"
は追加してくれますが、これを使うための
CONFIG_BT_DIS_FW_REV=y
CONFIG_BT_DIS_HW_REV=y
は追加してくれません。上記のprj.confに書かれているのはもちろんCONFIG_BT_DIS_HW_REV_STRだけでは動かないよというプロンプトを出した結果として追加されたものです。
厳密にはもっといっぱいプロンプト出しています(笑)
有料のモデルならもっと違う結果が出せるような気はします。また、プログラミングに特化したモデルならそれもまた違う結果が出せる気がします。が、現状で試せるのはここまでです。そして、ここまでの結果としては「少なくとも無料モデルでは使い物にならない」が結論です。
続く
続くの……?w