Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add int32_publisher_custom_transport_usbcdc example (backport #255) #262

Merged
merged 1 commit into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ jobs:
idf.py set-target ${{ matrix.idf_target }}
idf.py build

- name: Build sample - int32_publisher_custom_transport_usbcdc
shell: bash
if: (matrix.idf_target == 'esp32s2' || matrix.idf_target == 'esp32s3') && matrix.idf_version == 'espressif/idf:release-v5.2'
run: |
. $IDF_PATH/export.sh
cd micro_ros_espidf_component/examples/int32_publisher_custom_transport_usbcdc
idf.py set-target ${{ matrix.idf_target }}
idf.py build

- name: Build sample - multithread_publisher
shell: bash
run: |
Expand Down
5 changes: 5 additions & 0 deletions examples/int32_publisher_custom_transport_usbcdc/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
build
sdkconfig
sdkconfig.old
managed_components
.vscode
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.5)

set (EXTRA_COMPONENT_DIRS "./../../.")

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(int32_publisher)

138 changes: 138 additions & 0 deletions examples/int32_publisher_custom_transport_usbcdc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@

# USB-CDC Custom Transport Example

| Supported Targets | ESP32-S2 | ESP32-S3 |
|-------------------|----------|----------|

This example demonstrates how to set up the ESP32-S2/S3 to function as a USB Serial Device (CDC-ACM) and communicate with micro-ROS agent using USB-CDC custom transport.

