CodexPadFrameDecoder Arduino 库 1.0.3
载入中...
搜索中...
未找到
inputs_detection.ino
浏览该文件的文档.
1/**
2 * @~Chinese
3 * @file ble_uno_or_nl_16_module/inputs_detection/inputs_detection.ino
4 * @example ble_uno_or_nl_16_module/inputs_detection/inputs_detection.ino
5 * @brief 演示如何检测已连接的 CodexPad 设备的实时按钮状态与摇杆移动。
6 * @details 本示例先通过 AT 指令与指定的 CodexPad 设备建立蓝牙连接,然后持续监控所有用户输入。
7 * 它展示了三种不同的按钮状态检测: **按下** (瞬间按下)、 **释放** (瞬间释放)和 **持续按住** 。
8 * 同时,它监控模拟摇杆轴,当检测到超过设定阈值的显著变化时打印其值,从而过滤微小抖动。
9 * @note 必须在主循环中尽可能频繁地调用 `Update()` 方法,且不得添加延时,以确保实时响应性并防止数据包丢失。
10 * @see CodexPadFrameDecoder::Update
11 * @see CodexPadFrameDecoder::pressed
12 * @see CodexPadFrameDecoder::released
13 * @see CodexPadFrameDecoder::holding
14 * @see CodexPadFrameDecoder::HasAxisValueChanged
15 * @see CodexPadFrameDecoder::axis_value
16 */
17/**
18 * @~English
19 * @file ble_uno_or_nl_16_module/inputs_detection/inputs_detection.ino
20 * @example ble_uno_or_nl_16_module/inputs_detection/inputs_detection.ino
21 * @brief Demonstrate how to detect real-time button status and joystick movement of connected CodexPad devices.
22 * @details This example first sends AT commands to establish a BLE connection to a CodexPad device (by Bluetooth Device Address), then continuously
23 * monitors all user inputs. It showcases the detection of three distinct button states: **pressed** (momentary press), **released** (momentary
24 * release), and **holding** (sustained press). It also monitors the analog joystick axes and prints their values when a significant change beyond a
25 * set threshold is detected, filtering out minor jitter.
26 * @note The `Update()` method must be called as frequently as possible within the main loop without delays to ensure real‑time
27 * responsiveness and prevent data packet loss.
28 * @see CodexPadFrameDecoder::Update
29 * @see CodexPadFrameDecoder::pressed
30 * @see CodexPadFrameDecoder::released
31 * @see CodexPadFrameDecoder::holding
32 * @see CodexPadFrameDecoder::HasAxisValueChanged
33 * @see CodexPadFrameDecoder::axis_value
34 */
35
36#include <SoftwareSerial.h>
37
39
40namespace {
41// 调试串口引脚定义。
42// 将外部串口工具连接到这些引脚以查看调试输出。
43// Debug serial port pin definitions.
44// Connect external serial port tools to these pins to view debug output.
45constexpr uint8_t kDebugSerialRxPin = 6; // 调试输出接收引脚 | RX pin for debug output
46constexpr uint8_t kDebugSerialTxPin = 5; // 调试输出发送引脚 | TX pin for debug output
47
48// 摇杆轴变化检测的阈值。当前值与上一帧值的绝对差值达到此值(或到达边界0/255)时才被视为有效变化,用于过滤微小抖动。
49// Threshold for joystick axis change detection. Only when the absolute difference between the current and previous axis value reaches this value
50// (or the value hits boundaries 0/255) will it be reported as a significant change.
51constexpr uint8_t kAxisThreshold = 2;
52
53// 替换为你的 CodexPad 的 Bluetooth device address。
54// Replace with your CodexPad device's Bluetooth device address.
55const String kBluetoothDeviceAddress = "0C:3D:5E:9D:80:99";
56
57// 与蓝牙模块通信的串口波特率(请查阅模块数据手册,替换为模块对应的波特率)。
58// The serial port baud rate for communication with the Bluetooth module (please refer to the module data manual, replace with the corresponding baud
59// rate for the module).
60constexpr uint32_t kBluetoothModuleSerialBaudRate = 115200;
61
79
80// 用于调试输出的软串口。
81// A soft serial port used for debugging output.
82SoftwareSerial g_debug_serial(kDebugSerialRxPin, kDebugSerialTxPin);
83
84// 此处解码器传入的串口实例,与 Connect() 中传入的为同一个串口实例。
85// The serial instance passed in by the decoder here is the same as the one passed in Connect().
86CodexPadFrameDecoder g_codex_pad_frame_decoder(Serial);
87
88/**
89 * 将按钮枚举值转换为可读的字符串。
90 * Convert a Button enumeration value to a human-readable string.
91 */
92const String ButtonToString(CodexPadFrameDecoder::Button button) {
93 switch (button) {
95 return "Up"; // 上按钮 | UP button
96 }
98 return "Down"; // 下按钮 | DOWN button
99 }
101 return "Left"; // 左按钮 | LEFT button
102 }
104 return "Right"; // 右按钮 | RIGHT button
105 }
107 return "Square(X)"; // 方形 或者 X 按钮 | SQUARE or X button
108 }
110 return "Triangle(Y)"; // 三角 或者 Y 按钮 | TRIANGLE or Y button
111 }
113 return "Cross(A)"; // 叉形 或者 A 按钮 | CROSS or A button
114 }
116 return "Circle(B)"; // 圆形 或者 B 按钮 | CIRCLE or B button
117 }
119 return "L1"; // L1按钮 | L1 button
120 }
122 return "L2"; // L2按钮 | L2 button
123 }
125 return "L3"; // L3按钮 | L3 button
126 }
128 return "R1"; // R1按钮 | R1 button
129 }
131 return "R2"; // R2按钮 | R2 button
132 }
134 return "R3"; // R3按钮 | R3 button
135 }
137 return "Select"; // 选择按钮 | SELECT button
138 }
140 return "Start"; // 开始按钮 | START button
141 }
143 return "Home"; // 首页按钮 | HOME button
144 }
145 default: {
146 return {}; // 未知按钮返回空字符串 | Unknown button returns empty string
147 }
148 }
149}
150
151/**
152 * @~Chinese
153 * @brief 向蓝牙模块发送 AT 指令以建立 BLE 连接。
154 * @details 依次执行 AT 指令序列,将蓝牙模块配置为主机模式并连接到指定 MAC 地址的目标设备。
155 * 指令顺序:AT+DISCON → AT+RESET → AT+ECHO=0 → AT+ROLE=0 → AT+AUTOCON=0 → AT+CON=<mac>。
156 * @param[in] bluetooth_stream 连接蓝牙模块的 Stream 对象。
157 * 注意:此对象必须与传入 CodexPadFrameDecoder 的 Stream 是同一个实例(如 Serial),
158 * 因为解码器正是从这同一个串口读取手柄发来的数据。
159 * @param[in] bluetooth_device_address 目标设备的 MAC 地址,格式为 "XX:XX:XX:XX:XX:XX"。
160 */
161/**
162 * @~English
163 * @brief Send AT commands to the Bluetooth module to establish a BLE connection.
164 * @details Executes a sequence of AT commands to configure the Bluetooth module as master and connect to the target device
165 * at the specified MAC address.
166 * Command order: AT+DISCON → AT+RESET → AT+ECHO=0 → AT+ROLE=0 → AT+AUTOCON=0 → AT+CON=<mac>.
167 * @param[in] bluetooth_stream Stream connected to the Bluetooth module.
168 * Note: This must be the same Stream instance that is passed to the CodexPadFrameDecoder
169 * (e.g., Serial), as the decoder reads incoming data from this same serial port.
170 * @param[in] bluetooth_device_address MAC address of the target device in format "XX:XX:XX:XX:XX:XX".
171 */
172void Connect(Stream &bluetooth_stream, const String &bluetooth_device_address) {
173 if (bluetooth_device_address.length() != 17 || bluetooth_device_address[2] != ':' || bluetooth_device_address[5] != ':' ||
174 bluetooth_device_address[8] != ':' || bluetooth_device_address[11] != ':' || bluetooth_device_address[14] != ':') {
175 g_debug_serial.println("Error: Invalid MAC address format. Expected: XX:XX:XX:XX:XX:XX");
176 while (true);
177 }
178
179 g_debug_serial.print("Start to connect ");
180 g_debug_serial.println(kBluetoothDeviceAddress);
181
182 // 模块可能处于连接状态,先发送断开指令,确保模块是未连接状态。
183 // The module may be in a connected state. Send the disconnection command first to ensure the module is in an unconnected state.
184 bluetooth_stream.println("AT+DISCON");
185 delay(100);
186
187 // 软件复位蓝牙芯片,清除所有配对和配置数据。
188 // Software reset BLE chip, clear all pairing and configuration data.
189 bluetooth_stream.println("AT+RESET");
190 delay(100);
191
192 // 关闭AT信息回显。
193 // Close AT information echo.
194 bluetooth_stream.println("AT+ECHO=0");
195 delay(100);
196
197 // 设置模块为主机模式,使其能够主动连接从机蓝牙。
198 // Set the module to host mode so that it can actively connect to the BLE of the slave device.
199 bluetooth_stream.println("AT+ROLE=0");
200 delay(100);
201
202 // 关闭模块的蓝牙自动连接模式。
203 // Disable the module's automatic Bluetooth connection mode.
204 bluetooth_stream.println("AT+AUTOCON=0");
205 delay(100);
206
207 // 使用指定的MAC地址发起与从机蓝牙连接。
208 // Initiate BLE connection with the slave using the specified MAC address.
209 bluetooth_stream.print("AT+CON=");
210 bluetooth_stream.println(bluetooth_device_address);
211 delay(100);
212
213 g_debug_serial.println("Connected");
214}
215} // namespace
216
217void setup() {
218 g_debug_serial.begin(115200);
219
220 Serial.begin(kBluetoothModuleSerialBaudRate);
221
222 Connect(Serial, kBluetoothDeviceAddress);
223}
224
225void loop() {
226 // 重要:Update()方法必须在循环中尽可能频繁地调用,不能添加延时。
227 // 该方法负责处理所有接收到的蓝牙数据包,延时会导致数据丢失和响应延迟。
228 // 对于实时控制应用,必须保持高频率调用以确保及时响应手柄输入。
229 // Important: Update() method must be called as frequently as possible in the loop, no delays should be added.
230 // This method processes all received Bluetooth packets, delays will cause data loss and response lag.
231 // For real-time control applications, high-frequency calls are essential to ensure prompt response to gamepad input.
232 g_codex_pad_frame_decoder.Update();
233
234 for (auto button : kAllButtons) {
235 // 检测按钮是否刚刚按下(从弹起变为按下)。
236 // Check if button was just pressed (transition from released to pressed).
237 if (g_codex_pad_frame_decoder.pressed(button)) {
238 g_debug_serial.print("Button ");
239 g_debug_serial.print(ButtonToString(button));
240 g_debug_serial.println(": pressed");
241 }
242
243 // 检测按钮是否刚刚释放(从按下变为弹起)。
244 // Check if button was just released (transition from pressed to released).
245 else if (g_codex_pad_frame_decoder.released(button)) {
246 g_debug_serial.print("Button ");
247 g_debug_serial.print(ButtonToString(button));
248 g_debug_serial.println(": released");
249 }
250
251 // 检测按钮是否持续按下状态。
252 // Check if button is holding.
253 else if (g_codex_pad_frame_decoder.holding(button)) {
254 g_debug_serial.print("Button ");
255 g_debug_serial.print(ButtonToString(button));
256 g_debug_serial.println(": holding");
257 }
258 }
259
260 // 检测左右两个摇杆的X轴或Y轴是否有显著变化。
261 // Check if left and right sticks X or Y axes have significant changes.
262 if (g_codex_pad_frame_decoder.HasAxisValueChanged(CodexPadFrameDecoder::Axis::kLeftStickX, kAxisThreshold) ||
263 g_codex_pad_frame_decoder.HasAxisValueChanged(CodexPadFrameDecoder::Axis::kLeftStickY, kAxisThreshold) ||
264 g_codex_pad_frame_decoder.HasAxisValueChanged(CodexPadFrameDecoder::Axis::kRightStickX, kAxisThreshold) ||
265 g_codex_pad_frame_decoder.HasAxisValueChanged(CodexPadFrameDecoder::Axis::kRightStickY, kAxisThreshold)) {
266 // 打印摇杆轴的当前值(0-255)。
267 // Print current joystick axis values (0-255).
268 g_debug_serial.print("L(X:");
269 g_debug_serial.print(
270 g_codex_pad_frame_decoder.axis_value(CodexPadFrameDecoder::Axis::kLeftStickX)); // 左摇杆X轴当前值 | Left stick X axis current value
271 g_debug_serial.print(", Y:");
272 g_debug_serial.print(
273 g_codex_pad_frame_decoder.axis_value(CodexPadFrameDecoder::Axis::kLeftStickY)); // 左摇杆Y轴当前值 | Left stick Y axis current value
274 g_debug_serial.print("), R(X:");
275 g_debug_serial.print(
276 g_codex_pad_frame_decoder.axis_value(CodexPadFrameDecoder::Axis::kRightStickX)); // 右摇杆X轴当前值 | Right stick X axis current value
277 g_debug_serial.print(", Y:");
278 g_debug_serial.print(
279 g_codex_pad_frame_decoder.axis_value(CodexPadFrameDecoder::Axis::kRightStickY)); // 右摇杆Y轴当前值 | Right stick Y axis current value
280 g_debug_serial.println(")");
281 }
282}
CodexPad 手柄数据帧解码主类。
Button
手柄按键掩码枚举。