07.T-Watch S3 plusでスマホシャッター
07.T-Watch S3 plusでスマホシャッター
・T-Watch S3 plus の画面ボタンをタッチすると、スマホカメラのシャッターが切れます。
・T-Watch S3 plus でテストしていますが、plus 無しの T-Watch S3 でも動作します。
・lvgl 9.5.0で作成しています。
・簡易的に日本語が使えるようにfontは、&lv_font_source_han_sans_sc_16_cjk を使います。
・BLEは省電力のため、出力を最低まで下げています(-12dBm)。5mは届くと思います。
・カウントダウンのbeep音が鳴ります。
操作法
1.起動すると時計には"T-Watch Shutter"と表示されています。2.スマホ > 設定 > 接続設定 > Bluetooth > 新しいデバイスとペア設定 > T-Watch Shutter > ペア設定する
3.スマホ > カメラを開きます。
4.時計の"T-Watch Shutter"をタッチ > カウントダウンしてカメラのシャッターが切れます。
* BLE接続ができていない場合は未接続を表示します。
各Ver
・(ボード) esp32 by Espressif Systems 3.3.6 (固定)です。3.3.11では動作しませんでした。3.3.7以降はESP32-S3でBLE起動時にクラッシュする既知の不具合があるそうです。・Arduino IDE 2.3.10 (2026-06-09)
・LilyGoLib 0.2.0 (最新)
・SensorLib 0.3.3
・RadioLib 7.4.0
・TinyGPSPlus 1.1.0
・XPowersLib 0.3.1
・lvgl 9.5.0
・ESP32 BLE Keyboard 0.3.4
・NimBLE-Arduino 2.2.3
・esptool 5.1.0
・FFat 3.3.6
・FS 3.3.6
・Wire 3.3.6
・SPI 3.3.6
・ESP_I2S 3.3.6
・Preferences 3.3.6
* 今回使用していないのも入っています。
スケッチ
// セルフタイマー シャッター
// 画面タッチで 1秒ごとにビープ音 3秒後にスマホのシャッター
// ボード > esp32 by Espressif Systems 3.3.6固定 (arduino-esp32コア)
// (3.3.7以降はESP32-S3でBLE起動時にクラッシュする既知の不具合がある)
#include <LilyGoLib.h> // LilyGoLib 0.2.0(最新)を使用
#include <LV_Helper.h> // lvgl by kisvegabor 9.4.0 出荷プログラムで使用 → 9.5.0
#include <BleKeyboard.h> // BLEでスマホシャッター使用
#include <NimBLEDevice.h> // BLE出力パワーを下げる
#include <ESP_I2S.h> // I2S使用
I2SClass i2s;
BleKeyboard bleKeyboard("T-Watch Shutter", "LilyGO", 100); // (スマホに表示のデバイス名,機種,100)
#define SPK_BCLK_PIN 48 // MAX98357A I2S 3.2W MONO AMP
#define SPK_WCLK_PIN 15 // MAX98357A LRCLK
#define SPK_DOUT_PIN 46 // MAX98357A DIN
#define SPK_SAMPLE_RATE 16000 // (Hz) 毎秒RATE回発生
#define BEEP_FREQ 1000 // 音の高さ(Hz) 3秒後は別
//参考 NHK:予報440Hz/正報880Hz, NTT電話時報:1kHz
#define DURATION_MS 100 // 音の長さ(mS) 3秒後は別
//参考 NHK予報:正弦波0.1S,無音0.9S,3回繰返す, 正報:正弦波1S一定音量、次の2秒で直線減衰
void speaker_init() { // スピーカ初期設定関数
i2s.setPins(SPK_BCLK_PIN, SPK_WCLK_PIN, SPK_DOUT_PIN, -1, -1); // I2Sのピン設定
// (int8_t bclk,int8_t ws,int8_t dout,int8_t din=-1,int8_t mclk=-1)
i2s.begin(I2S_MODE_STD, SPK_SAMPLE_RATE, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO, -1); //I2S初期化
}
static void speaker_beep(uint32_t frequency, uint32_t duration_ms) { // HzとmSでbeep関数
// if (!spk_tx_handle) return;// 呼出中はこの処理をブロック
uint32_t num_samples = (SPK_SAMPLE_RATE * duration_ms) / 1000; // 発生音数
for (uint32_t i = 0; i < num_samples; i++) { // サイン波データを生成しながらI2Sへ書込み
float sample_f = sin(2.0 * PI * frequency * i / SPK_SAMPLE_RATE); // sin波の計算 16bit モノラル
int16_t sample = (int16_t)(sample_f * 15000); // 音量は32767まで
i2s.write((uint8_t *)&sample, sizeof(sample)); // 1サンプル(16bit=2bytes)を書込み
}
}
// セルフタイマー
static lv_timer_t *timer_handle = NULL; //
static int countdown_count = 0; // 経過秒数(1,2,3)
static lv_obj_t *shutter_label = NULL; //
static void countdown_timer_cb(lv_timer_t *t) { // 3秒カウントダウン後にスマホシャッターを押す関数
countdown_count++; // 1カウント(秒)追加
speaker_beep(BEEP_FREQ, DURATION_MS); // fHz を tms鳴らす
if (countdown_count < 3) { // 3秒以下なら
lv_label_set_text_fmt(shutter_label, "%d", 3 - countdown_count); // カウントダウン表示
} else { // 3秒経ったら
lv_obj_align(shutter_label, LV_ALIGN_CENTER, -35, -5);// 位置
lv_label_set_text(shutter_label, "シャッター完"); // ラベルに表示
delay(1000); // 1秒待つ
speaker_beep(2000, 300); // 2kHz 0.3秒鳴らす
if (bleKeyboard.isConnected()) bleKeyboard.write(KEY_MEDIA_VOLUME_UP); // スマホのシャッターを押す
lv_timer_del(timer_handle); //
timer_handle = NULL; //
countdown_count = 0; // カウントダウン変数リセット
}
}
static void shutter_btn_event_cb(lv_event_t *e) { // 画面シャッターボタンを押した時の関数
lv_event_code_t code = lv_event_get_code(e); // イベントコードを取得
if (code != LV_EVENT_CLICKED) return; // クリック以外は戻る
if (timer_handle != NULL) return; // すでにカウントダウン中なら戻る
if (!bleKeyboard.isConnected()) { // bleKeyboardに接続してなかったら
lv_obj_set_style_text_color(shutter_label, lv_color_make(255, 69, 0), LV_PART_MAIN); // 赤字 (Orangered)
lv_label_set_text(shutter_label, " BLE 未接続"); // ラベルに表示
return; // 戻る
}
countdown_count = 0; // カウントダウン変数リセット
lv_obj_center(shutter_label); // 位置
lv_obj_set_style_text_color(shutter_label, lv_color_make(0, 0, 0), LV_PART_MAIN); // 黒字
lv_label_set_text(shutter_label, "3"); // ラベルに表示
timer_handle = lv_timer_create(countdown_timer_cb, 1000, NULL); // 1s周期のタイマーを開始
// 新しいlv_timer(定期的なコールバック関数,呼出間隔mS,カスタムパラメータ)
}
static void create_ui() { // 画面表示の関数
lv_obj_t *scr = lv_scr_act(); // 現在の画面
lv_obj_set_style_bg_color(scr, lv_color_black(), 0); // 黒地
lv_obj_t *btn = lv_btn_create(scr); // 画面にボタン作成
lv_obj_set_size(btn, 238, 120); // サイズ
lv_obj_center(btn); // 位置
lv_obj_set_style_radius(btn, 30, LV_STATE_DEFAULT); // 角丸
lv_obj_set_style_bg_color(btn, lv_color_make(175, 238, 238), 0); // 水色 Paleturquoise
lv_obj_add_event_cb(btn, shutter_btn_event_cb, LV_EVENT_CLICKED, NULL); // タッチしたらcb関数へ
shutter_label = lv_label_create(btn); // ボタンにラベル作成
lv_obj_center(shutter_label); // 位置
lv_obj_set_style_text_font(shutter_label, &lv_font_source_han_sans_sc_16_cjk, 0); // 文字の種類
lv_obj_set_style_transform_zoom(shutter_label, 435, 0); // x/256倍に拡大 1.5倍
lv_obj_align(shutter_label, LV_ALIGN_CENTER, -40, -10); // 拡大文字はずれるので補正
lv_obj_set_style_text_color(shutter_label, lv_color_make(0, 100, 0), LV_PART_MAIN); // 緑字 (Darkgreen)
lv_label_set_text(shutter_label, "T-Watch Shutter"); // 表示文字(スマホの接続先)
}
void setup() {
Serial.begin(115200); // シリアルモニタの通信速度 Arduino-IDEと合っていないと表示しません
delay(100); // 0.1秒待つ
instance.begin(); // LilyGoLibを初期化
bool bleOk = bleKeyboard.begin(); // BLEキーボードを初期化
if (bleOk) NimBLEDevice::setPower(ESP_PWR_LVL_N12); // BLE最小出力 -12dB/m
// -12dBm:ESP_PWR_LVL_N12(N12),-9dBm:(N9),-6dBm:(N6),
// -3dBm:(N3),0dBm:(P0),3dBm:(P3),6dBm:(P6),9dBm:(P9)
beginLvglHelper(instance); // LVGLを初期化
instance.setBrightness(100); // 明るさ1-255,0(消)
speaker_init(); // スピーカーアンプを初期化
create_ui(); // 画面
}
void loop() {
lv_timer_handler(); //
delay(5); // 5mS待つ
}
* flash memory(3.1Mbyte)のうち、スケッチが42%使用。RAM(327kbyte)のうち、global変数が11%使用、local変数で291kbyte使用可能。(1000byte=1kbyteで計算)