The [TinyUSB component](https://components.espressif.com/components/espressif/esp_tinyusb) is used as the USB stack.

This example is based on the [int32_publisher_custom_transport](https://github.com/micro-ROS/micro_ros_espidf_component/tree/jazzy/examples/int32_publisher_custom_transport), the [TinyUSB Serial Device Example](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/usb/device/tusb_serial_device), and the [TinyUSB Console Example](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/usb/device/tusb_console) for log output.

## How to use example

This example is configured to use the two interfaces of USB-CDC. One interface is used for the micro-ROS communication, and the other interface is used for the log output.

### Hardware Required

This example can be run on any development board that has a USB-CDC interface.

### Configure the project

Set the target device in the project configuration:

```bash
idf.py set-target esp32s2 # or esp32s3
```

If you want to use only the micro-ROS communication interface, you need to turn off log output in menuconfig. Run `idf.py menuconfig` and navigate to `Component config → Log output → Default log verbosity` and set it to `No output`. You should also set `Component config → TinyUSB Stack → Communication Device Class (CDC) → CDC Channel Count` to 1.

### Build and Flash

> [!NOTE]
> The ESP32-S2/S3 chip needs to be in bootloader mode before it can be detected as a DFU device and flash. This can be achieved by pulling GPIO0 down (e.g., pressing the BOOT button), pulling RESET down for a moment, and releasing GPIO0.

#### Build the project

Build DFU image:

```bash
idf.py dfu
```

#### Flash the project

Put the ESP32-S2/S3 into bootloader mode and run the following command:

```bash
idf.py dfu-flash
```

### Run micro-ROS Agent

```bash
export ROS_DOMAIN_ID=100 # Set the ROS2 domain ID
ros2 run micro_ros_agent micro_ros_agent serial --dev /dev/ttyACM0
```

Output expected:

```bash
[1724443525.673894] info | TermiosAgentLinux.cpp | init | running... | fd: 3
[1724443525.674071] info | Root.cpp | set_verbose_level | logger setup | verbose_level: 4
[1724443529.936542] info | TermiosAgentLinux.cpp | init | running... | fd: 3
[1724443531.062646] info | Root.cpp | create_client | create | client_key: 0x3E801A05, session_id: 0x81
[1724443531.062805] info | SessionManager.hpp | establish_session | session established | client_key: 0x3E801A05, address: 0
[1724443531.107532] info | ProxyClient.cpp | create_participant | participant created | client_key: 0x3E801A05, participant_id: 0x000(1)
[1724443531.137064] info | ProxyClient.cpp | create_topic | topic created | client_key: 0x3E801A05, topic_id: 0x000(2), participant_id: 0x000(1)
[1724443531.167351] info | ProxyClient.cpp | create_publisher | publisher created | client_key: 0x3E801A05, publisher_id: 0x000(3), participant_id: 0x000(1)
[1724443531.237811] info | ProxyClient.cpp | create_datawriter | datawriter created | client_key: 0x3E801A05, datawriter_id: 0x000(5), publisher_id: 0x000(3)
```

After connecting the ESP32-S2/S3 and the micro-ROS agent, you can list the topics:

```bash
export ROS_DOMAIN_ID=100 # Set the ROS2 domain ID
ros2 topic list
```

Output expected:

```bash
/esp32s2/int32_publisher_usbcdc
/parameter_events
/rosout
```

And see if the `esp32s2/int32_publisher_usbcdc` topic is available. You can echo the topic to see the messages:

```bash
export ROS_DOMAIN_ID=100 # Set the ROS2 domain ID
ros2 topic echo esp32s2/int32_publisher_usbcdc
```

Output expected:

```bash
data: 1
---
data: 2
---
data: 3
---
data: 4
---
data: 5
.
.
.
```

To see the log output, you can use the following command:

```bash
minicom -D /dev/ttyACM1 -b 115200
```

Output expected:

```bash
Welcome to minicom 2.8

OPTIONS: I18n
Port /dev/ttyACM1
Press CTRL-A Z for help on special keys

I (2688) MAIN: micro-ROS task created
I (2688) main_task: Returned from app_main()
I (3708) TIMER_CALLBACK: Message published: 0
I (4708) TIMER_CALLBACK: Message published: 1
I (5708) TIMER_CALLBACK: Message published: 2
I (6708) TIMER_CALLBACK: Message published: 3
I (7708) TIMER_CALLBACK: Message published: 4
I (8708) TIMER_CALLBACK: Message published: 5
.
.
.
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"names": {
"rmw_microxrcedds": {
"cmake-args": [
"-DRMW_UXRCE_TRANSPORT=custom"
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
idf_component_register(SRCS "esp32s2_usbcdc_logging.c" INCLUDE_DIRS ".")
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include "esp32s2_usbcdc_logging.h"

// Initialize USB-CDC logging
esp_err_t esp32s2_usbcdc_logging_init(void)
{
const tinyusb_config_t tinyusb_config = {
.descriptor = NULL,
.string_descriptor = NULL,
.external_phy = false,
.configuration_descriptor = NULL,
};

esp_err_t ret = tinyusb_driver_install(&tinyusb_config);

if (ret == ESP_ERR_INVALID_ARG || ret == ESP_FAIL) {
return ret;
}

tinyusb_config_cdcacm_t acm_config = {
.usb_dev = TINYUSB_USBDEV_0,
.cdc_port = TINYUSB_CDC_ACM_1,
.rx_unread_buf_sz = CONFIG_TINYUSB_CDC_RX_BUFSIZE,
.callback_rx = NULL,
.callback_rx_wanted_char = NULL,
.callback_line_state_changed = NULL,
.callback_line_coding_changed = NULL,
};

ret = tusb_cdc_acm_init(&acm_config);

if (ret != ESP_OK) {
return ret;
}

ret = esp_tusb_init_console(TINYUSB_CDC_ACM_1);

return ret;
}

// Deinitialize USB-CDC logging
esp_err_t esp32s2_usbcdc_logging_deinit(void)
{
esp_err_t ret = esp_tusb_deinit_console(TINYUSB_CDC_ACM_1);

return ret;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef ESP32S2_USBCDC_LOGGING_H
#define ESP32S2_USBCDC_LOGGING_H

#include "esp_err.h"
#include "tinyusb.h"
#include "tusb_cdc_acm.h"
#include "tusb_console.h"
#include "sdkconfig.h"

#if (CONFIG_TINYUSB_CDC_COUNT < 2)
#warning "Define CONFIG_TINYUSB_CDC_COUNT to 2 in menuconfig if you want log over USBCDC. Otherwise, disable log output in menuconfig."
#endif

#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)

#ifdef __cplusplus
extern "C"
{
#endif

esp_err_t esp32s2_usbcdc_logging_init(void);
esp_err_t esp32s2_usbcdc_logging_deinit(void);

#ifdef __cplusplus
}
#endif

#else
#error "Logging over USB-CDC is only supported on ESP32-S2 or ESP32-S3 targets"
#endif

#endif // ESP32S2_USBCDC_LOGGING_H
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## IDF Component Manager Manifest File
dependencies:
espressif/esp_tinyusb: "^1.4.4"
## Required IDF version
idf:
version: ">=5.0"
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
idf_component_register(SRCS "esp32s2_usbcdc_transport.c"
INCLUDE_DIRS "."
REQUIRES micro_ros_espidf_component # include <uxr/client/transport.h>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include "esp32s2_usbcdc_transport.h"

// Open USB-CDC
bool esp32s2_usbcdc_open(struct uxrCustomTransport* transport) {
const tinyusb_config_t tinyusb_config = {
.device_descriptor = NULL,
.string_descriptor = NULL,
.external_phy = false,
.configuration_descriptor = NULL,
};

esp_err_t ret = tinyusb_driver_install(&tinyusb_config);

if (ret == ESP_ERR_INVALID_ARG || ret == ESP_FAIL) {
return ret;
}

tinyusb_cdcacm_itf_t* cdc_port = (tinyusb_cdcacm_itf_t*)transport->args;

tinyusb_config_cdcacm_t acm_cfg = {
.usb_dev = TINYUSB_USBDEV_0,
.cdc_port = *cdc_port,
.rx_unread_buf_sz = CONFIG_TINYUSB_CDC_RX_BUFSIZE,
.callback_rx = NULL,
.callback_rx_wanted_char = NULL,
.callback_line_state_changed = NULL,
.callback_line_coding_changed = NULL
};

if (tusb_cdc_acm_init(&acm_cfg) != ESP_OK) {
return false;
}

return true;
}

// Close USB-CDC
bool esp32s2_usbcdc_close(struct uxrCustomTransport* transport) {
tinyusb_cdcacm_itf_t* cdc_port = (tinyusb_cdcacm_itf_t*)transport->args;
return (tusb_cdc_acm_deinit(*cdc_port) == ESP_OK) ? true : false;
}

// Write to USB-CDC
size_t esp32s2_usbcdc_write(struct uxrCustomTransport* transport, const uint8_t* buf, size_t len, uint8_t* err) {
tinyusb_cdcacm_itf_t* cdc_port = (tinyusb_cdcacm_itf_t*)transport->args;
size_t tx_size = tinyusb_cdcacm_write_queue(*cdc_port, buf, len);
tinyusb_cdcacm_write_flush(*cdc_port, 0);
return tx_size;
}

// Read from USB-CDC
size_t esp32s2_usbcdc_read(struct uxrCustomTransport* transport, uint8_t* buf, size_t len, int timeout, uint8_t* err) {
tinyusb_cdcacm_itf_t* cdc_port = (tinyusb_cdcacm_itf_t*)transport->args;
size_t rx_size = 0;
esp_err_t ret = tinyusb_cdcacm_read(*cdc_port, buf, len, &rx_size);
return (ret == ESP_OK) ? rx_size : 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#ifndef ESP32S2_USBCDC_TRANSPORT_H
#define ESP32S2_USBCDC_TRANSPORT_H

#include <uxr/client/transport.h>

#include "tinyusb.h"
#include "tusb_cdc_acm.h"

#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)

#ifdef __cplusplus
extern "C"
{
#endif

bool esp32s2_usbcdc_open(struct uxrCustomTransport* transport);
bool esp32s2_usbcdc_close(struct uxrCustomTransport* transport);
size_t esp32s2_usbcdc_write(struct uxrCustomTransport* transport, const uint8_t* buf, size_t len, uint8_t* err);
size_t esp32s2_usbcdc_read(struct uxrCustomTransport* transport, uint8_t* buf, size_t len, int timeout, uint8_t* err);

#ifdef __cplusplus
}
#endif

#else
#error "This transport is only supported on ESP32-S2 or ESP32-S3 targets"
#endif // CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3

#endif // ESP32S2_USBCDC_TRANSPORT_H
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## IDF Component Manager Manifest File
dependencies:
espressif/esp_tinyusb: "^1.4.4"
## Required IDF version
idf:
version: ">=5.0"
21 changes: 21 additions & 0 deletions examples/int32_publisher_custom_transport_usbcdc/dependencies.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
dependencies:
espressif/esp_tinyusb:
component_hash: f151d680d6847bfcfd5d8eb6d1c3ff926c208e6b963b2e83643a141bc70baa15
source:
service_url: https://api.components.espressif.com/
type: service
version: 1.4.4
espressif/tinyusb:
component_hash: 214989d502fc168241a4a4f83b097d8ac44a93cd6f1787b4ac10069a8b3bebd3
source:
service_url: https://api.components.espressif.com/
type: service
version: 0.15.0~10
idf:
component_hash: null
source:
type: idf
version: 5.1.2
manifest_hash: 7f60fd8da9b1e2b73aafbc324107a75ce3928aa50666473c15511b764484240c
target: esp32s2
version: 1.0.0
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
idf_component_register(SRCS main.c INCLUDE_DIRS ".")
Loading
Loading