micro-ROS / micro-ROS-Agent

ROS 2 package using Micro XRCE-DDS Agent.
Apache License 2.0
101 stars 58 forks source link

No response to UDP connection request #211

Closed luukWeFab closed 11 months ago

luukWeFab commented 11 months ago

Issue template

Steps to reproduce the issue

  1. Have a serial connection established using the custom transport layer specified in the micro_ros_stm32cubemx_utils repository.
  2. Change the custom transport layer to work through a w5500 chip. (Using the ioLibrary_Driver library)
    
    #ifndef __W5500_TRANSPORT_LAYER_HPP
    #define __W5500_TRANSPORT_LAYER_HPP

include <uxr/client/transport.h>

include "socket.h"

include "dhcp.h"

include "dns.h"

include "spi.h"

include "rcl_error_handler.hpp"

include "pinout.hpp"

include "cmsis_os.h"

define DHCP_SOCKET 0

define DNS_SOCKET 1

define HTTP_SOCKET 2

define MICRO_ROS_SOCKET 3

/**

extern uint8_t rx_buffer[cnst::RX_SOCKET_BUFFER]; extern uint32_t curr_head; extern uint32_t curr_tail; extern volatile uint32_t curr_length;

template struct dma_tx_buffer_t { uint8_t buffer[Size]; bool transmission_started; };

/**

/**

/**

/**

/**

/**

/**

/**

bool w5500_transport_open(struct uxrCustomTransport transport); bool w5500_transport_close(struct uxrCustomTransport transport); size_t w5500_transport_write(struct uxrCustomTransport transport, const uint8_t buf, size_t len, uint8_t err); size_t w5500_transport_read(struct uxrCustomTransport transport, uint8_t buf, size_t len, int timeout, uint8_t err);

endif //__W5500_TRANSPORT_LAYER_HPP

include "w5500_transport_layer.hpp"

uint8_t microROS_agent_socket = MICRO_ROS_SOCKET;

// #define TCP

define UDP

void W5500_Select(void) { pins::ETH_SCS.clear(); }

void W5500_Unselect(void) { pins::ETH_SCS.set(); }

void W5500_ReadBuff(uint8_t* buff, uint16_t len) { HAL_SPI_Receive(pins::w5500_spi, buff, len, HAL_MAX_DELAY); }

void W5500_WriteBuff(uint8_t* buff, uint16_t len) { HAL_SPI_Transmit(pins::w5500_spi, buff, len, HAL_MAX_DELAY); }

uint8_t W5500_ReadByte(void) { uint8_t byte; W5500_ReadBuff(&byte, sizeof(byte)); return byte; }

void W5500_WriteByte(uint8_t byte) { W5500_WriteBuff(&byte, sizeof(byte)); }

void init_w5500() { print_debug_uart("Initializing w5500", "");

pins::ETH_RST.clear();
vTaskDelay(pdMS_TO_TICKS(1));
pins::ETH_RST.set();
vTaskDelay(pdMS_TO_TICKS(10));
reg_wizchip_cs_cbfunc(W5500_Select, W5500_Unselect);
reg_wizchip_spi_cbfunc(W5500_ReadByte, W5500_WriteByte);
reg_wizchip_spiburst_cbfunc(W5500_ReadBuff, W5500_WriteBuff);

uint8_t rx_tx_buff_sizes[] = {2, 2, 2, 2, 2, 2, 2, 2};
wizchip_init(rx_tx_buff_sizes, rx_tx_buff_sizes);

wizchip_sw_reset();

print_uint("Chip version", getVERSIONR());

wiz_NetInfo_t network_config = {
    .mac = {(uint8_t) (HAL_GetUIDw0()),
                         (uint8_t) (HAL_GetUIDw0() >> 8), 
                         (uint8_t) (HAL_GetUIDw0() >> 16),
                         (uint8_t) (HAL_GetUIDw0() >> 24),
                         (uint8_t) (HAL_GetUIDw1()),
                         (uint8_t) (HAL_GetUIDw1() >> 8)
                         },
    .sn = {255, 255, 255, 0  },
    .dhcp = NETINFO_STATIC
};

memcpy(network_config.ip, cnst::IP_ADDRESS, sizeof(cnst::IP_ADDRESS));

wiz_NetInfo_t test_config;

do {
    print_debug_uart("Setting mac", "");
    setSHAR(network_config.mac);
    wizchip_getnetinfo(&test_config);
} while(memcmp(network_config.mac, test_config.mac ,6));

do {
    print_debug_uart("Setting ip", "");
    wizchip_setnetinfo(&network_config);
    wizchip_getnetinfo(&test_config);
} while(memcmp(network_config.ip, test_config.ip, 4));

char address[100] = "{ ";
for(uint8_t i = 0; i < 6; i++) {
    char hex[10];
    sprintf(hex, "0x%x,", test_config.mac[i]);
    strcat(address, hex);
}
strcat(address, " }");
print_debug_uart("Mac address", address);

memset(address, 0, sizeof(address));
strcat(address, "{ ");
for(uint8_t i = 0; i < 4; i++) {
    char hex[10];
    sprintf(hex, "%3u,", test_config.ip[i]);
    strcat(address, hex);
}
strcat(address, " }");
print_debug_uart("IP address", address);

}

bool w5500_transport_open(struct uxrCustomTransport * transport) {

ifdef UDP

    int8_t rc = socket(microROS_agent_socket, Sn_MR_UDP, cnst::PORT, SF_IO_NONBLOCK);
    if(rc != microROS_agent_socket) {
        print_debug_uart("Error", "Couldn't create socket");
        return false;
    }
#endif

#ifdef TCP
    int8_t rc = socket(microROS_agent_socket, Sn_MR_TCP, cnst::PORT, SF_IO_NONBLOCK);
    if(rc != microROS_agent_socket) {
        print_debug_uart("Error", "Couldn't create socket");
        return false;
    }
    char address[100] = "{ ";
    for(uint8_t i = 0; i < 4; i++) {
        char hex[10];
        sprintf(hex, "%3u,", cnst::DEST_IP_ADDRESS[i]);
        strcat(address, hex);
    }
    strcat(address, " }");
    print_debug_uart("Connecting to ip", address);
    print_uint("Connecting to port", cnst::PORT);
    rc = connect(microROS_agent_socket, (uint8_t *) cnst::DEST_IP_ADDRESS, cnst::PORT);
    if(rc != SOCK_OK) {
        print_socket_error(rc);
        close(microROS_agent_socket);
        return false;
    }
    print_debug_uart("Socket", "connected");
#endif
return true;

}

bool w5500_transport_close(struct uxrCustomTransport * transport) { close(microROS_agent_socket); print_debug_uart("Socket", "closed"); return true; }

size_t w5500_transport_write(struct uxrCustomTransport transport, const uint8_t buf, size_t len, uint8_t err) { // TODO: remove this later static uint32_t write_counter = 0; write_counter++; print_uint("Write counter", write_counter); // END TODO print_debug_uart("Socket", "Trying to send data"); print_uint("Data length", len); print_array("Write array", (uint8_t ) buf, len, true);

ifdef UDP

    int32_t result = sendto(microROS_agent_socket, (uint8_t *) buf, len, (uint8_t *) cnst::DEST_IP_ADDRESS, cnst::PORT);
#endif
#ifdef TCP
    // int32_t result = sendto(microROS_agent_socket, (uint8_t *) buf, len, (uint8_t *) cnst::DEST_IP_ADDRESS, cnst::PORT);
    int32_t result = send(microROS_agent_socket, (uint8_t *) buf, len);
#endif
if((result >= 0) && ((uint32_t) result) == len) {
    print_debug_uart("Socket", "send succesfull");
    return len;
} 
print_socket_error(result);
return 0;

}

size_t w5500_transport_read(struct uxrCustomTransport transport, uint8_t buf, size_t len, int timeout, uint8_t * err) { // print_debug_uart("Socket", "Trying to receive data"); // print_uint("Data length", len);

ifdef UDP

    int32_t result = recvfrom(microROS_agent_socket, buf, len, (uint8_t *) cnst::DEST_IP_ADDRESS, (uint16_t *) &cnst::PORT);
#endif
#ifdef TCP
    // int32_t result = recvfrom(microROS_agent_socket, buf, len, (uint8_t *) cnst::DEST_IP_ADDRESS, (uint16_t *) &cnst::PORT);
    int32_t result = recv(microROS_agent_socket, buf, len);
#endif
if((result >= 0) && ((uint32_t) result) == len) {
    print_debug_uart("Socket", "receive sucessfull");
    return len;
}
// print_socket_error(result);
return 0;

}

void print_socket_error(int8_t socket_error_code) { switch(socket_error_code) { case SOCK_OK: print_debug_uart("Socket ok", ""); break; case SOCK_BUSY: print_debug_uart("Socket error", "Port busy"); break; case SOCKERR_SOCKNUM: print_debug_uart("Socker error", "Socket num invalid"); break; case SOCKERR_SOCKOPT: print_debug_uart("Socket error", "Invalid socket option"); break; case SOCKERR_SOCKINIT: print_debug_uart("Socket error", "Socket init error"); break; case SOCKERR_SOCKCLOSED: print_debug_uart("Socket error", "Socket was closed"); break; case SOCKERR_SOCKMODE: print_debug_uart("Socket error", "Invalid socket mode"); break; case SOCKERR_SOCKFLAG: print_debug_uart("Socket error", "Invalid socket flag"); break; case SOCKERR_SOCKSTATUS: print_debug_uart("Socket error", "Socket status"); break; case SOCKERR_ARG: print_debug_uart("Socket error", "Invalid socket arguemnt"); break; case SOCKERR_PORTZERO: print_debug_uart("Socket error", "Port zero"); break; case SOCKERR_IPINVALID: print_debug_uart("Socket error", "IP invalid"); break; case SOCKERR_TIMEOUT: print_debug_uart("Socket error", "Timout connecting to socket"); break; case SOCKERR_DATALEN: print_debug_uart("Socket error", "Data length is incorrect"); break; case SOCKERR_BUFFER: print_debug_uart("Socket error", "Buffer size is not enough"); break; default: print_debug_uart("Socket error", "Error code not supported"); break; } }



3. Start the micro-ros-agent with udp4, on port 8888.

#### Expected behavior
microROS agent logging the recv_message, and responding with a send_message that it connects.

#### Actual behavior
The microROS agent logs the recv_message, however it doesn't respond to it. 
![image](https://github.com/micro-ROS/micro-ROS-Agent/assets/143802674/27176288-1b91-4fd1-858d-1b7f75192a07)

#### Additional information
- I checked that what the microROS client tries to send through the w5500_transport_write function is the same as what the microROS agent is printing, so the flow from microROS client to microROS agent should be correct.
- I compared what the microROS client tries to send through the w5500_transport_write function, and the original custom_transport_write function, and both seem similar.
Serial (uart) communication:
![image](https://github.com/micro-ROS/micro-ROS-Agent/assets/143802674/ba17a6fc-6f8d-48ca-99d8-c2e02382789c)
SPI (w5500) communication:
![image](https://github.com/micro-ROS/micro-ROS-Agent/assets/143802674/73342485-3978-4aa6-b9bc-3fceadb8d7d0)
Received by microROS agent over UDP:
![image](https://github.com/micro-ROS/micro-ROS-Agent/assets/143802674/ea0185ca-d05a-4476-a804-324cd1092f5b)

- I tried to log what the microROS agent is receiving from the serial communication with tag -v6, however it is not logging its reception.

Basically my question is why is my client not connecting to the microROS agent over udp, but is through serial while the request seems to be similar?
pablogs9 commented 11 months ago

Serial and Networked transport are not handled in the same way by the library, check serial framing here: https://micro-xrce-dds.docs.eprosima.com/en/latest/transport.html#stream-framing-protocol

Are you taking into account that framing protocol shall be disabled when using UDP by means of this flag: https://github.com/micro-ROS/micro_ros_stm32cubemx_utils/blob/3819f6819cee1ff01c3da58ecbc8bfcbf4b0a2a5/sample_main.c#L382

luukWeFab commented 11 months ago

I did not accounting that the framing protocol needed to be disabled thank you. Changing image to image indeed made the udp agent respond to the request. Thank you.

A second quick question I have is whether TCP is supported or not? Because that would then also work without framing enabled right? I ask this because I read in the main readme of this repository that it is, however in the readme of the micro_ros_agent sub folder it is stated that only UDP, and serial are supported.

And thank you again for the help!

pablogs9 commented 11 months ago

Nice to hear that @luukWeFab, I'm closing as solved.

TCP is supported at XRCE-DDS level but it not very well integrated in micro-ROS.

Take a look here to a sample implementation: https://github.com/eProsima/Micro-XRCE-DDS-Client/blob/master/src/c/profile/transport/ip/tcp/tcp_transport_posix.c

As you can see, there is another "framing" for TCP, it is handled at XRCE Client level here: https://github.com/eProsima/Micro-XRCE-DDS-Client/blob/97175304425c5bee87c6fddd99de1ef8d0c394dc/src/c/profile/transport/ip/tcp/tcp_transport.c#L33