Skip to content
Open
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
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)

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

# 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 in DFU mode

> [!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
```

### Build and Flash in JTAG/serial mode

> [!NOTE]
> Some of the esp32-S3/S2 chips won't go into dfu mode but instead will be on flashing mode using JTAG/Serial. This can be checked after pulling GPIO0 down (e.g., pressing the BOOT button), pulling RESET down for a moment, and releasing GPIO0 then running:

```bash
lsusb
```
Then get this output:

```bash
ID 303a:1001 Espressif USB JTAG/serial debug unit
```

#### Build the project

```bash
idf.py build
```

#### Flash the project

Replace /dev/ttyACM0 with your actual serial port if needed:
```bash
idf.py -p /dev/ttyACM0 flash
```

### Run micro-ROS Agent

```bash
ros2 run micro_ros_agent micro_ros_agent serial --dev /dev/ttyACM0
```

Using Docker

```bash
docker run -it --rm -v /dev:/dev -v /dev/shm:/dev/shm --privileged --net=host microros/micro-ros-agent:humble serial --dev /dev/ttyACM0 -v6
```

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
ros2 topic list
```

Output expected:

```bash
/freertos_int32_publisher
/parameter_events
/rosout
```

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

```bash
ros2 topic echo /freertos_int32_publisher
```

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"
Loading