GamepadCodec Arduino Lib 1.0.1
Loading...
Searching...
No Matches
basic_polling.ino
Go to the documentation of this file.
1
2/**
3 * @~English
4 * @file decoder/ble_serial_module/basic_polling/basic_polling.ino
5 * @example decoder/ble_serial_module/basic_polling/basic_polling.ino
6 * @brief The demonstration regularly prints the status of all buttons and joystick values using a basic polling method.
7 * @details This example first sends AT commands to connect to a target device, then enters a simple polling loop. Every 30
8 * milliseconds, it prints the current state of all buttons (pressed/released) and the raw analog values (0‑255) of
9 * both joysticks. It showcases the fundamental usage of `gamepad::input::Tracker[Button]` for discrete button queries
10 * and `gamepad::input::Tracker[Axis]` for continuous joystick readings.
11 * @note This example uses a simple timing mechanism (`millis()`) to print at a fixed interval, which is suitable for
12 * monitoring or logging. For real‑time control, ensure `Update()` is called as frequently as possible without blocking
13 * delays.
14 * @see gamepad::codec::Decoder::Update
15 */
16/**
17 * @~Chinese
18 * @file decoder/ble_serial_module/basic_polling/basic_polling.ino
19 * @example decoder/ble_serial_module/basic_polling/basic_polling.ino
20 * @brief 演示通过基本轮询方式定期打印所有按钮状态与摇杆值。
21 * @details 本示例先通过 AT 指令连接到指定的蓝牙设备,然后进入轮询循环。
22 * 每隔 30 毫秒,它会打印所有按钮的当前状态(按下/弹起)以及两个摇杆的原始模拟值(0‑255)。
23 * 它展示了 `gamepad::input::Tracker[Button]` 用于离散按钮查询和 `gamepad::input::Tracker[Axis]`
24 * 用于连续摇杆读取的基本用法。
25 * @note 本示例使用简单的定时机制(`millis()`)以固定间隔打印,适用于状态监控或日志记录。
26 * 对于实时控制应用,请确保尽可能频繁地调用 `Update()` 且无阻塞延时。
27 * @see gamepad::codec::Decoder::Update
28 */
29
30#include <SoftwareSerial.h>
31
33
34/**
35 * IMPORTANT:
36 * This using directive is REQUIRED to directly access `Button` and `Axis`.
37 * Without it, you must write the fully qualified names:
38 * gamepad::input::Button::kUp
39 * gamepad::input::Axis::kLeftStickX
40 * Forgetting this line will cause compile errors when using Button or Axis.
41 */
42/**
43 * 重要:
44 * 必须使用该命名空间,否则无法直接访问 `Button` 和 `Axis`。
45 * 如果没有这一行,就必须写成完整限定名:
46 * gamepad::input::Button::kUp
47 * gamepad::input::Axis::kLeftStickX
48 * 忘记引入命名空间会导致编译失败。
49 */
50using namespace gamepad::input;
51
52namespace {
53// Debug serial port pin definitions.
54// Connect external serial port tools to these pins to view debug output.
55// 调试串口引脚定义。
56// 将外部串口工具连接到这些引脚以查看调试输出。
57constexpr uint8_t kDebugSerialRxPin = 6; // RX pin for debug output | 调试输出接收引脚
58constexpr uint8_t kDebugSerialTxPin = 5; // TX pin for debug output | 调试输出发送引脚
59
60// Replace with your target device's Bluetooth device address.
61// 替换为目标设备的 Bluetooth device address。
62const String kBluetoothDeviceAddress = "16:00:00:00:03:05";
63
64// The serial port baud rate for communication with the Bluetooth module (please refer to the module data manual, replace with
65// the corresponding baud rate for the module).
66// 与蓝牙模块通信的串口波特率(请查阅模块数据手册,替换为模块对应的波特率)。
67constexpr uint32_t kBluetoothModuleSerialBaudRate = 115200;
68
69// The serial instance passed in by the decoder here is the same as the one passed in Connect().
70// 此处解码器传入的串口实例,与 Connect() 中传入的为同一个串口实例。
71gamepad::codec::Decoder g_decoder(Serial);
72
73// A soft serial port used for debugging output.
74// 用于调试输出的软串口。
75SoftwareSerial g_debug_serial(kDebugSerialRxPin, kDebugSerialTxPin);
76
77/**
78 * @~English
79 * @brief Send AT commands to the Bluetooth module to establish a BLE connection.
80 * @details Executes a sequence of AT commands to configure the Bluetooth module as master and connect to the target device
81 * at the specified MAC address.
82 * Command order: AT+DISCON → AT+RESET → AT+ECHO=0 → AT+ROLE=0 → AT+AUTOCON=0 → AT+CON=<mac>.
83 * @param[in] bluetooth_stream Stream connected to the Bluetooth module.
84 * Note: This must be the same Stream instance that is passed to the decoder
85 * (e.g., Serial), as the decoder reads incoming data from this same serial port.
86 * @param[in] bluetooth_device_address MAC address of the target device in format "XX:XX:XX:XX:XX:XX".
87 */
88/**
89 * @~Chinese
90 * @brief 向蓝牙模块发送 AT 指令以建立 BLE 连接。
91 * @details 依次执行 AT 指令序列,将蓝牙模块配置为主机模式并连接到指定 MAC 地址的目标设备。
92 * 指令顺序:AT+DISCON → AT+RESET → AT+ECHO=0 → AT+ROLE=0 → AT+AUTOCON=0 → AT+CON=<mac>。
93 * @param[in] bluetooth_stream 连接蓝牙模块的 Stream 对象。
94 * 注意:此对象必须与传入解码器的 Stream 是同一个实例(如 Serial),
95 * 因为解码器正是从这同一个串口读取手柄发来的数据。
96 * @param[in] bluetooth_device_address 目标设备的 MAC 地址,格式为 "XX:XX:XX:XX:XX:XX"。
97 */
98void Connect(Stream &bluetooth_stream, const String &bluetooth_device_address) {
99 if (bluetooth_device_address.length() != 17 || bluetooth_device_address[2] != ':' || bluetooth_device_address[5] != ':' ||
100 bluetooth_device_address[8] != ':' || bluetooth_device_address[11] != ':' || bluetooth_device_address[14] != ':') {
101 g_debug_serial.println("Error: Invalid MAC address format. Expected: XX:XX:XX:XX:XX:XX");
102 while (true);
103 }
104
105 g_debug_serial.print("Start to connect ");
106 g_debug_serial.println(kBluetoothDeviceAddress);
107
108 // The module may be in a connected state. Send the disconnection command first to ensure the module is in an unconnected
109 // state.
110 // 模块可能处于连接状态,先发送断开指令,确保模块是未连接状态。
111 bluetooth_stream.println("AT+DISCON");
112 delay(100);
113
114 // Software reset BLE chip, clear all pairing and configuration data.
115 // 软件复位蓝牙芯片,清除所有配对和配置数据。
116 bluetooth_stream.println("AT+RESET");
117 delay(100);
118
119 // Close AT information echo.
120 // 关闭AT信息回显。
121 bluetooth_stream.println("AT+ECHO=0");
122 delay(100);
123
124 // Set the module to host mode so that it can actively connect to the BLE of the slave device.
125 // 设置模块为主机模式,使其能够主动连接从机蓝牙。
126 bluetooth_stream.println("AT+ROLE=0");
127 delay(100);
128
129 // Disable the module's automatic Bluetooth connection mode.
130 // 关闭模块的蓝牙自动连接模式。
131 bluetooth_stream.println("AT+AUTOCON=0");
132 delay(100);
133
134 // Initiate BLE connection with the slave using the specified MAC address.
135 // 使用指定的MAC地址发起与从机蓝牙连接。
136 bluetooth_stream.print("AT+CON=");
137 bluetooth_stream.println(bluetooth_device_address);
138 delay(100);
139
140 g_debug_serial.println("Connected");
141}
142} // namespace
143
144void setup() {
145 g_debug_serial.begin(115200);
146
147 Serial.begin(kBluetoothModuleSerialBaudRate);
148
149 Connect(Serial, kBluetoothDeviceAddress);
150}
151
152void loop() {
153 // ==========================================================================
154 // 🔴 CRITICAL: Call Update() as frequently as possible in loop()
155 // ==========================================================================
156 // • Update() processes incoming Bluetooth packets from the gamepad.
157 // • Any delay(...) or long blocking code WILL cause:
158 // - Packet loss
159 // - Input lag
160 // - Unstable connection
161 //
162 // • For real-time control, call Update() every loop iteration
163 // without any blocking operations
164 //
165 // 🔴【重要】Update() 必须在 loop() 中尽可能高频调用
166 // • Update() 负责处理来自手柄的蓝牙数据包。
167 // • 任何形式的 delay 或阻塞代码都会导致:
168 // - 数据丢失
169 // - 响应延迟
170 // - 连接不稳定
171 //
172 // • 实时控制应用中,必须每轮循环都调用 Update(),不可阻塞
173 // ==========================================================================
174 const Tracker &it = g_decoder.Update();
175 // ==========================================================================
176 // Tracker: Gamepad Input Snapshot & Change Engine
177 // ==========================================================================
178 // • Returned by Update()
179 // • Maintains previous and current input snapshots
180 // • Enables edge detection and delta detection
181 //
182 // Tracker 由 Update() 返回
183 // 内部保存上一帧和当前帧的输入数据
184 // 支持边沿检测和差值检测
185 //
186 // 📚 https://codexpad.github.io/gamepad_input_arduino_lib/
187 // ==========================================================================
188
189 static uint32_t s_print_time = 0;
190 if (s_print_time != 0 && s_print_time + 30 > millis()) {
191 return;
192 }
193
194 s_print_time = millis();
195
196 // ------------------------------------------------------------------
197 // Button states (boolean -> uint)
198 // operator[] returns bool:
199 // true = pressed
200 // false = released
201 //
202 // 按钮状态(bool 类型,打印时被隐式转换为 1 / 0)
203 // true : 按下
204 // false : 弹起
205 // ------------------------------------------------------------------
206 g_debug_serial.print("Up:");
207 g_debug_serial.print(it[Button::kUp]);
208 g_debug_serial.print(", ");
209 g_debug_serial.print("Down:");
210 g_debug_serial.print(it[Button::kDown]);
211 g_debug_serial.print(", ");
212 g_debug_serial.print("Left:");
213 g_debug_serial.print(it[Button::kLeft]);
214 g_debug_serial.print(", ");
215 g_debug_serial.print("Right:");
216 g_debug_serial.print(it[Button::kRight]);
217 g_debug_serial.print(", ");
218 g_debug_serial.print("Square(X):");
219 g_debug_serial.print(it[Button::kSquareX]);
220 g_debug_serial.print(", ");
221 g_debug_serial.print("Triangle(Y):");
222 g_debug_serial.print(it[Button::kTriangleY]);
223 g_debug_serial.print(", ");
224 g_debug_serial.print("Cross(A):");
225 g_debug_serial.print(it[Button::kCrossA]);
226 g_debug_serial.print(", ");
227 g_debug_serial.print("Circle(B):");
228 g_debug_serial.print(it[Button::kCircleB]);
229 g_debug_serial.print(", ");
230 g_debug_serial.print("L1:");
231 g_debug_serial.print(it[Button::kL1]);
232 g_debug_serial.print(", ");
233 g_debug_serial.print("L2:");
234 g_debug_serial.print(it[Button::kL2]);
235 g_debug_serial.print(", ");
236 g_debug_serial.print("L3:");
237 g_debug_serial.print(it[Button::kL3]);
238 g_debug_serial.print(", ");
239 g_debug_serial.print("R1:");
240 g_debug_serial.print(it[Button::kR1]);
241 g_debug_serial.print(", ");
242 g_debug_serial.print("R2:");
243 g_debug_serial.print(it[Button::kR2]);
244 g_debug_serial.print(", ");
245 g_debug_serial.print("R3:");
246 g_debug_serial.print(it[Button::kR3]);
247 g_debug_serial.print(", ");
248 g_debug_serial.print("Select:");
249 g_debug_serial.print(it[Button::kSelect]);
250 g_debug_serial.print(", ");
251 g_debug_serial.print("Start:");
252 g_debug_serial.print(it[Button::kStart]);
253 g_debug_serial.print(", ");
254 g_debug_serial.print("Home:");
255 g_debug_serial.print(it[Button::kHome]);
256 g_debug_serial.print(", ");
257
258 // ------------------------------------------------------------------
259 // Joystick axis values (0–255)
260 // Center position ≈ 128
261 // Smaller → left / down
262 // Larger → right / up
263 //
264 // 摇杆轴数据(0~255)
265 // 中间值约 128
266 // 越小越左 / 下,越大越右 / 上
267 // ------------------------------------------------------------------
268 g_debug_serial.print("L(X:");
269 g_debug_serial.print(it[Axis::kLeftStickX]);
270 g_debug_serial.print(", Y:");
271 g_debug_serial.print(it[Axis::kLeftStickY]);
272 g_debug_serial.print("), R(X:");
273 g_debug_serial.print(it[Axis::kRightStickX]);
274 g_debug_serial.print(", Y:");
275 g_debug_serial.print(it[Axis::kRightStickY]);
276 g_debug_serial.println(")");
277}
Decoder class responsible for parsing gamepad input frames from a byte stream and updating the state ...