CodexPad Arduino Lib 2.1.9
Loading...
Searching...
No Matches
codex_pad.h
1#pragma once
2
3#include <functional>
4#include <mutex>
5#include <optional>
6#include <queue>
7#include <string>
8#include <vector>
9
10#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include)
11// 如果支持 __has_include,则检查 "NimBLEDevice.h" 是否存在
12// If __has_include is supported, check for the existence of "NimBLEDevice.h"
13#if !__has_include("NimBLEDevice.h")
14#error "NimBLEDevice.h not found. CodexPad requires the NimBLE-Arduino library."
15#error "Please install the latest version of NimBLE-Arduino via Arduino Library Manager or PlatformIO's lib_deps."
16#error ""
17#error "未找到 NimBLEDevice.h 文件。CodexPad 依赖于 NimBLE-Arduino 库。"
18#error "请通过 Arduino 库管理器或 PlatformIO 的 lib_deps 安装最新版本的 NimBLE-Arduino 库。"
19#endif
20#else
21/*
22 * 对于不支持 __has_include 的旧编译器,我们无法在编译前可靠地检查头文件是否存在。
23 * 此处放置一个强提醒,如果因为缺少此头文件导致编译错误,解决方法是相同的。
24 * For older compilers that do not support __has_include, we cannot reliably check for the header before compilation.
25 * A strong reminder is placed here. If a compilation error occurs due to this missing header, the solution is the same.
26 */
27#warning "CodexPad depends on NimBLE-Arduino. Ensure 'NimBLEDevice.h' is in your include path."
28#warning "If you encounter errors about missing 'NimBLEDevice.h', please install the latest NimBLE-Arduino library."
29#warning ""
30#warning "CodexPad 依赖于 NimBLE-Arduino 库。请确保 'NimBLEDevice.h' 在您的头文件包含路径中。"
31#warning "如果遇到找不到 'NimBLEDevice.h' 的错误,请安装最新版本的 NimBLE-Arduino 库。"
32#endif
33
34#include "NimBLEDevice.h"
35
36/**
37 * @~English
38 * @class CodexPad
39 * @brief CodexPad main class
40 */
41/**
42 * @~Chinese
43 * @class CodexPad
44 * @brief CodexPad主类
45 */
46class CodexPad {
47 public:
48 /**
49 * @~English
50 * @brief Number of axis values
51 */
52 /**
53 * @~Chinese
54 * @brief 轴值数量
55 */
56 static constexpr size_t kAxisValueNum = 4;
57
58 /**
59 * @~English
60 * @brief Axis center value
61 */
62 /**
63 * @~Chinese
64 * @brief 轴中心值
65 */
66 static constexpr uint8_t kAxisCenter = 0x80;
67
68 /**
69 * @~English
70 * @enum CodexPad::TxPower
71 * @brief CodexPad transmission power levels
72 */
73 /**
74 * @~Chinese
75 * @enum CodexPad::TxPower
76 * @brief CodexPad发射功率等级
77 */
78 enum class TxPower : int8_t {
79 /**
80 * @~English
81 * @brief -16 dBm
82 */
83 /**
84 * @~Chinese
85 * @brief -16 dBm
86 */
88
89 /**
90 * @~English
91 * @brief -12 dBm
92 */
93 /**
94 * @~Chinese
95 * @brief -12 dBm
96 */
98
99 /**
100 * @~English
101 * @brief -8 dBm
102 */
103 /**
104 * @~Chinese
105 * @brief -8 dBm
106 */
108
109 /**
110 * @~English
111 * @brief -5 dBm
112 */
113 /**
114 * @~Chinese
115 * @brief -5 dBm
116 */
118
119 /**
120 * @~English
121 * @brief -3 dBm
122 */
123 /**
124 * @~Chinese
125 * @brief -3 dBm
126 */
128
129 /**
130 * @~English
131 * @brief -1 dBm
132 */
133 /**
134 * @~Chinese
135 * @brief -1 dBm
136 */
138
139 /**
140 * @~English
141 * @brief 0 dBm
142 */
143 /**
144 * @~Chinese
145 * @brief 0 dBm
146 */
147 k0dBm = 0,
148
149 /**
150 * @~English
151 * @brief 1 dBm
152 */
153 /**
154 * @~Chinese
155 * @brief 1 dBm
156 */
157 k1dBm = 1,
158
159 /**
160 * @~English
161 * @brief 2 dBm
162 */
163 /**
164 * @~Chinese
165 * @brief 2 dBm
166 */
167 k2dBm = 2,
168
169 /**
170 * @~English
171 * @brief 3 dBm
172 */
173 /**
174 * @~Chinese
175 * @brief 3 dBm
176 */
177 k3dBm = 3,
178
179 /**
180 * @~English
181 * @brief 4 dBm
182 */
183 /**
184 * @~Chinese
185 * @brief 4 dBm
186 */
187 k4dBm = 4,
188
189 /**
190 * @~English
191 * @brief 5 dBm
192 */
193 /**
194 * @~Chinese
195 * @brief 5 dBm
196 */
197 k5dBm = 5,
198
199 /**
200 * @~English
201 * @brief 6 dBm
202 */
203 /**
204 * @~Chinese
205 * @brief 6 dBm
206 */
208 };
209
210 /**
211 * @~English
212 * @enum CodexPad::Button
213 * @brief Button Type of CodexPad
214 */
215 /**
216 * @~Chinese
217 * @enum CodexPad::Button
218 * @brief CodexPad按键类型
219 */
220 enum class Button : uint32_t {
221 /**
222 * @~English
223 * @brief Up
224 */
225 /**
226 * @~Chinese
227 * @brief 上
228 */
229 kUp = uint32_t{1} << 0,
230
231 /**
232 * @~English
233 * @brief Down
234 */
235 /**
236 * @~Chinese
237 * @brief 下
238 */
239 kDown = uint32_t{1} << 1,
240
241 /**
242 * @~English
243 * @brief Left
244 */
245 /**
246 * @~Chinese
247 * @brief 左
248 */
249 kLeft = uint32_t{1} << 2,
250
251 /**
252 * @~English
253 * @brief Right
254 */
255 /**
256 * @~Chinese
257 * @brief 右
258 */
259 kRight = uint32_t{1} << 3,
260
261 /**
262 * @~English
263 * @brief Square or X
264 */
265 /**
266 * @~Chinese
267 * @brief 方形或者X
268 */
269 kSquareX = uint32_t{1} << 4,
270
271 /**
272 * @~English
273 * @brief Triangle or Y
274 */
275 /**
276 * @~Chinese
277 * @brief 三角形或者Y
278 */
279 kTriangleY = uint32_t{1} << 5,
280
281 /**
282 * @~English
283 * @brief Cross or A
284 */
285 /**
286 * @~Chinese
287 * @brief 叉或者A
288 */
289 kCrossA = uint32_t{1} << 6,
290
291 /**
292 * @~English
293 * @brief Circle or B
294 */
295 /**
296 * @~Chinese
297 * @brief 圆形或者B
298 */
299 kCircleB = uint32_t{1} << 7,
300
301 /**
302 * @~English
303 * @brief L1
304 */
305 /**
306 * @~Chinese
307 * @brief L1
308 */
309 kL1 = uint32_t{1} << 8,
310
311 /**
312 * @~English
313 * @brief L2
314 */
315 /**
316 * @~Chinese
317 * @brief L2
318 */
319 kL2 = uint32_t{1} << 9,
320
321 /**
322 * @~English
323 * @brief L3
324 */
325 /**
326 * @~Chinese
327 * @brief L3
328 */
329 kL3 = uint32_t{1} << 10,
330
331 /**
332 * @~English
333 * @brief R1
334 */
335 /**
336 * @~Chinese
337 * @brief R1
338 */
339 kR1 = uint32_t{1} << 11,
340
341 /**
342 * @~English
343 * @brief R2
344 */
345 /**
346 * @~Chinese
347 * @brief R2
348 */
349 kR2 = uint32_t{1} << 12,
350
351 /**
352 * @~English
353 * @brief R3
354 */
355 /**
356 * @~Chinese
357 * @brief R3
358 */
359 kR3 = uint32_t{1} << 13,
360
361 /**
362 * @~English
363 * @brief Select
364 */
365 /**
366 * @~Chinese
367 * @brief 选择
368 */
369 kSelect = uint32_t{1} << 14,
370
371 /**
372 * @~English
373 * @brief Start
374 */
375 /**
376 * @~Chinese
377 * @brief 开始
378 */
379 kStart = uint32_t{1} << 15,
380
381 /**
382 * @~English
383 * @brief Home
384 */
385 /**
386 * @~Chinese
387 * @brief 首页
388 */
389 kHome = uint32_t{1} << 16,
390 };
391
392 /**
393 * @~English
394 * @enum CodexPad::Axis
395 * @brief Axis
396 */
397 /**
398 * @~Chinese
399 * @enum CodexPad::Axis
400 * @brief 轴
401 */
402 enum class Axis : size_t {
403 /**
404 * @~English
405 * @brief Left stick X axis
406 */
407 /**
408 * @~Chinese
409 * @brief 左摇杆X轴
410 */
412
413 /**
414 * @~English
415 * @brief Left stick Y axis
416 */
417 /**
418 * @~Chinese
419 * @brief 左摇杆Y轴
420 */
422
423 /**
424 * @~English
425 * @brief Right stick X axis
426 */
427 /**
428 * @~Chinese
429 * @brief 右摇杆X轴
430 */
432
433 /**
434 * @~English
435 * @brief Right stick Y axis
436 */
437 /**
438 * @~Chinese
439 * @brief 右摇杆Y轴
440 */
442 };
443
444#if __cplusplus < 201402L
445 static constexpr uint32_t ButtonMask() { return 0; }
446
447 /**
448 * @~English
449 * @brief Combines multiple button identifiers into a single 32-bit button mask.
450 * @details This is a convenience function for creating a bitmask that represents a combination of specific buttons (e.g., Start + A).
451 * The resulting mask can be used as a parameter for methods like @ref ScanAndConnect, or for checking against the combined state
452 * returned by @ref button_states.
453 * @param[in] button One or more button identifiers from the @ref Button enumeration.
454 * @return A 32-bit unsigned integer where the bits corresponding to the input @ref Button values are set to 1.
455 * @note This is a `constexpr` function, allowing the mask to be computed at compile time.
456 * Example - Creating a mask for the Start and Cross/A buttons:
457 * @code
458 * // Create a mask representing the Start and Cross/A buttons held simultaneously.
459 * constexpr auto kStartAMask = CodexPad::ButtonMask(CodexPad::Button::kStart, CodexPad::Button::kCrossA);
460 *
461 * // Use the mask to scan and connect to a device holding these buttons.
462 * codex_pad.ScanAndConnect(kStartAMask);
463 *
464 * // Alternatively, check the current combined button state against the mask.
465 * if ((codex_pad.button_states() & kStartAMask) == kStartAMask) {
466 * // Both Start and Cross/A are currently pressed.
467 * }
468 * @endcode
469 */
470 /**
471 * @~Chinese
472 * @brief 将多个按键标识符组合成一个32位的按钮掩码。
473 * @details 这是一个工具函数,用于创建一个表示特定按键组合(例如 Start + A)的位掩码。
474 * 生成的掩码可作为参数用于 @ref ScanAndConnect 等方法,或用于与 @ref button_states 返回的组合状态进行比较。
475 * @param[in] button 一个或多个来自 @ref Button 枚举的按键标识符。
476 * @return 一个32位无符号整数,其中与输入的 @ref Button 值对应的位被设为1。
477 * @note 这是一个 `constexpr` 函数,允许掩码在编译时计算。
478 * 示例 - 为 Start 和 Cross/A 按键创建掩码:
479 * @code
480 * // 创建一个表示 Start 和 Cross/A 按键被同时按下的掩码。
481 * constexpr auto kStartAMask = CodexPad::ButtonMask(CodexPad::Button::kStart, CodexPad::Button::kCrossA);
482 *
483 * // 使用该掩码来扫描并连接到正按住这些按键的设备。
484 * codex_pad.ScanAndConnect(kStartAMask);
485 *
486 * // 或者,用该掩码检查当前的组合按键状态。
487 * if ((codex_pad.button_states() & kStartAMask) == kStartAMask) {
488 * // Start 和 Cross/A 按键当前均被按下。
489 * }
490 * @endcode
491 */
492 template <typename FirstButton, typename... RestButtons>
493 static constexpr uint32_t ButtonMask(FirstButton first, RestButtons... rest) {
494 return static_cast<uint32_t>(first) | ButtonMask(rest...);
495 }
496#else
497
498 /**
499 * @~English
500 * @brief Combines multiple button identifiers into a single 32-bit button mask.
501 * @details This is a convenience function for creating a bitmask that represents a combination of specific buttons (e.g., Start + A).
502 * The resulting mask can be used as a parameter for methods like @ref ScanAndConnect, or for checking against the combined state
503 * returned by @ref button_states.
504 * @param[in] button One or more button identifiers from the @ref Button enumeration.
505 * @return A 32-bit unsigned integer where the bits corresponding to the input @ref Button values are set to 1.
506 * @note This is a `constexpr` function, allowing the mask to be computed at compile time.
507 * Example - Creating a mask for the Start and Cross/A buttons:
508 * @code
509 * // Create a mask representing the Start and Cross/A buttons held simultaneously.
510 * constexpr auto kStartAMask = CodexPad::ButtonMask(CodexPad::Button::kStart, CodexPad::Button::kCrossA);
511 *
512 * // Use the mask to scan and connect to a device holding these buttons.
513 * codex_pad.ScanAndConnect(kStartAMask);
514 *
515 * // Alternatively, check the current combined button state against the mask.
516 * if ((codex_pad.button_states() & kStartAMask) == kStartAMask) {
517 * // Both Start and Cross/A are currently pressed.
518 * }
519 * @endcode
520 */
521 /**
522 * @~Chinese
523 * @brief 将多个按键标识符组合成一个32位的按钮掩码。
524 * @details 这是一个工具函数,用于创建一个表示特定按键组合(例如 Start + A)的位掩码。
525 * 生成的掩码可作为参数用于 @ref ScanAndConnect 等方法,或用于与 @ref button_states 返回的组合状态进行比较。
526 * @param[in] button 一个或多个来自 @ref Button 枚举的按键标识符。
527 * @return 一个32位无符号整数,其中与输入的 @ref Button 值对应的位被设为1。
528 * @note 这是一个 `constexpr` 函数,允许掩码在编译时计算。
529 * 示例 - 为 Start 和 Cross/A 按键创建掩码:
530 * @code
531 * // 创建一个表示 Start 和 Cross/A 按键被同时按下的掩码。
532 * constexpr auto kStartAMask = CodexPad::ButtonMask(CodexPad::Button::kStart, CodexPad::Button::kCrossA);
533 *
534 * // 使用该掩码来扫描并连接到正按住这些按键的设备。
535 * codex_pad.ScanAndConnect(kStartAMask);
536 *
537 * // 或者,用该掩码检查当前的组合按键状态。
538 * if ((codex_pad.button_states() & kStartAMask) == kStartAMask) {
539 * // Start 和 Cross/A 按键当前均被按下。
540 * }
541 * @endcode
542 */
543 template <typename... Button>
544 static constexpr uint32_t ButtonMask(Button... button) {
545 return (static_cast<uint32_t>(button) | ... | uint32_t{0});
546 }
547#endif
548
549 /**
550 * @~English
551 * @brief Constructor
552 */
553 /**
554 * @~Chinese
555 * @brief 构造函数
556 */
557 CodexPad();
558
559 /**
560 * @~English
561 * @brief Destructor
562 */
563 /**
564 * @~Chinese
565 * @brief 析构函数
566 */
567 ~CodexPad();
568
569 /**
570 * @~English
571 * @brief Initialize
572 */
573 /**
574 * @~Chinese
575 * @brief 初始化
576 */
577 void Init();
578
579 /**
580 * @~English
581 * @brief Connect
582 * @param[in] bluetooth_device_address The Bluetooth device address(BD_ADDR) of the CodexPad, formatted as "XX:XX:XX:XX:XX:XX", X is a number or
583 * uppercase letter, colon separated
584 * @param[in] timeout_ms Timeout in milliseconds
585 * @retval true if connected successfully
586 * @retval false if connection failed
587 * */
588 /**
589 * @~Chinese
590 * @brief 连接
591 * @param[in] bluetooth_device_address CodexPad的蓝牙设备地址(BD_ADDR),格式为"XX:XX:XX:XX:XX:XX",X为数字或者大写字母, 半角冒号分隔
592 * @param[in] timeout_ms 超时时间,单位毫秒
593 * @retval true 连接成功
594 * @retval false 连接失败
595 */
596 bool Connect(const std::string& bluetooth_device_address, const uint32_t timeout_ms = 5000);
597
598 /**
599 * @~English
600 * @brief Scans for nearby CodexPad devices and automatically connects to a device whose button state matches the specified mask.
601 * @details This method actively scans for Bluetooth devices. When it discovers one or more CodexPad devices whose current button states
602 * exactly match the provided `button_mask`, it will attempt to establish a connection.
603 * If multiple nearby devices match the button mask, the device with the strongest signal (highest RSSI) will be selected for connection.
604 * @param[in] button_mask A 32-bit button mask used to match the target device's button states.
605 * Use the `ButtonMask()` function to combine multiple @ref Button values.
606 * @retval true Connection successful (a device matching the button mask was found and connected).
607 * @retval false Connection failed (no matching device found or connection attempt failed).
608 * @note This is a blocking call. It will continuously scan until a matching device is found, a connection is established, or the internal timeout
609 * is reached.
610 * @warning **DO NOT** include `Button::kHome` in the button mask. Holding the Home button triggers a device reboot, which will interrupt the
611 * connection process. Example:
612 * @code
613 * CodexPad pad;
614 * pad.Init();
615 * // Continuously try to connect to a device where the Start and Cross/A buttons are pressed.
616 * while (!pad.ScanAndConnect(CodexPad::ButtonMask(CodexPad::Button::kStart, CodexPad::Button::kCrossA))) {
617 * // Optional: Add a small delay or other operations between attempts.
618 * delay(100);
619 * }
620 * // Once the loop exits, connection is successful.
621 * Serial.println("CodexPad connected!");
622 * @endcode
623 */
624 /**
625 * @~Chinese
626 * @brief 扫描附近的 CodexPad 设备,并自动连接到一个按键状态与指定掩码匹配的设备。
627 * @details 此方法会主动扫描蓝牙设备。当发现一个或多个 CodexPad 设备,其当前按键状态与提供的 `button_mask` 完全匹配时,它将尝试建立连接。
628 * 如果附近有多个设备都符合按键掩码,则会选择信号最强(RSSI 值最大)的设备进行连接。
629 * @param[in] button_mask 用于匹配目标设备按键状态的32位按钮掩码。
630 * 使用 `ButtonMask()` 函数来组合多个 @ref Button 枚举值。
631 * @retval true 连接成功(找到了符合按键掩码的设备并成功连接)。
632 * @retval false 连接失败(未找到匹配设备或连接尝试失败)。
633 * @note 这是一个阻塞式调用。它会持续扫描,直到找到匹配的设备、连接建立或达到内部超时时间。
634 * @warning **请勿**在按钮掩码中包含 `Button::kHome`。按住 Home 键会触发设备重启,这将中断连接过程。
635 * 示例:
636 * @code
637 * CodexPad pad;
638 * pad.Init();
639 * // 持续尝试连接到 Start 和 Cross/A 按键被同时按下的设备。
640 * while (!pad.ScanAndConnect(CodexPad::ButtonMask(CodexPad::Button::kStart, CodexPad::Button::kCrossA))) {
641 * // 可选:在尝试之间添加短暂延时或其他操作。
642 * delay(100);
643 * }
644 * // 循环退出,意味着连接成功。
645 * Serial.println("CodexPad 连接成功!");
646 * @endcode
647 */
648 bool ScanAndConnect(const uint32_t button_mask);
649
650 /**
651 * @~English
652 * @brief Disconnect
653 */
654 /**
655 * @~Chinese
656 * @brief 断开连接
657 */
658 void Disconnect() { Reset(); }
659
660 /**
661 * @~English
662 * @brief Update, need to be called in Loop
663 */
664 /**
665 * @~Chinese
666 * @brief 更新,需要在Loop中不断调用
667 */
668 void Update();
669
670 /**
671 * @~English
672 * @brief Is connected
673 * @retval true if connected
674 * @retval false if not
675 */
676 /**
677 * @~Chinese
678 * @brief 是否连接
679 * @retval true 已连接
680 * @retval false 未连接
681 */
682 bool is_connected() const;
683
684 /**
685 * @~English
686 * @brief Set transmission power, only effective when connected, immediately effective for current connection, effective for next connection
687 * @param[in] power Transmission power
688 * @retval true Success
689 * @retval false Fail
690 */
691 /**
692 * @~Chinese
693 * @brief 设置发射功率,连接状态下调用,立即生效于当前连接,下次连接生效
694 * @param[in] power 发射功率
695 * @retval true 成功
696 * @retval false 失败
697 */
698 bool set_remote_tx_power(const TxPower power);
699
700 /**
701 * @~English
702 * @brief Get model number of the CodexPad
703 * @return Model number of the CodexPad
704 */
705 /**
706 * @~Chinese
707 * @brief 获取CodexPad的型号
708 * @return CodexPad的型号
709 */
710 inline const std::string& remote_device_name() const { return remote_device_name_; }
711
712 /**
713 * @~English
714 * @brief Get model number of the CodexPad
715 * @return Model number of the CodexPad
716 */
717 /**
718 * @~Chinese
719 * @brief 获取CodexPad的型号
720 * @return CodexPad的型号
721 */
722 inline const std::string& remote_model_number() const { return remote_model_number_; }
723
724 /**
725 * @~English
726 * @brief Get firmware version of the CodexPad
727 * @return Firmware version of the CodexPad
728 */
729 /**
730 * @~Chinese
731 * @brief 获取CodexPad的固件版本
732 * @return CodexPad的固件版本
733 */
734 inline const std::array<uint8_t, 3> remote_firmware_version() const { return remote_firmware_version_; }
735
736 /**
737 * @~English
738 * @brief check if a button is pressed
739 * @retval true if the button is pressed
740 * @retval false if the button is not pressed
741 */
742 /**
743 * @~Chinese
744 * @brief 查询按键是否被按下
745 * @retval true 按键被按下
746 * @retval false 按键没有被按下
747 */
748 bool pressed(const Button button) const;
749
750 /**
751 * @~English
752 * @brief check if a button is released
753 * @retval true if the button is released
754 * @retval false if the button is not released
755 */
756 /**
757 * @~Chinese
758 * @brief 查询按键是否被释放
759 * @retval true 按键被释放
760 * @retval false 按键没有被释放
761 */
762 bool released(const Button button) const;
763
764 /**
765 * @~English
766 * @brief check if a button is held
767 * @retval true if the button is held
768 * @retval false if the button is not held
769 */
770 /**
771 * @~Chinese
772 * @brief 查询按键是否被持续按下
773 * @retval true 按键被持续按下
774 * @retval false 按键没有被持续按下
775 */
776 bool holding(const Button button) const;
777
778 /**
779 * @~English
780 * @brief check if a button is pressed or held
781 * @retval true if the button is pressed or held
782 * @retval false if the button is not pressed or held
783 */
784 /**
785 * @~Chinese
786 * @brief 查询按键是否被按下或持续按下
787 * @retval true 按键被按下或持续按下
788 * @retval false 按键没有被按下或持续按下
789 */
790 bool button_state(const Button button) const;
791
792 /**
793 * @~English
794 * @brief Get all button states, return a 32-bit unsigned integer where each bit represents the state of a specific button
795 * @return A 32-bit unsigned integer where each bit represents the state of a specific button
796 * The bits are from the least significant bit (LSB, bit 0) to the most significant bit (MSB, bit 31).
797 * You can use bit-and (&) operator to check specific button states.
798 * Example:
799 * @code
800 * const uint32_t button_states = codex_pad.button_states();
801 * if ((button_states & CodexPad::Button::kUp) != 0) {
802 * // Up button is pressed
803 * }
804 * @endcode
805 */
806 /**
807 * @~Chinese
808 * @brief 以位掩码形式获取所有按键的当前状态
809 * @return 一个32位无符号整数,每一位(bit)表示 @ref Button 枚举中对应按键的状态(1为按下,0为未按下)。
810 * 位的分配从最低有效位(LSB,第0位)开始,依次对应 @ref Button 中的各个值。
811 * 可使用位与操作 (&) 配合具体的 @ref Button 枚举值来检查特定按键状态。
812 * 示例:
813 * @code
814 * const uint32_t button_states = codex_pad.button_states();
815 * if ((button_states & CodexPad::Button::kUp)) != 0) {
816 * // 方向上按键被按下
817 * }
818 * @endcode
819 */
820 uint32_t button_states() const;
821
822 /**
823 * @~English
824 * @brief Get axis value
825 * @param[in] axis Axis
826 * @return Axis value, range 0~255
827 */
828 /**
829 * @~Chinese
830 * @brief 获取轴值
831 * @param[in] axis 轴
832 * @return 轴值,范围 0~255
833 */
834 uint8_t axis_value(const Axis axis) const;
835
836 /**
837 * @~English
838 * @brief Get current values of all analog axes
839 * @return Array of axis values with length @ref kAxisValueNum (4 elements), where each element represents the position of a specific axis in the
840 * range 0-255. The array indices correspond to the @ref Axis enumeration:
841 * - Index 0: Left stick X axis (kLeftStickX)
842 * - Index 1: Left stick Y axis (kLeftStickY)
843 * - Index 2: Right stick X axis (kRightStickX)
844 * - Index 3: Right stick Y axis (kRightStickY)
845 *
846 * Typical center position value is 128 (0x80).
847 *
848 * Example:
849 * @code
850 * const auto axes = codex_pad.axis_values();
851 * const uint8_t left_stick_x = axes[static_cast<size_t>(CodexPad::Axis::kLeftStickX)]; // Get the value of the left stick X axis
852 * @endcode
853 */
854 /**
855 * @~Chinese
856 * @brief 获取所有模拟轴的当前值
857 * @return 长度为 @ref kAxisValueNum (4个元素) 的轴值数组,每个元素代表特定轴的位置,范围 0-255。数组索引与 @ref Axis 枚举对应关系:
858 * - 索引 0:左摇杆X轴 (@ref Axis::kLeftStickX)
859 * - 索引 1:左摇杆Y轴 (@ref Axis::kLeftStickY)
860 * - 索引 2:右摇杆X轴 (@ref Axis::kRightStickX)
861 * - 索引 3:右摇杆Y轴 (@ref Axis::kRightStickY)
862 *
863 * 典型中心位置值为 128 (0x80)。
864 *
865 * 示例:
866 * @code
867 * const auto axes = codex_pad.axis_values();
868 * const uint8_t left_stick_x = axes[static_cast<size_t>(CodexPad::Axis::kLeftStickX)]; // 获取左摇杆X轴值
869 * @endcode
870 */
871 std::array<uint8_t, kAxisValueNum> axis_values() const;
872
873 /**
874 * @~English
875 * @brief check if an axis value has changed
876 * @param[in] axis Axis
877 * @param[in] threshold Threshold
878 * @retval true if the axis value has changed
879 * @retval false if the axis value has not changed
880 */
881 /**
882 * @~Chinese
883 * @brief 查询轴值是否发生变化
884 * @param[in] axis 轴
885 * @param[in] threshold 阈值
886 * @retval true 轴值发生变化
887 * @retval false 轴值未发生变化
888 */
889 bool HasAxisValueChanged(const Axis axis, const uint8_t threshold) const;
890
891 /**
892 * @~English
893 * @brief Get the BLE client object
894 * @details This function returns a pointer to the internal BLE client object. The caller should not try and release/delete it.
895 * @return BLE client object
896 */
897 /**
898 * @~Chinese
899 * @brief 获取 BLE 客户端对象
900 * @details 此函数返回内部 BLE 客户端对象的指针。调用者不应尝试释放/删除它。
901 * @return BLE 客户端对象
902 */
903 NimBLEClient* ble_client() const { return ble_client_; }
904
905 private:
906 static constexpr size_t kButtonNum = 32;
907 static constexpr size_t kInputsQueueMax = 10;
908 static constexpr size_t kInputsBytes = 8;
909 struct Inputs {
910 uint32_t button_states = 0;
911 uint8_t axis_values[kAxisValueNum] = {kAxisCenter, kAxisCenter, kAxisCenter, kAxisCenter};
912 };
913
914 static_assert(sizeof(Inputs) == kInputsBytes);
915
916 bool Connect(const NimBLEAddress& address, bool async_connect, const uint32_t timeout_ms);
917 void OnNotify(const NimBLERemoteCharacteristic* remote_characteristic, const uint8_t* data, const size_t length, const bool is_notify);
918 void Reset();
919
920 mutable std::mutex mutex_;
921 NimBLEClient* ble_client_ = nullptr;
922 std::string remote_device_name_;
923 std::string remote_model_number_;
924 std::array<uint8_t, 3> remote_firmware_version_ = {0, 0, 0};
925 Inputs prev_inputs_;
926 Inputs current_inputs_;
927 std::queue<Inputs> inputs_queue_;
928};
const std::array< uint8_t, 3 > remote_firmware_version() const
Get firmware version of the CodexPad.
Definition codex_pad.h:734
uint32_t button_states() const
Get all button states, return a 32-bit unsigned integer where each bit represents the state of a spec...
const std::string & remote_model_number() const
Get model number of the CodexPad.
Definition codex_pad.h:722
bool ScanAndConnect(const uint32_t button_mask)
Scans for nearby CodexPad devices and automatically connects to a device whose button state matches t...
Definition codex_pad.cpp:51
@ kLeftStickX
Left stick X axis.
Definition codex_pad.h:411
@ kRightStickY
Right stick Y axis.
Definition codex_pad.h:441
@ kLeftStickY
Left stick Y axis.
Definition codex_pad.h:421
@ kRightStickX
Right stick X axis.
Definition codex_pad.h:431
bool pressed(const Button button) const
check if a button is pressed
void Disconnect()
Disconnect.
Definition codex_pad.h:658
static constexpr size_t kAxisValueNum
Number of axis values.
Definition codex_pad.h:56
bool Connect(const std::string &bluetooth_device_address, const uint32_t timeout_ms=5000)
Connect.
Definition codex_pad.cpp:40
@ kSelect
Select.
Definition codex_pad.h:369
@ kCrossA
Cross or A.
Definition codex_pad.h:289
@ kSquareX
Square or X.
Definition codex_pad.h:269
@ kCircleB
Circle or B.
Definition codex_pad.h:299
@ kTriangleY
Triangle or Y.
Definition codex_pad.h:279
const std::string & remote_device_name() const
Get model number of the CodexPad.
Definition codex_pad.h:710
NimBLEClient * ble_client() const
Get the BLE client object.
Definition codex_pad.h:903
static constexpr uint32_t ButtonMask(FirstButton first, RestButtons... rest)
Combines multiple button identifiers into a single 32-bit button mask.
Definition codex_pad.h:493
uint8_t axis_value(const Axis axis) const
Get axis value.
~CodexPad()
Destructor.
Definition codex_pad.cpp:32
bool HasAxisValueChanged(const Axis axis, const uint8_t threshold) const
check if an axis value has changed
static constexpr uint8_t kAxisCenter
Axis center value.
Definition codex_pad.h:66
CodexPad()
Constructor.
Definition codex_pad.cpp:30
bool set_remote_tx_power(const TxPower power)
Set transmission power, only effective when connected, immediately effective for current connection,...
bool is_connected() const
Is connected.
bool released(const Button button) const
check if a button is released
std::array< uint8_t, kAxisValueNum > axis_values() const
Get current values of all analog axes.
void Init()
Initialize.
Definition codex_pad.cpp:34
@ kMinus16dBm
-16 dBm
Definition codex_pad.h:87
@ kMinus12dBm
-12 dBm
Definition codex_pad.h:97
void Update()
Update, need to be called in Loop.
bool button_state(const Button button) const
check if a button is pressed or held
bool holding(const Button button) const
check if a button is held