Hi all,
I hope someone here can help me, been looking a lot through the documentation and I can't seem to figure out what I'm missing.
I have been trying to get RTI Connext DDS Micro (2.4.14) to work on a microcontroller, the Nucleo-H745ZI-Q.
The furthest I have now gotton is that i was able to create a data reader that is visible in the RTI Administration Console (see attachment). But as you can see there is no IP address or locators data, which makes it that the microcontroller can't receive packages.
For the initial code i used the setup of the RTI Connext DDS Micro user manual. See below the code of the UDP:
// Unregister the pre-registered UDP component
if (!RT_Registry_unregister(registry, "_udp", NULL, NULL)) {
//printf("Unregistration of pre-registered UDP component failed\n");
goto done;
}
// configure UDP transport properties
struct UDP_InterfaceFactoryProperty *udp_property = NULL;
udp_property = (struct UDP_InterfaceFactoryProperty*) malloc(
sizeof(struct UDP_InterfaceFactoryProperty));
if (udp_property != NULL) {
*udp_property = UDP_INTERFACE_FACTORY_PROPERTY_DEFAULT;
/* allow_interface: Names of network interfaces allowed to send/receive.
* Allow one loopback (lo) and one NIC (eth0).
*/
REDA_StringSeq_set_maximum(&udp_property->allow_interface, 2);
REDA_StringSeq_set_length(&udp_property->allow_interface, 2);
*REDA_StringSeq_get_reference(&udp_property->allow_interface, 0) = DDS_String_dup("lo");
*REDA_StringSeq_get_reference(&udp_property->allow_interface, 1) = DDS_String_dup("eth0");
} else {
//printf("failed to allocate udp properties\n");
goto done;
}
udp_property->disable_auto_interface_config = RTI_TRUE;
// allow interface
*DDS_StringSeq_get_reference(&udp_property->allow_interface,0) =
DDS_String_dup("eth0");// re-register UDP component with updated properties
if (!RT_Registry_register(registry, NETIO_DEFAULT_UDP_NAME,
UDP_InterfaceFactory_get_interface(),
(struct RT_ComponentFactoryProperty*) udp_property, NULL)) {
//printf("failed to register udp\n");
goto done;
}
One of the things i tried was adding an interface table to the udp with the following code:
// add local network interface
if (!UDP_InterfaceTable_add_entry(&udp_property->if_table, <hex ip address>,
<hex mask>, "eth0",
UDP_INTERFACE_INTERFACE_UP_FLAG |
UDP_INTERFACE_INTERFACE_MULTICAST_FLAG)) {
/* Error */
goto done;
}
This got me a bit further, but now it seems to get stuck on the Participant part where the last function returns NULL:
// create participant
DDS_DomainParticipant *participant = NULL;
// create QoS
struct DDS_DomainParticipantQos dp_qos =
DDS_DomainParticipantQos_INITIALIZER;
// DDS domain of DomainParticipant
DDS_Long domain_id = 0;
// Name of your registered Discovery component
if (!RT_ComponentFactoryId_set_name(&dp_qos.discovery.discovery.name, "dpde")) {
// printf("failed to set discovery plugin name\n");
goto done;
}
/* Initial peers: use only default multicast peer */
DDS_StringSeq_set_maximum(&dp_qos.discovery.initial_peers, 1);
DDS_StringSeq_set_length(&dp_qos.discovery.initial_peers, 1);
*DDS_StringSeq_get_reference(&dp_qos.discovery.initial_peers, 0) =
DDS_String_dup("_udp://239.255.0.1");
/* Resource limits */
dp_qos.resource_limits.max_destination_ports = 8;
dp_qos.resource_limits.max_receive_ports = 8;
dp_qos.resource_limits.local_topic_allocation = 4;
dp_qos.resource_limits.local_type_allocation = 4;
dp_qos.resource_limits.local_reader_allocation = 4;
dp_qos.resource_limits.local_writer_allocation = 4;
dp_qos.resource_limits.remote_participant_allocation = 4;
dp_qos.resource_limits.remote_reader_allocation = 4;
dp_qos.resource_limits.remote_writer_allocation = 4;
dp_qos.discovery.accept_unknown_peers = DDS_BOOLEAN_TRUE;
/* Participant name */
strcpy(dp_qos.participant_name.name, "STM32H745");
participant = DDS_DomainParticipantFactory_create_participant(factory,
domain_id, &dp_qos,
NULL,
DDS_STATUS_MASK_NONE);
if (participant == NULL) {
goto done2;
}
I can't seem to figure out what the problem is and why the participant part is returning NULL. Hope someone here has an idea what could fix my problem :)
Kind regards,
Toon
Attachment | Size |
---|---|
![]() | 38.4 KB |
what i noticed just now is that the moment i try to create the participant that it sets the "Immutable QoS Enabled" bit.
the manual says this:
"The values of some QosPolicies cannot be changed after the Entity is created or after the Entity is enabled. Others may be changed at any time. The detailed section on each QosPolicy states when each policy can be changed. If you attempt to change a QosPolicy after it becomes immutable (because the associated Entity has been created or enabled, depending on the policy), set_qos() will fail with a return code of IMMUTABLE_POLICY."
But i don't fully understand why the bit is set in my code.
Sorry don't know what you mean by "Immutable QoS Enabled bit". What the manual states is correct. Some QoS settings can be changed after the DDS Entity (e.g., DomainParticipant) is created and other cannot. Those cannot are "immutable".
In your code you have
REDA_StringSeq_set_maximum(&udp_property->allow_interface, 2);
REDA_StringSeq_set_length(&udp_property->allow_interface, 2);
*REDA_StringSeq_get_reference(&udp_property->allow_interface, 0) = DDS_String_dup("lo");
*REDA_StringSeq_get_reference(&udp_property->allow_interface, 1) = DDS_String_dup("eth0");
} else {
//printf("failed to allocate udp properties\n");
goto done;
}
udp_property->disable_auto_interface_config = RTI_TRUE;
// allow interface
*DDS_StringSeq_get_reference(&udp_property->allow_interface,0) =
DDS_String_dup("eth0");// re-register UDP component with updated properties
I'm not sure why you have disabled the automatic interface configuration of the UDP transport plugin. If you do that, then you have to configure the interfaces used by the transport manually.
allow_interfaces doesn't confgure the transport to use an interface. It just tells the UDP transport to allow the use of specified interfaces if those interfaces were configured in the transport.
The code snippet that you sent sets
REDA_StringSeq_set_maximum(&udp_property->allow_interface, 2);
REDA_StringSeq_set_length(&udp_property->allow_interface, 2);
*REDA_StringSeq_get_reference(&udp_property->allow_interface, 0) = DDS_String_dup("lo");
*REDA_StringSeq_get_reference(&udp_property->allow_interface, 1) = DDS_String_dup("eth0");
*DDS_StringSeq_get_reference(&udp_property->allow_interface,0) = DDS_String_dup("eth0");
which fundamentally sets the sequence of "allow_interface" to have 2 values, each of which is "eth0".
I suggest
1) delete the line
udp_property->disable_auto_interface_config = RTI_TRUE;
2) make sure that your OS actually has an interface named "eth0" as the interface that you want Connext Micro to use to talk on the network, if not, then you need to use the name of the interface that the OS uses. You should delete this line as well
*DDS_StringSeq_get_reference(&udp_property->allow_interface,0) = DDS_String_dup("eth0");
In addition the code that you posted didn't show that you registered the discovery plugin?
Something like:
if (!RT_Registry_register(registry,
"dpde",
DPDE_DiscoveryFactory_get_interface(),
&discovery_plugin_properties._parent,
NULL))
{
printf("failed to register dpde\n");
goto done;
}
Finally, typically, you should not need to add entries into your interface table, the UDP plugin should automatically create the interface table, unless somehow your OS isn't allowing the UDP plugin to figure out what NICs are available OR you have disabled auto-configuration which you did. You didn't state which OS you're running, I'll assume some version of embedded Linux.
Thank you Howerd for your reply.
I wasn't home this weekend, so sorry for the late reply.
So to program the microcontroller i use the CubeIDE, in Cube i can debug my code by running it and going through it line by line and seeing all the variables, registers, etc... . This way I know what happens in the code. When I was doing this I saw a variable named "Immutable QoS Enabled" being set to 1 after the participant was created.
Ah this makes sence.
My apoligies, I did register the discovery plugin.
I'm using FreeRTOS
I followed the steps you said, but it Crashes on the participant creation.
So, in your original post, you wrote:
To get that far, you must have had code using Micro that could create a participant successfully. What was that code? If that code turned off the automatic interface configuration, change that to leave the configuration enabled and run again.
So that code indeed turned off the automatic interface configuration. like you previously stated i tried my code with the automatic interface configurations enabled, but then it craches on the participant creation.
In the end I started partly new. So i reorganized my code and added some extra stuff and now it seems to work. the locators are being send, connection is healthy and the ACK is being send.
But i now walked against a different problem.
When i look at the RTPS packages through wireshark i think they look beautiful, everything is filled correctly.
But when i look at the console log of the RTI administration console then i see a lot of warnings (like 2 or 3 per second).
in particular i see these:
- MIGInterpreter_parse:!RTPS
- MIGInterpreter_parseHeader:!version
- MIGInterpreter_parse:available space 24 < 967
Any idea what these could mean?
EDIT
I found out that the administration console spams a lot of messages when it finds a new participant. so i won't be using it for now because the microcontroller can't handle that many packages yet.
So i'm now testing with wireshark and a subscriber and publisher on windows.
So now the code seems to run normally. But strange things seems to happen when sending packages.
So what happens is that when i upload my code into the microcontroller it sometimes works. So the subscriber on my windows pc sees the microcontroller, communicates and then start receiving data.
But what also sometimes happens when i upload exactly the same code is that the packages seem to be corrupted. In Wireshark a lot of the data points like PID_PRODUCT_VERSION change into PID_PAD, or Wireshark says the package is malformed.
I have a feeling that this happens because of where the memory is allocated and that they are overlapping, but I'm not sure. According to the times the code does work there should be 14,6 KB left on the heap and there should be 152,7 KB left on the RAM so no shortage there i think.
this might be more of an embedded question then a DDS question.
Kind regards,
Toon
Hello,
What you describe might be related to a stack overflow problem.
Connext Micro uses the FreeRTOS timer task to trigger all the Connext Micro internal timers, etc. What is the stack size for the FreeRTOS timer task in your FreeRTOS configuration? We recommend to have at least 5Kbytes for the FreeRTOS timer task, but if possible use at least 8KBytes. You can configure this with macro configTIMER_TASK_STACK_DEPTH in file FreeRTOSConfig.h. Note that to calculate the actual number of bytes used for the task, you would need to multiply this constant by sizeof(StackType_t). I.e. so if you want an 8Kbyte stack for this task, the value of configTIMER_TASK_STACK_DEPTH should be (8*1024)/sizeof(StackType_t).
What is the stack size for your application thread(s) that creates all DDS entities, sends data, etc? We suggest that you should use a minimum of 8KBytes for that thread.
On the following link you will find some useful information about running Connext Micro on FreeRTOS + lwIP.
https://community.rti.com/static/documentation/connext-micro/2.4.14/doc/html/building/freertos.html#
Regards,
Francis.
Hi Francis,
Thank you for the response.
In FreeRTOSConfig.h TIMER_TASK_STACK_DEPTH, I took this configuration straight from the link that you also included. I replaced it for now with (8*1024)/sizeof(StackType_t) to make sure it has enough.
The stack size of my task is 6KB. I changed it to 8KB.
Also i had decreased the TOTAL_HEAP_SIZE to 300000 instead of 400000 because i noticed that it didn't use everything. But I put it back at 400000.
After these changes i tried it again and saw only malformed packages. And these malformed packages contain a lot of PID_PAD data and a few things that are actual data like the unicast locator, product version and the sentinel, the rest is PID_PAD..
This makes me think that something else is writing over the data before it gets send.
I can if it helps post my configuration file of LWIP and FreeRTOS.
About the link, something i find interesting that it sets a lot of stuff for the TCP, but doesn't it send UDP based?
Kind regards,
Toon
Hello Toon,
What other threads do you have in your system? Anything that might be causing data corruption?
I can see that you are using malloc(). How large is the heap for the libc? You might prefer to use pvPortMalloc() instead of malloc().
What are the system heap stats? You can use vPortGetHeapStats()
What are the stats for all threads in the system? I think you can use uxTaskGetSystemState() (see https://www.freertos.org/uxTaskGetSystemState.html). Specially important the stack high water mark.
Regards,
Francis.
Hi Francis,
I have 2 threads atm. One for DDS and the other one doesn't really do much except blink an LED so i know it is still running. The DDS thread has a higher priority. So as far as i know nothing causes corruption there.
Ah yes, i had forgotten that the malloc() function isn't really a thread safe option. I changed the malloc() to pvPortMalloc(), but this didn't change much.
Heap for the libc? Do you mean the total amount of heap available to FreeRTOS? Because TOTAL_HEAP_SIZE is 400000 bytes, at the beginning of the thread the heap is 393240 bytes, when the microcontroller is running in the loop where it sends it's samples it has 114192 bytes.
I used the vPortGetHeapStats() function. I got the following results at the beginning of the task:
xAvailableHeapSpaceInBytes: 393240
xSizeOfLargestFreeBlockInBytes: 393240
xSizeOfSmallestFreeBlockInBytes: 393240
xNumberOfFreeBlocks: 1
xMinimumEverFreeBytesRemaining: 393240
xNumberOfSuccessfulAllocations: 13
xNumberOfSuccessfulFrees: 0
I got the following results at the beginning of the for loop:
xAvailableHeapSpaceInBytes: 114192
xSizeOfLargestFreeBlockInBytes: 114192
xSizeOfSmallestFreeBlockInBytes: 114192
xNumberOfFreeBlocks: 1
xMinimumEverFreeBytesRemaining: 114192
xNumberOfSuccessfulAllocations: 2050
xNumberOfSuccessfulFrees: 5
I used the uxTaskGetSystemState() function. I never used the function so i hope i used it right. When i used it at the beginning of the task it returned 7, when the microcontroller is running in the loop where it sends it's samples it returned 9.
To make sure i do it right, below is the code:
uxArraySize = uxTaskGetNumberOfTasks();
pxTaskStatusArray = pvPortMalloc( uxArraySize * sizeof( TaskStatus_t ) )
; if( pxTaskStatusArray != NULL ){
taskstats = uxTaskGetSystemState(pxTaskStatusArray, uxArraySize, &ulTotalRunTime);
}
kind regards,
Toon
Hello Toon,
uxTaskGetSystemState() returns the number of TaskStatus_t structures that were populated by the function (as explained on https://www.freertos.org/uxTaskGetSystemState.html).
On that link there is also an example of how to use it. For each of the task please print at least the "pcTaskName" and "usStackHighWaterMark".
Francis.
Hi,
Did you get this working?
I am trying to implement Micro on STM32H745xx with FreeRTOS and LwIP, and I have the same problems with malformed messages.
In my case, it seems that the RTPS package changes while going through the LwIP stack, since the packet contents is different when comparing them from before LwIP and then looking at them in wireshark..
- Eric
Hi,
@ethron: No I haven't gotten it working yet. In the end I approached it in a different way.
I found out that the DMA of the ethernet driver of the STM32H7 boards isn't working properly. So that is where the malformed messages are comming from. I am planning to go try again since the driver has been fully rewritten, but i'm not sure when I will try again.
For now I have got a datawriter and datareader working on a ESP32 with wifi. It works pretty good with the standard settings so my next step is to play with the settings a bit.
Something else that I did was write my own DDS test software. Main reason was that I noticed was that the Admin Console didn't want to communicate properly with the STM32 at the time, the STM32 crashed the moment it saw the admin console and another reason was that the shapes demo for some reason goes into an infinite handshake I think with the ESP32 (heartbeat, heartbeat, acknack, acknack, etc).
So I wrote my own datawriter and datareader in C# so I have more control over what happens in the communication and such. Because of this I now have a ESP32 with a working datawriter and datareader. I still have to check but if i remember correctly it uses around 150KB RAM for only a datawriter.
Kind regards.