I am using uavcan to communicate between an IMX6 and stm32f4. stm32f4 has a configuration server which updates some parameters locally when a request is made from the IMX6::ros node. Here is the code from the IMX6 side of things:
void motorController::configCallback(const robotics_msgs::Config& conf) {
if(conf.enableControl)
{
uavcan::control::config::Config::Request req;
req.enableControl = true;
req.encoderTicksPerRev = params.maxEncoderTicks;
req.maxRpm = params.maxrpmMotor;
req.gearRatio = params.gearRatio;
const int res = configClient.call(this->nParams.config_server_ID, req);
if(res < 0)
{
ROS_INFO("config Client call failed\n");
}
else
ROS_INFO("config call successful\n");
}
}
on the stm32 side:
const int config_res = configServer.start([&](const uavcan::ReceivedDataStructure<uavcan::control::config::Config::Request>& req,
uavcan::control::config::Config::Response& resp)
{
if(req.enableControl)
{
if(!paramsInited)
{
staticParams.maxRPM = req.maxRpm;
staticParams.gearRatio = req.gearRatio;
staticParams.maxEncoderTicks = req.encoderTicksPerRev;
paramsInited = true;
resp.configApplied = true;
}
if(!driverInited)
{
driver.init(staticParams.maxRPM,staticParams.gearRatio);
driverInited=true;
resp.configApplied = true;
}
}
else
{
resp.configApplied = false;
}
});
if(config_res < 0)
{
sys::debug(Debug_Level::Error,"Failed to start config Server\r\n");
uavcan_thd.stop();
}
after a successful service call. The boolean fields are properly initialized but the other fields are ‘0’. I have checked it with other service calls as well by making the stm32f4 a client and IMX6 a server. Below is the uavcan message definition
bool enableControl
uavcan.control.RpmPID Pid
uint32 encoderTicksPerRev
uint16 encoderPubHz
uint16 controlLoopHz
uint8 maxRpm
uint8 gearRatio
uint8 accLimit
uint8 MODE_OPENLOOP = 0
uint8 MODE_CLOSEDLOOP = 1
uint8 mode
---
bool configApplied
Any suggestions on what might be wrong?
My understanding is that the boolean field you are referring to is enableControl
? I see no other boolean fields in the request schema.
You seem to be using the free store (heap) in your STM32 code indirectly through the lambda closure. Probably a bad idea unless you really understand how it works and what the trade-offs are. You also seem to be running a multi-threaded environment (judging by uavcan_thd.stop()
) which makes it a minefield unless the proper precautions have been taken – I assume they weren’t because you didn’t mention that.
I suggest making sure that the firmware is sound before proceeding further.
Hi Pavel,
Thank you for the thorough response.
In regards to the usage of free store in the code sample pasted above. I always thought of c++ lambda closures as stack-allocated objects and if i’m capturing something by reference “staticParams” here then it is simply a reference member variable of the lambda object. And the lambda lives as long as the scope in which it was declared i.e uavcan_thd::main()
. Since staticParams is a member variable of the class uavcan_thd
it has a longer lifetime than that of the closure. I’ve cross-checked and the closure seems to be static allocated on the uavcan_thd stack. Regarding multi-threading, currently i have disabled all other threads except the uavcan node and the chibios idle thread.
reference : https://stackoverflow.com/questions/12202656/c11-lambda-implementation-and-memory-model
This is the output when i query the “uavcan_data_type”::Request object in gdb:
(const uavcan::ReceivedDataStructure<uavcan::control::config::Config_::Request_<0> > &) @0x2000c9b0: {<uavcan::control::config::Config_::Request_<0>> = {static MODE_OPENLOOP = <optimized out>,
static MODE_CLOSEDLOOP = <optimized out>, enableControl = true, Pid = {kp = 0, ki = 0, kd = 0, ilimit = 0}, encoderTicksPerRev = 0, encoderPubHz = 0, controlLoopHz = 0, maxRpm = 0 '\000', gearRatio = 0 '\000', accLimit = 0 '\000',
mode = 0 '\000'}, <uavcan::Noncopyable> = {<No data fields>}, _transfer_ = 0x2000ca08 <can::imuNode+6024>}
only the boolean variable has been initialized to its intended value
Re lambdas: a heap allocation may occur when your closure is passed into start(std::function<...>)
of the server. At least GCC v7 with C++14 seems to be doing that at least occasionally; the standard does not prohibit that. I would recommend to avoid closures completely unless you can tolerate dynamic storage allocation.
I don’t know why your object is not being assigned correctly, sorry. The available context is insufficient and I don’t think it is related to Libuavcan anyway.
Thank you @pavel.kirienko. I will remove all the lambda’s and check if the problem persists.