Hello,
I’m using CoppeliaSim for robot simulation and LuaMQTT to connect the robot to a local Mosquitto MQTT broker. The issue is that the MQTT connection is blocking the simulation, preventing the robot from moving based on MQTT payloads received in topics.
I’m trying to use async code to handle the MQTT connection without blocking the simulation, but it’s not working as expected. When the MQTT connection is established, the simulation gets blocked.
I’ve used coroutines in CoppeliaSim and LuaMQTT’s callback functions, but the MQTT connection still seems to block the simulation.
Has anyone managed to make this work asynchronously in CoppeliaSim? Any help would be appreciated!
PS: Just to be clear, the broker works, and both publish and subscribe functions work. However, when the MQTT connection is established, the simulation crashes.
Useful links:
The link to my Lua script: https://www.blackbox.ai/share/eb9b70e1- ... 10981d41ae
The LuaMQTT library I am using: https://github.com/xHasKx/luamqtt
Thank you in advance for your help!
MQTT Connection Blocking CoppeliaSim Simulation – Need Async Solution
Re: MQTT Connection Blocking CoppeliaSim Simulation – Need Async Solution
Hello,
CoppeliaSim's main loop uses a single thread that serves all scrips. If that thread gets blocked, then the whole simulator is blocked.
Now of course, if a script uses a library that launches its own thread, then CoppeliaSim won't block when the other thread blocks. But that other thread is not allowed to access CoppeliaSim API functions.
Not sure how LuaMQTT works in details. But in your code, you create a coroutine, but only resume the coroutine once, during initialization. You need to resume the coroutine at least once per simulation step, e.g. in the sensing section, e.g.:
Cheers
CoppeliaSim's main loop uses a single thread that serves all scrips. If that thread gets blocked, then the whole simulator is blocked.
Now of course, if a script uses a library that launches its own thread, then CoppeliaSim won't block when the other thread blocks. But that other thread is not allowed to access CoppeliaSim API functions.
Not sure how LuaMQTT works in details. But in your code, you create a coroutine, but only resume the coroutine once, during initialization. You need to resume the coroutine at least once per simulation step, e.g. in the sensing section, e.g.:
Code: Select all
function sysCall_init()
corout = coroutine.create(coroutineMain)
end
function sysCall_sensing()
if coroutine.status(corout) ~= 'dead' then
local ok, errorMsg = coroutine.resume(corout)
if errorMsg then
error(debug.traceback(corout, errorMsg), 2)
end
end
end
function coroutineMain()
...
end
Re: MQTT Connection Blocking CoppeliaSim Simulation – Need Async Solution
Thank you very much for your help! your solution worked perfectly.
For anyone who might find it useful, here is the working code, it connects CoppeliaSim to a local MQTT broker in a non-blocking way :
For anyone who might find it useful, here is the working code, it connects CoppeliaSim to a local MQTT broker in a non-blocking way :
Code: Select all
function sysCall_init()
sim = require('sim')
-- Initialize MQTT
package.path = package.path .. ";C:/Program Files/CoppeliaRobotics/CoppeliaSimEdu/lua/mqtt/?.lua"
print("Package path updated:", package.path)
local mqtt = require("mqtt")
local ioloop = require("mqtt.ioloop").get() -- Get the default ioloop instance
if not mqtt then
error("Failed to load MQTT library. Check the path and ensure the library exists.")
end
-- Create MQTT client
client = mqtt.client{
uri = "127.0.0.1:1883",
id = "robot_client",
clean = true
}
if not client then
error("Failed to create MQTT client.")
end
print("Created MQTT client:", client)
-- Set up the MQTT client on events
client:on{
connect = function(connack)
if connack.rc ~= 0 then
print("Connection to broker failed:", connack:reason_string(), connack)
return
end
print("Connected to broker:", connack)
-- Subscribe to the topic after connection
assert(client:subscribe{
topic = "robot/commands",
qos = 2,
callback = function(suback)
print("Subscribed to topic:", suback)
end
})
end,
message = function(msg)
assert(client:acknowledge(msg))
print("Received message:", msg.topic, msg.payload)
end,
error = function(err)
print("MQTT client error:", err)
end,
close = function()
print("MQTT connection closed")
end
}
-- Attach client to the ioloop
local success, err = pcall(function()
ioloop:add(client)
end)
if not success then
print("Failed to add client to ioloop:", err)
return
end
print("Client added to ioloop")
-- Create the coroutine for the MQTT loop
mqttCoroutine = coroutine.create(function()
while true do
local success, err = pcall(function()
ioloop:iteration(0.1) -- Small timeout to prevent excessive CPU usage
end)
if not success then
print("Error in ioloop iteration:", err)
end
coroutine.yield() -- Yield control back to CoppeliaSim
end
end)
end
-- Resume the coroutine in the sensing function
function sysCall_sensing()
if coroutine.status(mqttCoroutine) ~= 'dead' then
local ok, errorMsg = coroutine.resume(mqttCoroutine)
if errorMsg then
error(debug.traceback(mqttCoroutine, errorMsg), 2)
end
end
end
Re: MQTT Connection Blocking CoppeliaSim Simulation – Need Async Solution
CoppeliaSim can handle the creation of the (default) coroutine for you, simply by having the sysCall_thread() function in place, e.g. your code would become:
Cheers
Code: Select all
function sysCall_init()
sim = require('sim')
-- Initialize MQTT
package.path = package.path .. ";C:/Program Files/CoppeliaRobotics/CoppeliaSimEdu/lua/mqtt/?.lua"
print("Package path updated:", package.path)
local mqtt = require("mqtt")
local ioloop = require("mqtt.ioloop").get() -- Get the default ioloop instance
if not mqtt then
error("Failed to load MQTT library. Check the path and ensure the library exists.")
end
-- Create MQTT client
client = mqtt.client{
uri = "127.0.0.1:1883",
id = "robot_client",
clean = true
}
if not client then
error("Failed to create MQTT client.")
end
print("Created MQTT client:", client)
-- Set up the MQTT client on events
client:on{
connect = function(connack)
if connack.rc ~= 0 then
print("Connection to broker failed:", connack:reason_string(), connack)
return
end
print("Connected to broker:", connack)
-- Subscribe to the topic after connection
assert(client:subscribe{
topic = "robot/commands",
qos = 2,
callback = function(suback)
print("Subscribed to topic:", suback)
end
})
end,
message = function(msg)
assert(client:acknowledge(msg))
print("Received message:", msg.topic, msg.payload)
end,
error = function(err)
print("MQTT client error:", err)
end,
close = function()
print("MQTT connection closed")
end
}
-- Attach client to the ioloop
local success, err = pcall(function()
ioloop:add(client)
end)
if not success then
print("Failed to add client to ioloop:", err)
return
end
print("Client added to ioloop")
end
function sysCall_thread()
while true do
local success, err = pcall(function()
ioloop:iteration(0.1) -- Small timeout to prevent excessive CPU usage
end)
if not success then
print("Error in ioloop iteration:", err)
end
sim.yield() -- Yield control back to CoppeliaSim
end
end