CodexPad Arduino 库 3.0.0
载入中...
搜索中...
未找到
inputs_detection.ino
浏览该文件的文档.
1/**
2 * @~English
3 * @file inputs_detection.ino
4 * @example inputs_detection.ino
5 * @brief Demonstrates how to detect real-time button states and joystick movements of a connected CodexPad.
6 * @details This example establishes a connection to a specific CodexPad device (by Bluetooth Device Address) and continuously
7 * monitors all user inputs. It showcases the detection of three distinct button states: **pressed** (momentary press),
8 * **released** (momentary release), and
9 * **holding** (sustained press). It also monitors the analog joystick axes and prints their values when a significant
10 * change beyond a set threshold is detected, filtering out minor jitter.
11 * @note The `Update()` method must be called as frequently as possible within the main loop without delays to ensure
12 * real-time responsiveness and prevent data packet loss.
13 * @see CodexPad::Update
14 */
15/**
16 * @~Chinese
17 * @file inputs_detection.ino
18 * @example inputs_detection.ino
19 * @brief 演示如何检测已连接的 CodexPad 设备的实时按钮状态与摇杆移动。
20 * @details 本示例通过Bluetooth Device Address连接到指定的 CodexPad 设备,并持续监控所有用户输入。
21 * 它展示了三种不同的按钮状态检测: **按下** (瞬间按下)、 **释放** (瞬间释放)和 **持续按住** 。
22 * 同时,它监控模拟摇杆轴,当检测到超过设定阈值的显著变化时打印其值,从而过滤微小抖动。
23 * @note 必须在主循环中尽可能频繁地调用 `Update()` 方法,且不得添加延时,以确保实时响应性并防止数据包丢失。
24 * @see CodexPad::Update
25 */
26
27#include "codex_pad.h"
28
29/**
30 * IMPORTANT:
31 * This using directive is REQUIRED to directly access `Button` and `Axis`.
32 * Without it, you must write the fully qualified names:
33 * gamepad::input::Button::kUp
34 * gamepad::input::Axis::kLeftStickX
35 * Forgetting this line will cause compile errors when using Button or Axis.
36 */
37/**
38 * 重要:
39 * 必须使用该命名空间,否则无法直接访问 `Button` 和 `Axis`。
40 * 如果没有这一行,就必须写成完整限定名:
41 * gamepad::input::Button::kUp
42 * gamepad::input::Axis::kLeftStickX
43 * 忘记引入命名空间会导致编译失败。
44 */
45using namespace gamepad::input;
46
47namespace {
48// Replace with your CodexPad device's Bluetooth device address
49// 替换为你的 CodexPad 的 Bluetooth device address
50const std::string kBluetoothDeviceAddress = "E4:66:E5:A2:17:06";
51
52CodexPad g_codex_pad;
53
54/**
55 * Convert button constant to readable string name
56 * 将按钮枚举转换为可读的字符串名称
57 */
58std::string ButtonToString(Button button) {
59 switch (button) {
60 case Button::kUp: {
61 return "Up"; // 上按钮 | UP button
62 }
63 case Button::kDown: {
64 return "Down"; // 下按钮 | DOWN button
65 }
66 case Button::kLeft: {
67 return "Left"; // 左按钮 | LEFT button
68 }
69 case Button::kRight: {
70 return "Right"; // 右按钮 | RIGHT button
71 }
72 case Button::kSquareX: {
73 return "Square(X)"; // 方形 或者 X 按钮 | SQUARE or X button
74 }
75 case Button::kTriangleY: {
76 return "Triangle(Y)"; // 三角 或者 Y 按钮 | TRIANGLE or Y button
77 }
78 case Button::kCrossA: {
79 return "Cross(A)"; // 叉型 或者 A 按钮 | CROSS or A button
80 }
81 case Button::kCircleB: {
82 return "Circle(B)"; // 圆形 或者 B 按钮 | CIRCLE or B button
83 }
84 case Button::kL1: {
85 return "L1"; // L1按钮 | L1 button
86 }
87 case Button::kL2: {
88 return "L2"; // L2按钮 | L2 button
89 }
90 case Button::kL3: {
91 return "L3"; // L3按钮 | L3 button
92 }
93 case Button::kR1: {
94 return "R1"; // R1按钮 | R1 button
95 }
96 case Button::kR2: {
97 return "R2"; // R2按钮 | R2 button
98 }
99 case Button::kR3: {
100 return "R3"; // R3按钮 | R3 button
101 }
102 case Button::kSelect: {
103 return "Select"; // 选择按钮 | SELECT button
104 }
105 case Button::kStart: {
106 return "Start"; // 开始按钮 | START button
107 }
108 case Button::kHome: {
109 return "Home"; // 首页按钮 | HOME button
110 }
111 default: {
112 return {}; // 未知按钮返回空字符串 | Unknown button returns empty string
113 }
114 }
115}
116
117void Connect() {
118 printf("Start to connect %s\n", kBluetoothDeviceAddress.c_str());
119 // Connect to the CodexPad with specified Bluetooth device address
120 // 连接到指定蓝牙设备地址的手柄
121 while (!g_codex_pad.Connect(kBluetoothDeviceAddress, 5000)) {
122 printf("Retry to connect %s\n", kBluetoothDeviceAddress.c_str());
123 }
124
125 printf("Remote device name: %s\n", g_codex_pad.remote_device_name().c_str());
126 printf("Remote model number: %s\n", g_codex_pad.remote_model_number().c_str());
127 printf("Remote firmware revision: %u.%u.%u\n", g_codex_pad.remote_firmware_version()[0],
128 g_codex_pad.remote_firmware_version()[1], g_codex_pad.remote_firmware_version()[2]);
129
130 if (const auto ble_client = g_codex_pad.ble_client(); ble_client != nullptr) {
131 printf("Remote Bluetooth Device Address: %s\n", ble_client->getPeerAddress().toString().c_str());
132 } else {
133 printf("Remote Bluetooth Device Address: unknown\n");
134 }
135
136 // Set transmission power to 0dBm
137 // Transmission power affects communication range and power consumption:
138 // Higher power provides longer range but consumes more battery
139 // Choose appropriate power level based on your application to balance range and battery life
140 // 设置发射功率为0dBm
141 // 发射功率影响通信距离和功耗:功率越高,通信距离越远,但功耗也越大
142 // 建议根据实际应用场景选择合适的功率等级以平衡距离和电池寿命
144 printf("Set remote tx power to 0dBm successfully\n");
145 }
146
147 printf("Connected\n");
148}
149} // namespace
150
151void setup() {
152 Serial.begin(115200);
153
154 printf("Init\n");
155 g_codex_pad.Init();
156
157 Connect();
158}
159
160void loop() {
161 // ==========================================================================
162 // 🔴 CRITICAL: Call Update() as frequently as possible in loop()
163 // ==========================================================================
164 // • Update() processes incoming Bluetooth packets from the CodexPad
165 // • Any delay(...) or long blocking code WILL cause:
166 // - Packet loss
167 // - Input lag
168 // - Unstable connection
169 //
170 // • For real-time control, call Update() every loop iteration
171 // without any blocking operations
172 //
173 // 🔴【重要】Update() 必须在 loop() 中尽可能高频调用
174 // • Update() 负责处理来自 CodexPad 的蓝牙数据包
175 // • 任何形式的 delay 或阻塞代码都会导致:
176 // - 数据丢失
177 // - 响应延迟
178 // - 连接不稳定
179 //
180 // • 实时控制应用中,必须每轮循环都调用 Update(),不可阻塞
181 // ==========================================================================
182 const gamepad::input::Tracker& it = g_codex_pad.Update();
183 // ==========================================================================
184 // Tracker: Gamepad Input Snapshot & Change Engine
185 // ==========================================================================
186 // • Returned by Update()
187 // • Maintains previous and current input snapshots
188 // • Enables edge detection and delta detection
189 //
190 // Tracker 由 Update() 返回
191 // 内部保存上一帧和当前帧的输入数据
192 // 支持边沿检测和差值检测
193 //
194 // 📚 https://codexpad.github.io/gamepad_input_arduino_lib/
195 // ==========================================================================
196
197 if (!g_codex_pad.is_connected()) {
198 printf("Disconnected, start to reconnect\n");
199 Connect();
200 return;
201 }
202
203 // ==========================================================================
204 // 🟢 Button State Change Detection (Edge-Based)
205 // ==========================================================================
206 // • pressed() → button was just pressed (released → pressed)
207 // • released() → button was just released (pressed → released)
208 // • holding() → button remains pressed across frames
209 //
210 // These APIs are *frame-differential* and rely on Update() frequency.
211 // Ideal for UI navigation, action triggers, and avoiding repeat firing.
212 //
213 // • pressed() → 按钮刚被按下(弹起 → 按下)
214 // • released() → 按钮刚被释放(按下 → 弹起)
215 // • holding() → 按钮在两帧之间持续按下
216 //
217 // 这些接口是“帧间差分”的,依赖于 Update() 的高频调用
218 // 非常适合 UI 导航、动作触发和防止长按连发
219 // ==========================================================================
220 for (auto button : {Button::kUp, Button::kDown, Button::kLeft, Button::kRight, Button::kSquareX, Button::kTriangleY,
221 Button::kCrossA, Button::kCircleB, Button::kL1, Button::kL2, Button::kL3, Button::kR1, Button::kR2,
222 Button::kR3, Button::kSelect, Button::kStart, Button::kHome}) {
223 if (it.pressed(button)) {
224 printf("Button %s: pressed\n", ButtonToString(button).c_str());
225 } else if (it.released(button)) {
226 printf("Button %s: released\n", ButtonToString(button).c_str());
227 } else if (it.holding(button)) {
228 printf("Button %s: holding\n", ButtonToString(button).c_str());
229 }
230 }
231
232 // ==========================================================================
233 // 🟢 Joystick Axis Change Detection (Threshold-Based Filtering)
234 // ==========================================================================
235 // • AxisChanged() detects significant changes between frames
236 // • Uses a threshold to filter out noise and minor jitter
237 // • Only reports movement when change ≥ threshold
238 //
239 // • AxisChanged() 用于检测摇杆轴值的有效变化
240 // • 使用阈值过滤微小抖动和噪声
241 // • 只有当变化幅度 ≥ 阈值时才视为有效移动
242 // ==========================================================================
243 constexpr uint8_t kAxisValueChangeThreshold = 2;
244
245 if (it.AxisChanged(Axis::kLeftStickX, kAxisValueChangeThreshold) ||
246 it.AxisChanged(Axis::kLeftStickY, kAxisValueChangeThreshold) ||
247 it.AxisChanged(Axis::kRightStickX, kAxisValueChangeThreshold) ||
248 it.AxisChanged(Axis::kRightStickY, kAxisValueChangeThreshold)) {
249 printf("L(X: %3" PRIu8 ", Y:%3" PRIu8 "), R(X: %3" PRIu8 ", Y: %3" PRIu8 ")\n", it[Axis::kLeftStickX],
250 it[Axis::kLeftStickY], it[Axis::kRightStickX], it[Axis::kRightStickY]);
251 }
252}
CodexPad主类
bool Connect(const std::string &bluetooth_device_address, uint32_t timeout_ms=5000) noexcept
连接
bool is_connected() const noexcept
是否连接
const std::array< uint8_t, 3 > & remote_firmware_version() const noexcept
获取CodexPad的固件版本
const gamepad::input::Tracker & Update() noexcept
更新,需要在Loop中不断调用
const std::string & remote_model_number() const noexcept
获取CodexPad的型号
void Init() noexcept
初始化
NimBLEClient * ble_client() const noexcept
获取 BLE 客户端对象
const std::string & remote_device_name() const noexcept
获取CodexPad的型号
bool set_remote_tx_power(TxPower power) noexcept
设置发射功率,连接状态下调用,立即生效于当前连接,下次连接生效