diff --git a/README.md b/README.md index 2be4654..99e6d4b 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,29 @@ -# Product Name +# Chain Joystick ## Overview +Chain Joystick is a high‑precision Hall‑effect joystick input node in the M5Stack Chain series. +It supports three‑axis control signal input, including analog inputs for the X and Y axes and a digital button input for the Z axis. +The device adopts a Hall‑effect joystick, which achieves high‑precision control by detecting changes in the magnetic field. +It features a contactless design, high durability, excellent precision, and strong anti‑interference capability, ensuring product stability and long service life. +An integrated programmable RGB LED is included for status indication and interactive display. +It is suitable for applications such as human‑machine interaction and robot control. -### SKU:xxx - -Description of the product +### SKU: U205 ## Related Link +https://docs.m5stack.com/en/products/sku/U205 -- [Document & Datasheet](https://docs.m5stack.com/en/unit/product_Link) - -## Required Libraries: - -- [Adafruit_BMP280_Library](https://github.com/adafruit/Required_Libraries_Link) - -## License - -- [Product Name- MIT](LICENSE) - -## Remaining steps(Editorial Staff Look,After following the steps, remember to delete all the content below) - -1. Change [clang format check path](./.github/workflows/clang-format-check.yml#L42-L47). -2. Add License content to [LICENSE](/LICENSE). -3. Change link on line 78 of [bug-report.yml](./.github/ISSUE_TEMPLATE/bug-report.yml#L79). - -```cpp -Example -# M5Unit-ENV +# Chain Key ## Overview +Chain Key is a single‑button input node in the M5Stack Chain series. +The button adopts a hot‑swappable mechanical keyboard switch design, featuring a tactile Blue switch with a distinct click feel, and also integrates a programmable RGB LED. +It is suitable for applications such as human‑machine interaction and smart home control. -### SKU:U001 & U001-B & U001-C - -Contains M5Stack-**UNIT ENV** series related case programs.ENV is an environmental sensor with integrated SHT30 and QMP6988 internally to detect temperature, humidity, and atmospheric pressure data. +### SKU: U206 ## Related Link - -- [Document & Datasheet](https://docs.m5stack.com/en/unit/envIII) - -## Required Libraries: - -- [Adafruit_BMP280_Library](https://github.com/adafruit/Adafruit_BMP280_Library) +https://docs.m5stack.com/en/products/sku/U206 ## License - -- [M5Unit-ENV - MIT](LICENSE) -``` \ No newline at end of file +- [MIT](https://github.com/m5stack/M5Chain/blob/main/LICENSE) \ No newline at end of file diff --git a/examples/Angle_Example/Angle_Example.ino b/examples/Angle_Example/Angle_Example.ino new file mode 100644 index 0000000..de01775 --- /dev/null +++ b/examples/Angle_Example/Angle_Example.ino @@ -0,0 +1,165 @@ +/* + *SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + *SPDX-License-Identifier: MIT + */ + +#include "M5Chain.h" + +#define TXD_PIN GPIO_NUM_21 // Tx +#define RXD_PIN GPIO_NUM_22 // Rx + +Chain M5Chain; + +device_list_t *devices_list = NULL; +uint16_t device_nums = 0; +uint8_t operation_status = 0; +chain_status_t chain_status = CHAIN_OK; + +uint8_t rgb_test[5][3] = { + {0xFF, 0x00, 0x00}, {0x00, 0xFF, 0x00}, {0x00, 0x00, 0xFF}, {0xFF, 0xFF, 0xFF}, {0x00, 0x00, 0x00}, +}; + +void printDeviceList(device_list_t *devices) +{ + if (devices == NULL) { + Serial.println("devices is NULL"); + return; + } + + Serial.print("devices count: "); + Serial.println(devices->count); + + for (uint8_t i = 0; i < devices->count; i++) { + Serial.print("devices ID: "); + Serial.println(devices->devices[i].id); + Serial.print("devices type: "); + Serial.println(devices->devices[i].device_type); + } +} + +void setup() +{ + Serial.begin(115200); + Serial.println("M5Chain Angle Test"); + M5Chain.begin(&Serial2, 115200, RXD_PIN, TXD_PIN); + + if (M5Chain.isDeviceConnected()) { + Serial.println("devices is connected"); + chain_status = M5Chain.getDeviceNum(&device_nums); + if (chain_status == CHAIN_OK) { + devices_list = (device_list_t *)malloc(sizeof(device_list_t)); + devices_list->count = device_nums; + devices_list->devices = (device_info_t *)malloc(sizeof(device_info_t) * device_nums); + if (M5Chain.getDeviceList(devices_list)) { + Serial.println("get devices list success"); + printDeviceList(devices_list); + } else { + Serial.println("get devices list failed"); + } + } else { + Serial.printf("error status:%d \r\n", chain_status); + Serial.printf("devices num get failed.\r\n"); + } + } else { + Serial.println("devices is not connected."); + } + + if (devices_list) { + for (uint8_t i = 0; i < devices_list->count; i++) { + if (devices_list->devices[i].device_type == CHAIN_ANGLE_TYPE_CODE) { + chain_status = M5Chain.setRGBLight(devices_list->devices[i].id, 100, &operation_status); + if (chain_status == CHAIN_OK && operation_status) { + Serial.printf("ID[%d] set rgb light success\r\n", devices_list->devices[i].id); + } else { + Serial.printf("ID[%d] set rgb light failed, chain_status:%d operation_status:%d \r\n", + devices_list->devices[i].id, chain_status, operation_status); + } + for (uint8_t j = 0; j < 5; j++) { + uint8_t rgb[3] = {0}; + chain_status = + M5Chain.setRGBValue(devices_list->devices[i].id, 0, 1, rgb_test[j], 3, &operation_status); + if (chain_status == CHAIN_OK && operation_status) { + Serial.printf("ID[%d] set rgb %d %d %d success\r\n", devices_list->devices[i].id, + rgb_test[j][0], rgb_test[j][1], rgb_test[j][2]); + } else { + Serial.printf("ID[%d] set rgb %d %d %d failed, chain_status:%d operation_status:%d \r\n", + devices_list->devices[i].id, rgb_test[j][0], rgb_test[j][1], rgb_test[j][2], + chain_status, operation_status); + } + chain_status = M5Chain.getRGBValue(devices_list->devices[i].id, 0, 1, rgb, 3, &operation_status); + if (chain_status == CHAIN_OK && operation_status) { + Serial.printf("ID[%d] get rgb %d %d %d success \r\n", devices_list->devices[i].id, rgb[0], + rgb[1], rgb[2]); + } else { + Serial.printf("ID[%d] get rgb %d %d %d failed, chain_status:%d operation_status:%d \r\n", + devices_list->devices[i].id, rgb[0], rgb[1], rgb[2], chain_status, + operation_status); + } + delay(500); + } + + // M5Chain.setAngleRotationDirection(devices_list->devices[i].id, ANGLE_ROTATION_DECREASING, + // &operation_status, + // CHAIN_SAVE_FLASH_ENABLE); // Angle rotation direction decreasing, save to flash + // M5Chain.setAngleRotationDirection(devices_list->devices[i].id, ANGLE_ROTATION_INCREASING, + // &operation_status, + // CHAIN_SAVE_FLASH_ENABLE); // Angle rotation direction increasing, save to flash + // chain_status = M5Chain.setAngleRotationDirection(devices_list->devices[i].id, + // ANGLE_ROTATION_DECREASING, + // &operation_status, + // CHAIN_SAVE_FLASH_DISABLE); // Angle rotation direction decreasing, not save to flash + M5Chain.setAngleRotationDirection(devices_list->devices[i].id, ANGLE_ROTATION_INCREASING, + &operation_status, + CHAIN_SAVE_FLASH_DISABLE); // Angle rotation direction increasing, not save to flash + + if (chain_status == CHAIN_OK && operation_status) { + Serial.printf("Angle ID[%d] set angle rotation direction success \r\n", devices_list->devices[i].id); + } else { + Serial.printf( + "Angle ID[%d] set angle rotation direction failed, chain_status:%d operation_status:%d \r\n", + devices_list->devices[i].id, chain_status, operation_status); + } + } + } + } else { + Serial.println("devices list is NULL"); + } +} + +void loop() +{ + if (devices_list) { + for (uint8_t i = 0; i < devices_list->count; i++) { + if (devices_list->devices[i].device_type == CHAIN_ANGLE_TYPE_CODE) { + uint16_t angle12Bit = 0; + uint8_t angle8Bit = 0; + angle_rotation_direction_t angle_rotation_direction; + chain_status = M5Chain.getAngle12BitAdc(devices_list->devices[i].id, &angle12Bit); + if (chain_status == CHAIN_OK) { + Serial.printf("Angle ID[%d] angle12Bit:%d \r\n", devices_list->devices[i].id, angle12Bit); + } else { + Serial.printf("Angle ID[%d] get 12bit adc failed, chain_status:%d \r\n", devices_list->devices[i].id, + chain_status); + } + chain_status = M5Chain.getAngle8BitAdc(devices_list->devices[i].id, &angle8Bit); + if (chain_status == CHAIN_OK) { + Serial.printf("Angle ID[%d] angle8Bit:%d \r\n", devices_list->devices[i].id, angle8Bit); + } else { + Serial.printf("Angle ID[%d] get 8bit adc failed, chain_status:%d \r\n", devices_list->devices[i].id, + chain_status); + } + chain_status = + M5Chain.getAngleRotationDirection(devices_list->devices[i].id, &angle_rotation_direction); + if (chain_status == CHAIN_OK) { + Serial.printf("Angle ID[%d] angle_rotation_direction:%d \r\n", devices_list->devices[i].id, + angle_rotation_direction); + } else { + Serial.printf("Angle ID[%d] get rotation direction failed, chain_status:%d \r\n", + devices_list->devices[i].id, chain_status); + } + } + } + } + delay(100); +} \ No newline at end of file diff --git a/examples/Encoder_Example/Encoder_Example.ino b/examples/Encoder_Example/Encoder_Example.ino new file mode 100644 index 0000000..08d21ec --- /dev/null +++ b/examples/Encoder_Example/Encoder_Example.ino @@ -0,0 +1,201 @@ +/* + *SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + *SPDX-License-Identifier: MIT + */ + +#include "M5Chain.h" + +#define TXD_PIN GPIO_NUM_21 // Tx +#define RXD_PIN GPIO_NUM_22 // Rx + +Chain M5Chain; + +device_list_t *devices_list = NULL; +uint16_t device_nums = 0; +uint8_t operation_status = 0; +chain_status_t chain_status = CHAIN_OK; + +uint8_t rgb_test[5][3] = { + {0xFF, 0x00, 0x00}, {0x00, 0xFF, 0x00}, {0x00, 0x00, 0xFF}, {0xFF, 0xFF, 0xFF}, {0x00, 0x00, 0x00}, +}; + +void printDeviceList(device_list_t *devices) +{ + if (devices == NULL) { + Serial.println("devices is NULL"); + return; + } + + Serial.print("devices count: "); + Serial.println(devices->count); + + for (uint8_t i = 0; i < devices->count; i++) { + Serial.print("devices ID: "); + Serial.println(devices->devices[i].id); + Serial.print("devices type: "); + Serial.println(devices->devices[i].device_type); + } +} + +void setup() +{ + Serial.begin(115200); + Serial.println("M5Chain Encoder Test"); + M5Chain.begin(&Serial2, 115200, RXD_PIN, TXD_PIN); + + if (M5Chain.isDeviceConnected()) { + Serial.println("devices is connected"); + chain_status = M5Chain.getDeviceNum(&device_nums); + if (chain_status == CHAIN_OK) { + devices_list = (device_list_t *)malloc(sizeof(device_list_t)); + devices_list->count = device_nums; + devices_list->devices = (device_info_t *)malloc(sizeof(device_info_t) * device_nums); + if (M5Chain.getDeviceList(devices_list)) { + Serial.println("get devices list success"); + printDeviceList(devices_list); + } else { + Serial.println("get devices list failed"); + } + } else { + Serial.printf("error status:%d \r\n", chain_status); + Serial.printf("devices num get failed.\r\n"); + } + } else { + Serial.println("devices is not connected."); + } + + if (devices_list) { + for (uint8_t i = 0; i < devices_list->count; i++) { + if (devices_list->devices[i].device_type == CHAIN_ENCODER_TYPE_CODE) { + chain_status = M5Chain.setRGBLight(devices_list->devices[i].id, 100, &operation_status); + if (chain_status == CHAIN_OK && operation_status) { + Serial.printf("ID[%d] set rgb light success\r\n", devices_list->devices[i].id); + } else { + Serial.printf("ID[%d] set rgb light failed, chain_status:%d operation_status:%d \r\n", + devices_list->devices[i].id, chain_status, operation_status); + } + for (uint8_t j = 0; j < 5; j++) { + uint8_t rgb[3] = {0}; + chain_status = + M5Chain.setRGBValue(devices_list->devices[i].id, 0, 1, rgb_test[j], 3, &operation_status); + if (chain_status == CHAIN_OK && operation_status) { + Serial.printf("ID[%d] set rgb %d %d %d success\r\n", devices_list->devices[i].id, + rgb_test[j][0], rgb_test[j][1], rgb_test[j][2]); + } else { + Serial.printf("ID[%d] set rgb %d %d %d failed, chain_status:%d operation_status:%d \r\n", + devices_list->devices[i].id, rgb_test[j][0], rgb_test[j][1], rgb_test[j][2], + chain_status, operation_status); + } + chain_status = M5Chain.getRGBValue(devices_list->devices[i].id, 0, 1, rgb, 3, &operation_status); + if (chain_status == CHAIN_OK && operation_status) { + Serial.printf("ID[%d] get rgb %d %d %d success \r\n", devices_list->devices[i].id, rgb[0], + rgb[1], rgb[2]); + } else { + Serial.printf("ID[%d] get rgb %d %d %d failed, chain_status:%d operation_status:%d \r\n", + devices_list->devices[i].id, rgb[0], rgb[1], rgb[2], chain_status, + operation_status); + } + delay(500); + } + chain_status = + M5Chain.setKeyButtonTriggerInterval(devices_list->devices[i].id, BUTTON_DOUBLE_CLICK_TIME_200MS, + BUTTON_LONG_PRESS_TIME_3S, &operation_status); + if (chain_status == CHAIN_OK && operation_status) { + Serial.printf("ID[%d] set key button trigger interval success\r\n", devices_list->devices[i].id); + } else { + Serial.printf( + "ID[%d] set key button trigger interval failed, chain_status:%d operation_status:%d \r\n", + devices_list->devices[i].id, chain_status, operation_status); + } + chain_status = + M5Chain.setKeyButtonMode(devices_list->devices[i].id, CHAIN_BUTTON_REPORT_MODE, &operation_status); + if (chain_status == CHAIN_OK && operation_status) { + Serial.printf("ID[%d] set key button mode success\r\n", devices_list->devices[i].id); + } else { + Serial.printf("ID[%d] set key button mode failed, chain_status:%d operation_status:%d \r\n", + devices_list->devices[i].id, chain_status, operation_status); + } + } + } + } else { + Serial.println("devices list is NULL"); + } +} + +void loop() +{ + if (devices_list) { + for (uint8_t i = 0; i < devices_list->count; i++) { + if (devices_list->devices[i].device_type == CHAIN_ENCODER_TYPE_CODE) { + int16_t encoder = 0; + int16_t inc_encoder = 0; + encoder_ab_t direct; + uint8_t button_status = 0; + button_double_click_time_t button_double_click_time; + button_long_press_time_t button_long_press_time; + chain_button_press_type_t button_press_type; + chain_button_mode_t button_mode; + + chain_status = M5Chain.getEncoderValue(devices_list->devices[i].id, &encoder); + if (chain_status == CHAIN_OK) { + Serial.printf("ENCODER ID[%d] encoder:%d \r\n", devices_list->devices[i].id, encoder); + } else { + Serial.printf("ENCODER ID[%d] get encoder status failed, chain_status:%d \r\n", + devices_list->devices[i].id, chain_status); + } + chain_status = M5Chain.getEncoderIncValue(devices_list->devices[i].id, &inc_encoder); + if (chain_status == CHAIN_OK) { + Serial.printf("ENCODER ID[%d] inc encoder:%d \r\n", devices_list->devices[i].id, inc_encoder); + } else { + Serial.printf("ENCODER ID[%d] get inc encoder status failed, chain_status:%d \r\n", + devices_list->devices[i].id, chain_status); + } + chain_status = M5Chain.getEncoderABDirect(devices_list->devices[i].id, &direct); + if (chain_status == CHAIN_OK) { + Serial.printf("ENCODER ID[%d] ab direct:%d \r\n", devices_list->devices[i].id, direct); + } else { + Serial.printf("ENCODER ID[%d] get direct status failed, chain_status:%d \r\n", + devices_list->devices[i].id, chain_status); + } + chain_status = M5Chain.getEncoderButtonStatus(devices_list->devices[i].id, &button_status); + if (chain_status == CHAIN_OK) { + Serial.printf("ENCODER ID[%d] button status:%d \r\n", devices_list->devices[i].id, button_status); + } else { + Serial.printf("ENCODER ID[%d] get button status failed, chain_status:%d \r\n", + devices_list->devices[i].id, chain_status); + } + chain_status = M5Chain.getEncoderButtonTriggerInterval( + devices_list->devices[i].id, &button_double_click_time, &button_long_press_time); + if (chain_status == CHAIN_OK) { + Serial.printf("ENCODER ID[%d] button double click time:%d, button long press time:%d \r\n", + devices_list->devices[i].id, button_double_click_time, button_long_press_time); + } else { + Serial.printf("ENCODER ID[%d] get button trigger interval failed, chain_status:%d \r\n", + devices_list->devices[i].id, chain_status); + } + chain_status = M5Chain.getEncoderButtonMode(devices_list->devices[i].id, &button_mode); + if (chain_status == CHAIN_OK) { + Serial.printf("ENCODER ID[%d] button mode:%d \r\n", devices_list->devices[i].id, button_mode); + } else { + Serial.printf("ENCODER ID[%d] get button mode failed, chain_status:%d \r\n", + devices_list->devices[i].id, chain_status); + } + while (M5Chain.getEncoderButtonPressStatus(devices_list->devices[i].id, &button_press_type)) { + switch (button_press_type) { + case CHAIN_BUTTON_PRESS_SINGLE: + Serial.printf("ENCODER ID[%d] button press type: single \r\n", devices_list->devices[i].id); + break; + case CHAIN_BUTTON_PRESS_DOUBLE: + Serial.printf("ENCODER ID[%d] button press type: double \r\n", devices_list->devices[i].id); + break; + case CHAIN_BUTTON_PRESS_LONG: + Serial.printf("ENCODER ID[%d] button press type: long \r\n", devices_list->devices[i].id); + break; + } + } + } + } + } + delay(100); +} \ No newline at end of file diff --git a/examples/ToF_Example/Continuous/Continuous.ino b/examples/ToF_Example/Continuous/Continuous.ino new file mode 100644 index 0000000..9b6d7ad --- /dev/null +++ b/examples/ToF_Example/Continuous/Continuous.ino @@ -0,0 +1,174 @@ +/* + *SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + *SPDX-License-Identifier: MIT + */ + +#include "M5Chain.h" + +#define TXD_PIN GPIO_NUM_21 // Tx +#define RXD_PIN GPIO_NUM_22 // Rx + +Chain M5Chain; + +device_list_t *devices_list; + +uint16_t device_nums = 0; +uint8_t operation_status = 0; +chain_status_t chain_status = CHAIN_OK; + +uint8_t rgb_test[5][3] = { + {0xFF, 0x00, 0x00}, {0x00, 0xFF, 0x00}, {0x00, 0x00, 0xFF}, {0xFF, 0xFF, 0xFF}, {0x00, 0x00, 0x00}, +}; + +void printDeviceList(device_list_t *devices) +{ + if (devices == NULL) { + Serial.println("devices is NULL"); + return; + } + + Serial.print("devices count: "); + Serial.println(devices->count); + + for (uint8_t i = 0; i < devices->count; i++) { + Serial.print("devices ID: "); + Serial.println(devices->devices[i].id); + Serial.print("devices type: "); + Serial.println(devices->devices[i].device_type); + } +} + +void setup() +{ + Serial.begin(115200); + Serial.println("M5Chain ToF Test"); + M5Chain.begin(&Serial2, 115200, RXD_PIN, TXD_PIN); + + if (M5Chain.isDeviceConnected()) { + Serial.println("devices is connected"); + chain_status = M5Chain.getDeviceNum(&device_nums); + if (chain_status == CHAIN_OK) { + devices_list = (device_list_t *)malloc(sizeof(device_list_t)); + devices_list->count = device_nums; + devices_list->devices = (device_info_t *)malloc(sizeof(device_info_t) * device_nums); + if (M5Chain.getDeviceList(devices_list)) { + Serial.println("devices list get success"); + printDeviceList(devices_list); + } else { + Serial.println("devices list get failed"); + } + } else { + Serial.printf("error status:%d \r\n", chain_status); + Serial.printf("devices num get failed.\r\n"); + } + } else { + Serial.println("devices is not connected."); + } + + if (devices_list) { + for (uint8_t i = 0; i < devices_list->count; i++) { + if (devices_list->devices[i].device_type == CHAIN_TOF_TYPE_CODE) { + chain_status = M5Chain.setRGBLight(devices_list->devices[i].id, 100, &operation_status); + if (chain_status == CHAIN_OK && operation_status) { + Serial.printf("ID[%d] set rgb light success\r\n", devices_list->devices[i].id); + } else { + Serial.printf("ID[%d] set rgb light failed, chain_status:%d operation_status:%d \r\n", + devices_list->devices[i].id, chain_status, operation_status); + } + for (uint8_t j = 0; j < 5; j++) { + uint8_t rgb[3] = {0}; + chain_status = + M5Chain.setRGBValue(devices_list->devices[i].id, 0, 1, rgb_test[j], 3, &operation_status); + if (chain_status == CHAIN_OK && operation_status) { + Serial.printf("ID[%d] set rgb %d %d %d success\r\n", devices_list->devices[i].id, + rgb_test[j][0], rgb_test[j][1], rgb_test[j][2]); + } else { + Serial.printf("ID[%d] set rgb %d %d %d failed, chain_status:%d operation_status:%d \r\n", + devices_list->devices[i].id, rgb_test[j][0], rgb_test[j][1], rgb_test[j][2], + chain_status, operation_status); + } + chain_status = M5Chain.getRGBValue(devices_list->devices[i].id, 0, 1, rgb, 3, &operation_status); + if (chain_status == CHAIN_OK && operation_status) { + Serial.printf("ID[%d] get rgb %d %d %d success \r\n", devices_list->devices[i].id, rgb[0], + rgb[1], rgb[2]); + } else { + Serial.printf("ID[%d] get rgb %d %d %d failed, chain_status:%d operation_status:%d \r\n", + devices_list->devices[i].id, rgb[0], rgb[1], rgb[2], chain_status, + operation_status); + } + delay(500); + } + + // 设置测量模式 + chain_status = M5Chain.setToFMeasureMode(devices_list->devices[i].id, CHAIN_TOF_MODE_CONTINUOUS, + &operation_status); + if (chain_status == CHAIN_OK && operation_status) { + Serial.printf("TOF ID[%d] set measure mode CONTINUOUS success\r\n", devices_list->devices[i].id); + } else { + Serial.printf( + "TOF ID[%d] set measure mode CONTINUOUS failed, chain_status:%d operation_status:%d \r\n", + devices_list->devices[i].id, chain_status, operation_status); + } + + // 设置测量时间 + uint8_t measurement_time = 33; // ms + chain_status = + M5Chain.setToFMeasureTime(devices_list->devices[i].id, measurement_time, &operation_status); + if (chain_status == CHAIN_OK && operation_status) { + Serial.printf("TOF ID[%d] set measurement_time %d success\r\n", devices_list->devices[i].id, + measurement_time); + } else { + Serial.printf( + "TOF ID[%d] set measurement_time %d failed, chain_status:%d operation_status:%d \r\n", + devices_list->devices[i].id, measurement_time, chain_status, operation_status); + } + } + } + } else { + Serial.println("devices list is NULL"); + } +} + +void loop() +{ + if (devices_list) { + for (uint8_t i = 0; i < devices_list->count; i++) { + if (devices_list->devices[i].device_type == CHAIN_TOF_TYPE_CODE) { + uint16_t distance = 0; + uint8_t measurement_time = 0; + chain_tof_mode_t mode; + chain_tof_measure_status_t measure_status; + chain_status = M5Chain.getToFDistance(devices_list->devices[i].id, &distance); + if (chain_status == CHAIN_OK) { + Serial.printf("TOF ID[%d] distance:%d \r\n", devices_list->devices[i].id, distance); + } else { + Serial.printf("TOF ID[%d] get distance failed, chain_status:%d \r\n", devices_list->devices[i].id, + chain_status); + } + chain_status = M5Chain.getToFMeasureTime(devices_list->devices[i].id, &measurement_time); + if (chain_status == CHAIN_OK) { + Serial.printf("TOF ID[%d] measurement_time:%d \r\n", devices_list->devices[i].id, measurement_time); + } else { + Serial.printf("TOF ID[%d] get measurement_time failed, chain_status:%d \r\n", + devices_list->devices[i].id, chain_status); + } + chain_status = M5Chain.getToFMeasureMode(devices_list->devices[i].id, &mode); + if (chain_status == CHAIN_OK) { + Serial.printf("TOF ID[%d] mode:%d \r\n", devices_list->devices[i].id, mode); + } else { + Serial.printf("TOF ID[%d] get mode failed, chain_status:%d \r\n", devices_list->devices[i].id, + chain_status); + } + chain_status = M5Chain.getToFMeasureStatus(devices_list->devices[i].id, &measure_status); + if (chain_status == CHAIN_OK) { + Serial.printf("TOF ID[%d] measure_status:%d \r\n", devices_list->devices[i].id, measure_status); + } else { + Serial.printf("TOF ID[%d] get measure_status failed, chain_status:%d \r\n", + devices_list->devices[i].id, chain_status); + } + } + } + } + delay(100); +} \ No newline at end of file diff --git a/examples/ToF_Example/Single/Single.ino b/examples/ToF_Example/Single/Single.ino new file mode 100644 index 0000000..bfa304f --- /dev/null +++ b/examples/ToF_Example/Single/Single.ino @@ -0,0 +1,215 @@ +/* + *SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + *SPDX-License-Identifier: MIT + */ + +#include "M5Chain.h" + +#define TXD_PIN GPIO_NUM_21 // Tx +#define RXD_PIN GPIO_NUM_22 // Rx + +Chain M5Chain; + +device_list_t *devices_list; + +uint16_t device_nums = 0; +uint8_t operation_status = 0; +chain_status_t chain_status = CHAIN_OK; + +uint8_t rgb_test[5][3] = { + {0xFF, 0x00, 0x00}, {0x00, 0xFF, 0x00}, {0x00, 0x00, 0xFF}, {0xFF, 0xFF, 0xFF}, {0x00, 0x00, 0x00}, +}; + +void printDeviceList(device_list_t *devices) +{ + if (devices == NULL) { + Serial.println("devices is NULL"); + return; + } + + Serial.print("devices count: "); + Serial.println(devices->count); + + for (uint8_t i = 0; i < devices->count; i++) { + Serial.print("devices ID: "); + Serial.println(devices->devices[i].id); + Serial.print("devices type: "); + Serial.println(devices->devices[i].device_type); + } +} + +void setup() +{ + Serial.begin(115200); + Serial.println("M5Chain ToF Test"); + M5Chain.begin(&Serial2, 115200, RXD_PIN, TXD_PIN); + + if (M5Chain.isDeviceConnected()) { + Serial.println("devices is connected"); + chain_status = M5Chain.getDeviceNum(&device_nums); + if (chain_status == CHAIN_OK) { + devices_list = (device_list_t *)malloc(sizeof(device_list_t)); + devices_list->count = device_nums; + devices_list->devices = (device_info_t *)malloc(sizeof(device_info_t) * device_nums); + if (M5Chain.getDeviceList(devices_list)) { + Serial.println("devices list get success"); + printDeviceList(devices_list); + } else { + Serial.println("devices list get failed"); + } + } else { + Serial.printf("error status:%d \r\n", chain_status); + Serial.printf("devices num get failed.\r\n"); + } + } else { + Serial.println("devices is not connected."); + } + + if (devices_list) { + for (uint8_t i = 0; i < devices_list->count; i++) { + if (devices_list->devices[i].device_type == CHAIN_TOF_TYPE_CODE) { + chain_status = M5Chain.setRGBLight(devices_list->devices[i].id, 100, &operation_status); + if (chain_status == CHAIN_OK && operation_status) { + Serial.printf("ID[%d] set rgb light success\r\n", devices_list->devices[i].id); + } else { + Serial.printf("ID[%d] set rgb light failed, chain_status:%d operation_status:%d \r\n", + devices_list->devices[i].id, chain_status, operation_status); + } + for (uint8_t j = 0; j < 5; j++) { + uint8_t rgb[3] = {0}; + chain_status = + M5Chain.setRGBValue(devices_list->devices[i].id, 0, 1, rgb_test[j], 3, &operation_status); + if (chain_status == CHAIN_OK && operation_status) { + Serial.printf("ID[%d] set rgb %d %d %d success\r\n", devices_list->devices[i].id, + rgb_test[j][0], rgb_test[j][1], rgb_test[j][2]); + } else { + Serial.printf("ID[%d] set rgb %d %d %d failed, chain_status:%d operation_status:%d \r\n", + devices_list->devices[i].id, rgb_test[j][0], rgb_test[j][1], rgb_test[j][2], + chain_status, operation_status); + } + chain_status = M5Chain.getRGBValue(devices_list->devices[i].id, 0, 1, rgb, 3, &operation_status); + if (chain_status == CHAIN_OK && operation_status) { + Serial.printf("ID[%d] get rgb %d %d %d success \r\n", devices_list->devices[i].id, rgb[0], + rgb[1], rgb[2]); + } else { + Serial.printf("ID[%d] get rgb %d %d %d failed, chain_status:%d operation_status:%d \r\n", + devices_list->devices[i].id, rgb[0], rgb[1], rgb[2], chain_status, + operation_status); + } + delay(500); + } + + // 设置测量模式 + chain_status = + M5Chain.setToFMeasureMode(devices_list->devices[i].id, CHAIN_TOF_MODE_SINGLE, &operation_status); + if (chain_status == CHAIN_OK && operation_status) { + Serial.printf("TOF ID[%d] set measure mode SINGLE success\r\n", devices_list->devices[i].id); + } else { + Serial.printf( + "TOF ID[%d] set measure mode SINGLE failed, chain_status:%d operation_status:%d \r\n", + devices_list->devices[i].id, chain_status, operation_status); + } + + // 设置测量时间 + uint8_t measurement_time = 33; // ms + chain_status = + M5Chain.setToFMeasureTime(devices_list->devices[i].id, measurement_time, &operation_status); + if (chain_status == CHAIN_OK && operation_status) { + Serial.printf("TOF ID[%d] set measurement_time %d success\r\n", devices_list->devices[i].id, + measurement_time); + } else { + Serial.printf( + "TOF ID[%d] set measurement_time %d failed, chain_status:%d operation_status:%d \r\n", + devices_list->devices[i].id, measurement_time, chain_status, operation_status); + } + + // 设置测量状态,单次测量必须手运触发 + chain_status = + M5Chain.setToFMeasureStatus(devices_list->devices[i].id, CHAIN_TOF_STATUS_START, &operation_status); + if (chain_status == CHAIN_OK && operation_status) { + Serial.printf("TOF ID[%d] set measure status START success\r\n", devices_list->devices[i].id); + } else { + Serial.printf( + "TOF ID[%d] set measure status START failed, chain_status:%d operation_status:%d \r\n", + devices_list->devices[i].id, chain_status, operation_status); + } + } + } + } else { + Serial.println("devices list is NULL"); + } +} + +void loop() +{ + if (devices_list) { + for (uint8_t i = 0; i < devices_list->count; i++) { + if (devices_list->devices[i].device_type == CHAIN_TOF_TYPE_CODE) { + uint16_t distance = 0; + uint8_t measurement_time = 0; + chain_tof_mode_t mode; + chain_tof_measure_status_t measure_status; + uint8_t complete_flag = 0; + + // 读取测量时间 + chain_status = M5Chain.getToFMeasureTime(devices_list->devices[i].id, &measurement_time); + if (chain_status == CHAIN_OK) { + Serial.printf("TOF ID[%d] measurement_time:%d \r\n", devices_list->devices[i].id, measurement_time); + } else { + Serial.printf("TOF ID[%d] get measurement_time failed, chain_status:%d \r\n", + devices_list->devices[i].id, chain_status); + } + + // 读取测量模式 + chain_status = M5Chain.getToFMeasureMode(devices_list->devices[i].id, &mode); + if (chain_status == CHAIN_OK) { + Serial.printf("TOF ID[%d] mode:%d \r\n", devices_list->devices[i].id, mode); + } else { + Serial.printf("TOF ID[%d] get mode failed, chain_status:%d \r\n", devices_list->devices[i].id, + chain_status); + } + + // 读取测量状态 + chain_status = M5Chain.getToFMeasureStatus(devices_list->devices[i].id, &measure_status); + if (chain_status == CHAIN_OK) { + Serial.printf("TOF ID[%d] measure_status:%d \r\n", devices_list->devices[i].id, measure_status); + } else { + Serial.printf("TOF ID[%d] get measure_status failed, chain_status:%d \r\n", + devices_list->devices[i].id, chain_status); + } + + // 读取测量完成标志 + chain_status = M5Chain.getToFMeasureCompleteFlag(devices_list->devices[i].id, &complete_flag); + if (chain_status == CHAIN_OK) { + Serial.printf("TOF ID[%d] complete_flag:%d \r\n", devices_list->devices[i].id, complete_flag); + } else { + Serial.printf("TOF ID[%d] get complete_flag failed, chain_status:%d \r\n", + devices_list->devices[i].id, chain_status); + } + + // 如果是单次测量模式,且测量完成,则读取距离值,并重新触发测量 + if (measure_status == 0 && complete_flag == 1) { + chain_status = M5Chain.getToFDistance(devices_list->devices[i].id, &distance); + if (chain_status == CHAIN_OK) { + Serial.printf("TOF ID[%d] distance:%d \r\n", devices_list->devices[i].id, distance); + } else { + Serial.printf("TOF ID[%d] get distance failed, chain_status:%d \r\n", + devices_list->devices[i].id, chain_status); + } + // 重新触发单次测量 + chain_status = M5Chain.setToFMeasureStatus(devices_list->devices[i].id, CHAIN_TOF_STATUS_START, + &operation_status); + if (chain_status == CHAIN_OK && operation_status) { + Serial.printf("TOF ID[%d] set measure status START success\r\n", devices_list->devices[i].id); + } else { + Serial.printf( + "TOF ID[%d] set measure status START failed, chain_status:%d operation_status:%d \r\n", + devices_list->devices[i].id, chain_status, operation_status); + } + } + } + } + delay(100); + } +} \ No newline at end of file diff --git a/library.json b/library.json index b2f082f..475fdef 100644 --- a/library.json +++ b/library.json @@ -26,7 +26,10 @@ "-Isrc/Chain", "-Isrc/ChainCommon", "-Isrc/ChainJoystick", - "-Isrc/ChainKey" + "-Isrc/ChainKey", + "-Isrc/ChainEncoder", + "-Isrc/ChainAngle", + "-Isrc/ChainToF" ] }, "headers": [ diff --git a/library.properties b/library.properties index 94ab421..55987e7 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,10 @@ name=M5Chain -version=0.0.1 +version=1.0.0 author=M5Stack maintainer=M5Stack sentence=M5Stack Chain series support library. paragraph=Library for M5Stack Chain series units. -category=Uncategorized -url=https://github.com/m5stack -architectures=* \ No newline at end of file +category=Device Control +url=https://github.com/m5stack/M5Chain +architectures=* +includes=M5Chain.hpp \ No newline at end of file diff --git a/src/Chain/Chain.hpp b/src/Chain/Chain.hpp index 22524b8..57fdfff 100644 --- a/src/Chain/Chain.hpp +++ b/src/Chain/Chain.hpp @@ -10,10 +10,16 @@ #include "ChainCommon/ChainCommon.hpp" #include "ChainJoystick/ChainJoystick.hpp" #include "ChainKey/ChainKey.hpp" +#include "ChainAngle/ChainAngle.hpp" +#include "ChainEncoder/ChainEncoder.hpp" +#include "ChainToF/ChainToF.hpp" class Chain : virtual public ChainCommon, virtual public ChainJoystick, - virtual public ChainKey { + virtual public ChainKey, + virtual public ChainAngle, + virtual public ChainEncoder, + virtual public ChainToF { public: private: }; diff --git a/src/ChainAngle/ChainAngle.cpp b/src/ChainAngle/ChainAngle.cpp new file mode 100644 index 0000000..a42ed38 --- /dev/null +++ b/src/ChainAngle/ChainAngle.cpp @@ -0,0 +1,112 @@ +/* + *SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + *SPDX-License-Identifier: MIT + */ + +#include "ChainAngle/ChainAngle.hpp" + +chain_status_t ChainAngle::getAngle12BitAdc(uint8_t id, uint16_t *adcValue, unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (acquireMutex()) { + cmdBufferSize = 0; + sendPacket(id, CHAIN_ANGLE_GET_12ADC, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_ANGLE_GET_12ADC, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *adcValue = (returnPacket[7] << 8) | returnPacket[6]; + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} + +chain_status_t ChainAngle::getAngle8BitAdc(uint8_t id, uint8_t *adcValue, unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (acquireMutex()) { + cmdBufferSize = 0; + sendPacket(id, CHAIN_ANGLE_GET_8ADC, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_ANGLE_GET_8ADC, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *adcValue = returnPacket[6]; + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} + +chain_status_t ChainAngle::setAngleRotationDirection(uint8_t id, angle_rotation_direction_t direction, + uint8_t *operationStatus, chain_save_flash_t saveToFlash, + unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (direction > ANGLE_ROTATION_INCREASING) { + return CHAIN_PARAMETER_ERROR; + } + + if (acquireMutex()) { + cmdBufferSize = 0; + cmdBuffer[cmdBufferSize++] = direction; + cmdBuffer[cmdBufferSize++] = saveToFlash; + sendPacket(id, CHAIN_ANGLE_SET_CLOCKWISE_STATUS, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_ANGLE_SET_CLOCKWISE_STATUS, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *operationStatus = returnPacket[6]; + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} + +chain_status_t ChainAngle::getAngleRotationDirection(uint8_t id, angle_rotation_direction_t *direction, + unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (acquireMutex()) { + cmdBufferSize = 0; + sendPacket(id, CHAIN_ANGLE_GET_CLOCKWISE_STATUS, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_ANGLE_GET_CLOCKWISE_STATUS, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *direction = (angle_rotation_direction_t)returnPacket[6]; + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} \ No newline at end of file diff --git a/src/ChainAngle/ChainAngle.hpp b/src/ChainAngle/ChainAngle.hpp new file mode 100644 index 0000000..99e01f1 --- /dev/null +++ b/src/ChainAngle/ChainAngle.hpp @@ -0,0 +1,103 @@ +/* + *SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + *SPDX-License-Identifier: MIT + */ + +#ifndef _CHAIN_ANGLE_HPP_ +#define _CHAIN_ANGLE_HPP_ + +#include "ChainCommon/ChainCommon.hpp" + +/** + * @brief RotationDirection status for Angle device. + * + * This enumeration records whether the Angle device's clockwise direction + * is increasing or decreasing. + */ +typedef enum { + ANGLE_ROTATION_DECREASING = 0x00, /**< Rotation direction decreasing angle. */ + ANGLE_ROTATION_INCREASING = 0x01, /**< Rotation direction increasing angle. */ +} angle_rotation_direction_t; + +/** + * @brief Enumeration for Chain_Angle device commands. + * + * This enumeration defines command codes for various operations of the Chain_Angle device. + */ +typedef enum { + CHAIN_ANGLE_GET_12ADC = 0x30, /**< Command to get the latest 12-bit ADC value */ + CHAIN_ANGLE_GET_8ADC = 0x31, /**< Command to get the latest 8-bit mapped ADC value */ + CHAIN_ANGLE_SET_CLOCKWISE_STATUS = 0x32, /**< Command to set the clockwise direction status */ + CHAIN_ANGLE_GET_CLOCKWISE_STATUS = 0x33, /**< Command to get the current clockwise direction status */ +} CHAIN_ANGLE_CMD_T; /**< Command types for Chain_Angle device operations */ + +class ChainAngle : virtual public ChainCommon { +public: + /** + * @brief Retrieves the 12-bit ADC value of the Angle device. + * + * This function fetches the 12-bit ADC value from the specified Angle device. + * + * @param id The position of the Angle device in the chain (starting from 1). + * @param adcValue Pointer to store the retrieved 12-bit ADC value. + * @param timeout Timeout duration in milliseconds (default is 100ms). + * + * @return The operation status (e.g., CHAIN_OK, CHAIN_TIMEOUT, etc.). + */ + chain_status_t getAngle12BitAdc(uint8_t id, uint16_t *adcValue, unsigned long timeout = 100); + + /** + * @brief Retrieves the 8-bit ADC value of the Angle device. + * + * This function fetches the 8-bit ADC value from the specified Angle device. + * + * @param id The position of the Angle device in the chain (starting from 1). + * @param adcValue Pointer to store the retrieved 8-bit ADC value. + * @param timeout Timeout duration in milliseconds (default is 100ms). + * + * @return The operation status (e.g., CHAIN_OK, CHAIN_TIMEOUT, etc.). + */ + chain_status_t getAngle8BitAdc(uint8_t id, uint8_t *adcValue, unsigned long timeout = 100); + + /** + * @brief Sets the rotation direction change status for the angle device at the specified position in the chain. + * + * This function sets the rotation direction change status for the angle device. The direction can either be + * increasing or decreasing in the clockwise direction. By default, the clockwise direction is set to increasing. + * + * @param id The position of the angle device in the chain (starting from 1). + * @param direction The clockwise direction status. Use `ANGLE_ROTATION_INCREASING` for increasing and + * `ANGLE_ROTATION_DECREASING` for decreasing. + * @param operationStatus Pointer to store the operation status after execution (1 for success, 0 for failure). + * @param saveToFlash Optional parameter to specify whether to save the setting to flash memory (default is 0, no + * save). + * @param timeout The timeout duration for the operation in milliseconds (default is 100ms). + * + * @return The operation status (e.g., `CHAIN_OK`, `CHAIN_PARAMETER_ERROR`, etc.). + */ + chain_status_t setAngleRotationDirection(uint8_t id, angle_rotation_direction_t direction, uint8_t *operationStatus, + chain_save_flash_t saveToFlash = CHAIN_SAVE_FLASH_DISABLE, + unsigned long timeout = 100); + + /** + * @brief Retrieves the current clockwise rotation change status of the angle device at the specified position in + * the chain. + * + * This function gets the current clockwise rotation change status of the angle device. It determines whether the + * rotation direction is increasing or decreasing in the clockwise direction. + * + * @param id The position of the angle device in the chain (starting from 1). + * @param direction Pointer to store the current clockwise rotation change status. It will hold + * `ANGLE_ROTATION_INCREASING` for increasing and `ANGLE_ROTATION_DECREASING` for decreasing. + * @param timeout The timeout duration for the operation in milliseconds (default is 100ms). + * + * @return The operation status (e.g., `CHAIN_OK`, `CHAIN_TIMEOUT`, etc.). + */ + chain_status_t getAngleRotationDirection(uint8_t id, angle_rotation_direction_t *direction, + unsigned long timeout = 100); + +private: +}; + +#endif // CHAIN_ANGLE_H \ No newline at end of file diff --git a/src/ChainCommon/ChainCommon.hpp b/src/ChainCommon/ChainCommon.hpp index e518ec7..e9d7974 100644 --- a/src/ChainCommon/ChainCommon.hpp +++ b/src/ChainCommon/ChainCommon.hpp @@ -182,12 +182,12 @@ typedef enum { CHAIN_KEY_TYPE_CODE = 0x0003, /**< Key device type. */ CHAIN_JOYSTICK_TYPE_CODE = 0x0004, /**< Joystick device type. */ CHAIN_TOF_TYPE_CODE = 0x0005, /**< TOF device type. */ - CHAIN_UART_TYPE_CODE = 0x0006, /**< UART device type. */ - CHAIN_SWITCH_TYPE_CODE = 0x0007, /**< Switch device type. */ - CHAIN_PEDAL_TYPE_CODE = 0x0008, /**< Pedal device type. */ - CHAIN_PIR_TYPE_CODE = 0x0009, /**< PIR device type. */ - CHAIN_MIC_TYPE_CODE = 0x000A, /**< Microphone device type. */ - CHAIN_BUZZER_TYPE_CODE = 0x000B, /**< Buzzer device type. */ + // CHAIN_UART_TYPE_CODE = 0x0006, /**< UART device type. */ + // CHAIN_SWITCH_TYPE_CODE = 0x0007, /**< Switch device type. */ + // CHAIN_PEDAL_TYPE_CODE = 0x0008, /**< Pedal device type. */ + // CHAIN_PIR_TYPE_CODE = 0x0009, /**< PIR device type. */ + // CHAIN_MIC_TYPE_CODE = 0x000A, /**< Microphone device type. */ + // CHAIN_BUZZER_TYPE_CODE = 0x000B, /**< Buzzer device type. */ } chain_device_type_t; /** diff --git a/src/ChainEncoder/ChainEncoder.cpp b/src/ChainEncoder/ChainEncoder.cpp new file mode 100644 index 0000000..9a0a6e6 --- /dev/null +++ b/src/ChainEncoder/ChainEncoder.cpp @@ -0,0 +1,295 @@ +/* + *SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + *SPDX-License-Identifier: MIT + */ + +#include "ChainEncoder/ChainEncoder.hpp" + +chain_status_t ChainEncoder::getEncoderValue(uint8_t id, int16_t *value, unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (acquireMutex()) { + cmdBufferSize = 0; + sendPacket(id, CHAIN_ENCODER_GET_VALUE, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_ENCODER_GET_VALUE, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *value = (int16_t)((returnPacket[7] << 8) | returnPacket[6]); + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} + +chain_status_t ChainEncoder::getEncoderIncValue(uint8_t id, int16_t *incValue, unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (acquireMutex()) { + cmdBufferSize = 0; + sendPacket(id, CHAIN_ENCODER_GET_INC_VALUE, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_ENCODER_GET_INC_VALUE, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *incValue = (int16_t)((returnPacket[7] << 8) | returnPacket[6]); + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} + +chain_status_t ChainEncoder::resetEncoderValue(uint8_t id, uint8_t *operationStatus, unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (acquireMutex()) { + cmdBufferSize = 0; + sendPacket(id, CHAIN_ENCODER_RESET_VALUE, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_ENCODER_RESET_VALUE, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *operationStatus = returnPacket[6]; + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} + +chain_status_t ChainEncoder::resetEncoderIncValue(uint8_t id, uint8_t *operationStatus, unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (acquireMutex()) { + cmdBufferSize = 0; + sendPacket(id, CHAIN_ENCODER_RESET_INC_VALUE, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_ENCODER_RESET_INC_VALUE, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *operationStatus = returnPacket[6]; + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} + +chain_status_t ChainEncoder::setEncoderABDirect(uint8_t id, encoder_ab_t direct, uint8_t *operationStatus, + chain_save_flash_t saveToFlash, unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (acquireMutex()) { + cmdBufferSize = 0; + cmdBuffer[cmdBufferSize++] = direct; + cmdBuffer[cmdBufferSize++] = saveToFlash; + sendPacket(id, CHAIN_ENCODER_SET_AB_STATUS, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_ENCODER_SET_AB_STATUS, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *operationStatus = returnPacket[6]; + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} + +chain_status_t ChainEncoder::getEncoderABDirect(uint8_t id, encoder_ab_t *direct, unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (acquireMutex()) { + cmdBufferSize = 0; + sendPacket(id, CHAIN_ENCODER_GET_AB_STATUS, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_ENCODER_GET_AB_STATUS, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *direct = (encoder_ab_t)returnPacket[6]; + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} + +chain_status_t ChainEncoder::getEncoderButtonStatus(uint8_t id, uint8_t *buttonStatus, unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (acquireMutex()) { + cmdBufferSize = 0; + sendPacket(id, CHAIN_BUTTON_GET_STATUS, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_BUTTON_GET_STATUS, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *buttonStatus = returnPacket[6]; + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + return status; +} + +chain_status_t ChainEncoder::setEncoderButtonTriggerInterval(uint8_t id, button_double_click_time_t doubleClickTimeout, + button_long_press_time_t longPressTimeout, + uint8_t *operationStatus, unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (acquireMutex()) { + cmdBufferSize = 0; + cmdBuffer[cmdBufferSize++] = doubleClickTimeout; + cmdBuffer[cmdBufferSize++] = longPressTimeout; + sendPacket(id, CHAIN_BUTTON_SET_TRIGGER_TIMEOUT, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_BUTTON_SET_TRIGGER_TIMEOUT, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *operationStatus = returnPacket[6]; + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} + +chain_status_t ChainEncoder::getEncoderButtonTriggerInterval(uint8_t id, button_double_click_time_t *doubleClickTimeout, + button_long_press_time_t *longPressTimeout, + unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (acquireMutex()) { + cmdBufferSize = 0; + sendPacket(id, CHAIN_BUTTON_GET_TRIGGER_TIMEOUT, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_BUTTON_GET_TRIGGER_TIMEOUT, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *doubleClickTimeout = (button_double_click_time_t)returnPacket[6]; + *longPressTimeout = (button_long_press_time_t)returnPacket[7]; + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} + +chain_status_t ChainEncoder::setEncoderButtonMode(uint8_t id, chain_button_mode_t mode, uint8_t *operationStatus, + unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (acquireMutex()) { + cmdBufferSize = 0; + cmdBuffer[cmdBufferSize++] = mode; + sendPacket(id, CHAIN_BUTTON_SET_MODE, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_BUTTON_SET_MODE, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *operationStatus = returnPacket[6]; + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} + +chain_status_t ChainEncoder::getEncoderButtonMode(uint8_t id, chain_button_mode_t *mode, unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (acquireMutex()) { + cmdBufferSize = 0; + sendPacket(id, CHAIN_BUTTON_GET_MODE, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_BUTTON_GET_MODE, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *mode = (chain_button_mode_t)returnPacket[6]; + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} + +bool ChainEncoder::getEncoderButtonPressStatus(uint8_t id, chain_button_press_type_t *buttonStatus) +{ + processIncomingData(); + bool findStatus = 0; + record_info_t result; + findStatus = findRecord(&recordList, id, &result); + if (findStatus == true) { + *buttonStatus = (chain_button_press_type_t)result.type; + return true; + } + return false; +} \ No newline at end of file diff --git a/src/ChainEncoder/ChainEncoder.hpp b/src/ChainEncoder/ChainEncoder.hpp new file mode 100644 index 0000000..7cf5df1 --- /dev/null +++ b/src/ChainEncoder/ChainEncoder.hpp @@ -0,0 +1,210 @@ +/* + *SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + *SPDX-License-Identifier: MIT + */ + +#ifndef _CHAIN_ENCODER_HPP_ +#define _CHAIN_ENCODER_HPP_ + +#include "ChainCommon/ChainCommon.hpp" + +/** + * @brief Enumeration for AB pin status. + */ +typedef enum { + ENCODER_AB = 0x00, /**< AB encoding, clockwise increment. */ + ENCODER_BA = 0x01, /**< BA encoding, counterclockwise increment. */ +} encoder_ab_t; + +/** + * @brief Command types for the Chain_Encoder. + */ +typedef enum { + CHAIN_ENCODER_GET_VALUE = 0x10, /**< Get encoder value. */ + CHAIN_ENCODER_GET_INC_VALUE = 0x11, /**< Get encoder increment value. */ + CHAIN_ENCODER_RESET_VALUE = 0x13, /**< Reset encoder value. */ + CHAIN_ENCODER_RESET_INC_VALUE = 0x14, /**< Reset encoder increment value. */ + CHAIN_ENCODER_SET_AB_STATUS = 0x15, /**< Set AB status, 0->AB, 1->BA. */ + CHAIN_ENCODER_GET_AB_STATUS = 0x16, /**< Get AB status, 0->AB, 1->BA. */ +} chain_encoder_cmd_t; + +class ChainEncoder : virtual public ChainCommon { +public: + /** + * @brief Retrieves the encoder value. + * + * This function retrieves the current encoder value for the specified device in the chain. + * + * @param id The position of the device in the chain (starting from 1). + * @param value Pointer to the variable where the encoder value will be stored. + * @param timeout The timeout period for the operation (default is 100ms). + * + * @return Returns the status of the operation (e.g., success or failure). + */ + chain_status_t getEncoderValue(uint8_t id, int16_t *value, unsigned long timeout = 100); + + /** + * @brief Retrieves the encoder increment value. + * + * This function retrieves the increment value of the encoder for the specified device in the chain. + * The increment value represents the change in encoder position since the last read. + * + * @param id The position of the device in the chain (starting from 1). + * @param incValue Pointer to the variable where the increment value will be stored. + * @param timeout The timeout period for the operation (default is 100ms). + * + * @return Returns the status of the operation (e.g., success or failure). + */ + chain_status_t getEncoderIncValue(uint8_t id, int16_t *incValue, unsigned long timeout = 100); + + /** + * @brief Resets the encoder value. + * + * This function resets the encoder value to its initial state for the specified device in the chain. + * It is typically used when the encoder value needs to be cleared or initialized. + * + * @param id The position of the device in the chain (starting from 1). + * @param operationStatus Pointer to the variable where the operation status (1 for success, 0 for failure) will be + * stored. + * @param timeout The timeout period for the operation (default is 100ms). + * + * @return Returns the status of the operation (1 for success, 0 for failure). + */ + chain_status_t resetEncoderValue(uint8_t id, uint8_t *operationStatus, unsigned long timeout = 100); + + /** + * @brief Resets the encoder increment value. + * + * This function resets the encoder increment value to its initial state for the specified device in the chain. + * It is typically used when the increment value needs to be cleared or initialized. + * + * @param id The position of the device in the chain (starting from 1). + * @param operationStatus Pointer to the variable where the operation status (1 for success, 0 for failure) will be + * stored. + * @param timeout The timeout period for the operation (default is 100ms). + * + * @return Returns the status of the operation (1 for success, 0 for failure). + */ + chain_status_t resetEncoderIncValue(uint8_t id, uint8_t *operationStatus, unsigned long timeout = 100); + + /** + * @brief Sets the encoder AB direction. + * + * This function sets the AB direction of the encoder for the specified device in the chain. + * AB means the direction increases clockwise, and BA means the direction increases counterclockwise. + * + * @param id The position of the device in the chain (starting from 1). + * @param direct The direction to set: 0 for AB (clockwise increase), 1 for BA (counterclockwise increase). + * @param operationStatus Pointer to the variable where the operation status (1 for success, 0 for failure) will be + * stored. + * @param saveToFlash Optional parameter to save the setting to flash memory (default is CHAIN_SAVE_FLASH_DISABLE, + * not save). + * @param timeout The timeout period for the operation (default is 100ms). + * + * @return Returns the status of the operation (1 for success, 0 for failure). + */ + chain_status_t setEncoderABDirect(uint8_t id, encoder_ab_t direct, uint8_t *operationStatus, + chain_save_flash_t saveToFlash = CHAIN_SAVE_FLASH_DISABLE, + unsigned long timeout = 100); + + /** + * @brief Gets the encoder AB direction. + * + * This function retrieves the current AB direction of the encoder for the specified device in the chain. + * AB means the direction increases clockwise, and BA means the direction increases counterclockwise. + * + * @param id The position of the device in the chain (starting from 1). + * @param direct Pointer to the variable where the direction (0 for AB, 1 for BA) will be stored. + * @param timeout The timeout period for the operation (default is 100ms). + * + * @return Returns the status of the operation (1 for success, 0 for failure). + */ + chain_status_t getEncoderABDirect(uint8_t id, encoder_ab_t *direct, unsigned long timeout = 100); + + /** + * @brief Get the status of the encoder button device at a specified chain position. + * + * Retrieves whether the encoder button is currently pressed. + * + * @param id The encoder button device position in the chain (starting from 1). + * @param buttonStatus Pointer to receive the status (1: pressed, 0: not pressed). + * @param timeout Operation timeout in milliseconds (default 100ms). + * @return Operation result status code (e.g., CHAIN_OK, CHAIN_PARAMETER_ERROR). + */ + chain_status_t getEncoderButtonStatus(uint8_t id, uint8_t *buttonStatus, unsigned long timeout = 100); + + /** + * @brief Set the double-click and long-press interval times for the encoder button device. + * + * Configure the required interval durations (in milliseconds) to detect double-click and long-press actions. + * + * @param id The position of the encoder button device in the chain (starting from 1). + * @param doubleClickIntervalMs Pointer to a variable storing the double-click interval time in milliseconds. + * @param longPressIntervalMs Pointer to a variable storing the long-press interval time in milliseconds. + * @param operationStatus Pointer to store the operation result (1 for success, 0 for failure). + * @param timeout Operation timeout in milliseconds (default is 100 ms). + * + * @return Operation status code, e.g., CHAIN_OK for success, CHAIN_PARAMETER_ERROR for invalid parameters. + */ + chain_status_t setEncoderButtonTriggerInterval(uint8_t id, button_double_click_time_t doubleClickIntervalMs, + button_long_press_time_t longPressIntervalMs, + uint8_t *operationStatus, unsigned long timeout = 100); + + /** + * @brief Get the double-click and long-press interval times for the encoder button device. + * + * Retrieve the current interval durations (in milliseconds) used for detecting double-click and long-press actions. + * + * @param id The position of the encoder button device in the chain (starting from 1). + * @param doubleClickIntervalMs Pointer to a variable storing the double-click interval time in milliseconds. + * @param longPressIntervalMs Pointer to a variable storing the long-press interval time in milliseconds. + * @param timeout Operation timeout in milliseconds (default is 100 ms). + * + * @return Operation status code, e.g., CHAIN_OK for success, CHAIN_PARAMETER_ERROR for invalid parameters. + */ + chain_status_t getEncoderButtonTriggerInterval(uint8_t id, button_double_click_time_t *doubleClickIntervalMs, + button_long_press_time_t *longPressIntervalMs, + unsigned long timeout = 100); + + /** + * @brief Set the operating mode of the encoder button device. + * + * Defines the button behavior mode, such as single-click, double-click, or long-press. + * + * @param id The encoder device position in the chain (starting from 1). + * @param mode Desired encoder button mode. + * @param operationStatus Pointer to receive operation result (1: success, 0: failure). + * @param timeout Operation timeout in milliseconds (default 100ms). + * @return Operation result status code. + */ + chain_status_t setEncoderButtonMode(uint8_t id, chain_button_mode_t mode, uint8_t *operationStatus, + unsigned long timeout = 100); + + /** + * @brief Get the current operating mode of the encoder button device. + * + * Retrieves the current button mode setting. + * + * @param id The encoder device position in the chain (starting from 1). + * @param mode Pointer to receive the current mode. + * @param timeout Operation timeout in milliseconds (default 100ms). + * @return Operation result status code. + */ + chain_status_t getEncoderButtonMode(uint8_t id, chain_button_mode_t *mode, unsigned long timeout = 100); + + /** + * @brief Get the press type status of the encoder button device. + * + * Determines the type of press event (single, double, long). + * + * @param id The encoder device position in the chain (starting from 1). + * @param encoderPressType Pointer to receive the press type. + * @return true if the encoder button is currently pressed; false otherwise. + */ + bool getEncoderButtonPressStatus(uint8_t id, chain_button_press_type_t *encoderPressType); + +private: +}; + +#endif // _CHAIN_ENCODER_HPP_ diff --git a/src/ChainJoystick/ChainJoystick.cpp b/src/ChainJoystick/ChainJoystick.cpp index 77710ae..54c0ca1 100644 --- a/src/ChainJoystick/ChainJoystick.cpp +++ b/src/ChainJoystick/ChainJoystick.cpp @@ -60,8 +60,7 @@ chain_status_t ChainJoystick::getJoystickMappedRange(uint8_t id, uint16_t *mapBu { chain_status_t status = CHAIN_OK; - if(size != JOYSTICK_MAP_SIZE) - { + if (size != JOYSTICK_MAP_SIZE) { return CHAIN_PARAMETER_ERROR; } @@ -93,8 +92,7 @@ chain_status_t ChainJoystick::setJoystickMappedRange(uint8_t id, uint16_t *mapBu { chain_status_t status = CHAIN_OK; - if(size != JOYSTICK_MAP_SIZE) - { + if (size != JOYSTICK_MAP_SIZE) { return CHAIN_PARAMETER_ERROR; } @@ -119,7 +117,7 @@ chain_status_t ChainJoystick::setJoystickMappedRange(uint8_t id, uint16_t *mapBu } else { status = CHAIN_BUSY; } - + return status; } @@ -200,9 +198,10 @@ chain_status_t ChainJoystick::getJoystickButtonStatus(uint8_t id, uint8_t *keySt return status; } -chain_status_t ChainJoystick::setJoystickButtonTriggerInterval(uint8_t id, button_double_click_time_t doubleClickIntervalMs, - button_long_press_time_t longPressIntervalMs, uint8_t *operationStatus, - unsigned long timeout) +chain_status_t ChainJoystick::setJoystickButtonTriggerInterval(uint8_t id, + button_double_click_time_t doubleClickIntervalMs, + button_long_press_time_t longPressIntervalMs, + uint8_t *operationStatus, unsigned long timeout) { chain_status_t status = CHAIN_OK; @@ -228,8 +227,10 @@ chain_status_t ChainJoystick::setJoystickButtonTriggerInterval(uint8_t id, butto return status; } -chain_status_t ChainJoystick::getJoystickButtonTriggerInterval(uint8_t id, button_double_click_time_t *doubleClickIntervalMs, - button_long_press_time_t *longPressIntervalMs, unsigned long timeout) +chain_status_t ChainJoystick::getJoystickButtonTriggerInterval(uint8_t id, + button_double_click_time_t *doubleClickIntervalMs, + button_long_press_time_t *longPressIntervalMs, + unsigned long timeout) { chain_status_t status = CHAIN_OK; diff --git a/src/ChainJoystick/ChainJoystick.hpp b/src/ChainJoystick/ChainJoystick.hpp index 35e9dee..4096912 100644 --- a/src/ChainJoystick/ChainJoystick.hpp +++ b/src/ChainJoystick/ChainJoystick.hpp @@ -7,7 +7,7 @@ #ifndef _CHAIN_JOYSTICK_HPP_ #define _CHAIN_JOYSTICK_HPP_ -#include +#include "ChainCommon/ChainCommon.hpp" /** * @brief Size of the joystick map buffer. @@ -155,8 +155,8 @@ public: * @return Operation status code, e.g., CHAIN_OK for success, CHAIN_PARAMETER_ERROR for invalid parameters. */ chain_status_t setJoystickButtonTriggerInterval(uint8_t id, button_double_click_time_t doubleClickIntervalMs, - button_long_press_time_t longPressIntervalMs, uint8_t *operationStatus, - unsigned long timeout = 100); + button_long_press_time_t longPressIntervalMs, + uint8_t *operationStatus, unsigned long timeout = 100); /** * @brief Get the double-click and long-press interval times for the joystick button device. @@ -171,8 +171,8 @@ public: * @return Operation status code, e.g., CHAIN_OK for success, CHAIN_PARAMETER_ERROR for invalid parameters. */ chain_status_t getJoystickButtonTriggerInterval(uint8_t id, button_double_click_time_t *doubleClickIntervalMs, - button_long_press_time_t *longPressIntervalMs, - unsigned long timeout = 100); + button_long_press_time_t *longPressIntervalMs, + unsigned long timeout = 100); /** * @brief Set the operating mode of the joystick button device. diff --git a/src/ChainKey/ChainKey.hpp b/src/ChainKey/ChainKey.hpp index bb6bd29..be173b8 100644 --- a/src/ChainKey/ChainKey.hpp +++ b/src/ChainKey/ChainKey.hpp @@ -7,7 +7,7 @@ #ifndef _CHAIN_KEY_HPP_ #define _CHAIN_KEY_HPP_ -#include +#include "ChainCommon/ChainCommon.hpp" class ChainKey : virtual public ChainCommon { public: diff --git a/src/ChainToF/ChainToF.cpp b/src/ChainToF/ChainToF.cpp new file mode 100644 index 0000000..3eb75aa --- /dev/null +++ b/src/ChainToF/ChainToF.cpp @@ -0,0 +1,218 @@ +/* + *SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + *SPDX-License-Identifier: MIT + */ + +#include "ChainToF/ChainToF.hpp" + +chain_status_t ChainToF::getToFDistance(uint8_t id, uint16_t *distance, unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (acquireMutex()) { + cmdBufferSize = 0; + sendPacket(id, CHAIN_TOF_GET_DISTANCE, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_TOF_GET_DISTANCE, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *distance = (returnPacket[7] << 8) | returnPacket[6]; + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} + +chain_status_t ChainToF::setToFMeasureTime(uint8_t id, uint8_t time, uint8_t *operationStatus, unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (time < TOF_MEASUREMENT_TIME_MIN || time > TOF_MEASUREMENT_TIME_MAX) { + return CHAIN_PARAMETER_ERROR; + } + + if (acquireMutex()) { + cmdBufferSize = 0; + cmdBuffer[cmdBufferSize++] = time; + sendPacket(id, CHAIN_TOF_SET_MEASURE_TIME, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_TOF_SET_MEASURE_TIME, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *operationStatus = returnPacket[6]; + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} + +chain_status_t ChainToF::getToFMeasureTime(uint8_t id, uint8_t *time, unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (acquireMutex()) { + cmdBufferSize = 0; + sendPacket(id, CHAIN_TOF_GET_MEASURE_TIME, cmdBuffer, cmdBufferSize); + + if (waitForData(id, CHAIN_TOF_GET_MEASURE_TIME, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *time = returnPacket[6]; + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} + +chain_status_t ChainToF::setToFMeasureMode(uint8_t id, chain_tof_mode_t mode, uint8_t *operationStatus, + unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (mode < CHAIN_TOF_MODE_STOP || mode > CHAIN_TOF_MODE_CONTINUOUS) { + return CHAIN_PARAMETER_ERROR; + } + + if (acquireMutex()) { + cmdBufferSize = 0; + cmdBuffer[cmdBufferSize++] = mode; + sendPacket(id, CHAIN_TOF_SET_MEASURE_MODE, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_TOF_SET_MEASURE_MODE, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *operationStatus = returnPacket[6]; + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} + +chain_status_t ChainToF::getToFMeasureMode(uint8_t id, chain_tof_mode_t *mode, unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (acquireMutex()) { + cmdBufferSize = 0; + sendPacket(id, CHAIN_TOF_GET_MEASURE_MODE, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_TOF_GET_MEASURE_MODE, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *mode = (chain_tof_mode_t)returnPacket[6]; + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} + +chain_status_t ChainToF::setToFMeasureStatus(uint8_t id, chain_tof_measure_status_t measureStatus, + uint8_t *operationStatus, unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (measureStatus < CHAIN_TOF_STATUS_STOP || measureStatus > CHAIN_TOF_STATUS_START) { + return CHAIN_PARAMETER_ERROR; + } + + if (acquireMutex()) { + cmdBufferSize = 0; + cmdBuffer[cmdBufferSize++] = measureStatus; + sendPacket(id, CHAIN_TOF_SET_MEASURE_STATUS, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_TOF_SET_MEASURE_STATUS, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *operationStatus = returnPacket[6]; + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} + +chain_status_t ChainToF::getToFMeasureStatus(uint8_t id, chain_tof_measure_status_t *measureStatus, + unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (acquireMutex()) { + cmdBufferSize = 0; + sendPacket(id, CHAIN_TOF_GET_MEASURE_STATUS, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_TOF_GET_MEASURE_STATUS, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *measureStatus = (chain_tof_measure_status_t)returnPacket[6]; + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} + +chain_status_t ChainToF::getToFMeasureCompleteFlag(uint8_t id, uint8_t *completeFlag, unsigned long timeout) +{ + chain_status_t status = CHAIN_OK; + + if (acquireMutex()) { + cmdBufferSize = 0; + sendPacket(id, CHAIN_TOF_GET_MEASURE_COMPLETE_FLAG, cmdBuffer, cmdBufferSize); + if (waitForData(id, CHAIN_TOF_GET_MEASURE_COMPLETE_FLAG, timeout)) { + if (checkPacket(returnPacket, returnPacketSize)) { + *completeFlag = returnPacket[6]; + } else { + status = CHAIN_RETURN_PACKET_ERROR; + } + } else { + status = CHAIN_TIMEOUT; + } + releaseMutex(); + } else { + status = CHAIN_BUSY; + } + + return status; +} \ No newline at end of file diff --git a/src/ChainToF/ChainToF.hpp b/src/ChainToF/ChainToF.hpp new file mode 100644 index 0000000..63740c5 --- /dev/null +++ b/src/ChainToF/ChainToF.hpp @@ -0,0 +1,166 @@ +/* + * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ + +#ifndef _CHAIN_TOF_HPP_ +#define _CHAIN_TOF_HPP_ + +#include "ChainCommon/ChainCommon.hpp" + +/** + * @brief Minimum allowable measurement time for the Chain_ToF sensor (in milliseconds). + * + * Shorter integration time results in faster updates but lower accuracy. + */ +#define TOF_MEASUREMENT_TIME_MIN (20) + +/** + * @brief Maximum allowable measurement time for the Chain_ToF sensor (in milliseconds). + * + * Longer integration time generally increases accuracy but reduces the update rate. + */ +#define TOF_MEASUREMENT_TIME_MAX (200) + +/** + * @brief Command codes for Chain_ToF sensor operations. + * + * These codes are used when communicating with the device over the chain protocol. + */ +typedef enum { + CHAIN_TOF_GET_DISTANCE = 0x50, /**< Retrieve distance measurement from the sensor */ + CHAIN_TOF_SET_MEASURE_TIME = 0x51, /**< Set the measurement (integration) time */ + CHAIN_TOF_GET_MEASURE_TIME = 0x52, /**< Get the current measurement time */ + CHAIN_TOF_SET_MEASURE_MODE = 0x53, /**< Set the measurement mode (STOP / SINGLE / CONTINUOUS) */ + CHAIN_TOF_GET_MEASURE_MODE = 0x54, /**< Get the current measurement mode */ + CHAIN_TOF_SET_MEASURE_STATUS = 0x55, /**< Start or stop measurement explicitly */ + CHAIN_TOF_GET_MEASURE_STATUS = 0x56, /**< Get the current measurement status */ + CHAIN_TOF_GET_MEASURE_COMPLETE_FLAG = 0x57 /**< Check if the latest measurement is complete */ +} chain_tof_cmd_t; + +/** + * @brief Measure modes for Chain_ToF sensor. + */ +typedef enum { + CHAIN_TOF_MODE_STOP = 0, /**< Stop measurements; sensor idle */ + CHAIN_TOF_MODE_SINGLE = 1, /**< Take a single measurement then stop */ + CHAIN_TOF_MODE_CONTINUOUS = 2 /**< Continue measuring until stopped */ +} chain_tof_mode_t; + +/** + * @brief Measure status values for Chain_ToF sensor. + * + * Used to explicitly start or stop measurements. + */ +typedef enum { + CHAIN_TOF_STATUS_STOP = 0, /**< Stop the measurement process */ + CHAIN_TOF_STATUS_START = 1 /**< Start the measurement process */ +} chain_tof_measure_status_t; + +/** + * @class ChainToF + * @brief Interface for controlling chained Time-of-Flight (ToF) distance sensors. + * + * The ChainToF class inherits from ChainCommon and provides high-level methods + * for querying measurements, setting timing and modes, and controlling the measurement lifecycle. + * Sensors are addressed by their position in the chain starting from index 1. + */ +class ChainToF : virtual public ChainCommon { +public: + /** + * @brief Get the measured distance from the ToF device. + * + * @param id Position of the ToF device in the chain (starting from 1). + * @param distance Pointer to store the measured distance (in mm). + * @param timeout Timeout duration in milliseconds (default: 100 ms). + * @return Operation status (e.g., CHAIN_OK, CHAIN_TIMEOUT). + */ + chain_status_t getToFDistance(uint8_t id, uint16_t *distance, unsigned long timeout = 100); + + /** + * @brief Set the measurement timing (integration time) for the ToF device. + * + * @param id Position of the ToF device in the chain (starting from 1). + * @param time Measure time in milliseconds. + * Must be between TOF_MEASUREMENT_TIME_MIN and TOF_MEASUREMENT_TIME_MAX. + * @param operationStatus Pointer to store the device's execution result (1 = accepted, 0 = rejected). + * @param timeout Timeout duration in milliseconds (default: 100 ms). + * @return Operation status. + */ + chain_status_t setToFMeasureTime(uint8_t id, uint8_t time, uint8_t *operationStatus, unsigned long timeout = 100); + + /** + * @brief Get the current measurement timing from the ToF device. + * + * @param id Position of the ToF device in the chain (starting from 1). + * @param time Pointer to store the measurement time in milliseconds. + * @param timeout Timeout duration in milliseconds (default: 100 ms). + * @return Operation status. + */ + chain_status_t getToFMeasureTime(uint8_t id, uint8_t *time, unsigned long timeout = 100); + + /** + * @brief Set the measurement mode. + * + * @param id Position of the ToF device in the chain (starting from 1). + * @param mode Desired measurement mode (`chain_tof_mode_t`). + * @param operationStatus Pointer to store the device's execution result (1 = accepted, 0 = rejected). + * @param timeout Timeout duration in milliseconds (default: 100 ms). + * @return Operation status. + */ + chain_status_t setToFMeasureMode(uint8_t id, chain_tof_mode_t mode, uint8_t *operationStatus, + unsigned long timeout = 100); + + /** + * @brief Get the current measurement mode from the ToF device. + * + * @param id Position of the ToF device in the chain (starting from 1). + * @param mode Pointer to store the retrieved mode (`chain_tof_mode_t`). + * @param timeout Timeout duration in milliseconds (default: 100 ms). + * @return Operation status. + */ + chain_status_t getToFMeasureMode(uint8_t id, chain_tof_mode_t *mode, unsigned long timeout = 100); + + /** + * @brief Set the measurement status (START / STOP) for the ToF device. + * + * Explicitly starts or stops the measurement process, regardless of the current mode. + * + * @param id Position of the ToF device in the chain (starting from 1). + * @param measureStatus Desired status (`chain_tof_measure_status_t`). + * @param operationStatus Pointer to store the device's execution result (1 = accepted, 0 = rejected). + * @param timeout Timeout duration in milliseconds (default: 100 ms). + * @return Operation status. + */ + chain_status_t setToFMeasureStatus(uint8_t id, chain_tof_measure_status_t measureStatus, uint8_t *operationStatus, + unsigned long timeout = 100); + + /** + * @brief Get the current measurement status (START / STOP) from the ToF device. + * + * @param id Position of the ToF device in the chain (starting from 1). + * @param measureStatus Pointer to store the retrieved status (`chain_tof_measure_status_t`). + * @param timeout Timeout duration in milliseconds (default: 100 ms). + * @return Operation status. + */ + chain_status_t getToFMeasureStatus(uint8_t id, chain_tof_measure_status_t *measureStatus, + unsigned long timeout = 100); + + /** + * @brief Check whether the current measurement cycle is complete. + * + * This flag can be used to determine if the latest measurement data is ready for retrieval. + * + * @param id Position of the ToF device in the chain (starting from 1). + * @param completeFlag Pointer to store the completion flag (1 = complete, 0 = not ready). + * @param timeout Timeout duration in milliseconds (default: 100 ms). + * @return Operation status. + */ + chain_status_t getToFMeasureCompleteFlag(uint8_t id, uint8_t *completeFlag, unsigned long timeout = 100); + +private: + // No private members declared, potentially reserved for internal buffer or state tracking. +}; + +#endif // _CHAIN_TOF_HPP_ \ No newline at end of file