Machsupport Forum
Mach Discussion => Mach4 General Discussion => Topic started by: SebDE on March 15, 2018, 03:07:07 AM
-
Hello,
the last days I spent my time to implement a MQTT Client in Lua to work with Mach4. Actually I can connect to a MQTT Broker, publish Messages via functions und subscribe topics. But I'm not sure if I place all MQTT functions at the right place. I've made the main MQTT Connection in the Screen Load Script with the following code:
---------------------------------------------------------------
-- Load modules
....
--MQTT module
package.loaded.MqttModul = nil
mqtt = require "mqtt_library"
....
---------------------------------------------------------------
....
....
function mqttCallback(
topic, -- string
message) -- string
mc.mcCntlSetLastError(inst,"Topic: " .. topic .. ", message: '" .. message .. "'")
end
function SendMessage(msg)
mc.mcCntlSetLastError(inst, msg);
mqtt_client:publish("mach4",msg)
end
function MQTT_Connect()
mqtt_client = mqtt.client.create("192.168.0.55", nil, mqttCallback)
mqtt_client:connect("mach4")
mqtt_client:publish("mach4", "online")
mqtt_client:subscribe({"mach4/set"})
end
MQTT_Connect()
To maintain the MQTT Connection and to recieve messages it is important to call a handle function et least every few seconds. So I put this handler in the PLC Script:
local inst = mc.mcGetInstance()
local rc = 0;
testcount = testcount + 1
machState, rc = mc.mcCntlGetState(inst);
local inCycle = mc.mcCntlIsInCycle(inst);
mqtt_client:handler()
......
At the moment everything works fine but I have the following question: Is it a Problem that I call the mqtt_client:handler() in the PLC Script? Will I get problems like a delay if the handle function block for a moment? The code for the mqtt_client:handler() is:
function MQTT.client:handler() -- Public API
if (self.connected == false) then
error("MQTT.client:handler(): Not connected")
end
MQTT.Utility.debug("MQTT.client:handler()")
-- Transmit MQTT PING message
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~
-- MQTT 3.1 Specification: Section 3.13: PING request
--
-- bytes 1,2: Fixed message header, see MQTT.client:message_write()
local activity_timeout = self.last_activity + MQTT.client.KEEP_ALIVE_TIME
if (MQTT.Utility.get_time() > activity_timeout) then
MQTT.Utility.debug("MQTT.client:handler(): PINGREQ")
self:message_write(MQTT.message.TYPE_PINGREQ, nil)
end
-- Check for available client socket data
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local ready = MQTT.Utility.socket_ready(self.socket_client)
if (ready) then
local error_message, buffer =
MQTT.Utility.socket_receive(self.socket_client)
if (error_message ~= nil) then
self:destroy()
error_message = "socket_client:receive(): " .. error_message
MQTT.Utility.debug(error_message)
return(error_message)
end
if (buffer ~= nil and #buffer > 0) then
local index = 1
-- Parse individual messages (each must be at least 2 bytes long)
-- Decode "remaining length" (MQTT v3.1 specification pages 6 and 7)
while (index < #buffer) do
local message_type_flags = string.byte(buffer, index)
local multiplier = 1
local remaining_length = 0
repeat
index = index + 1
local digit = string.byte(buffer, index)
remaining_length = remaining_length + ((digit % 128) * multiplier)
multiplier = multiplier * 128
until digit < 128 -- check continuation bit
local message = string.sub(buffer, index + 1, index + remaining_length)
if (#message == remaining_length) then
self:parse_message(message_type_flags, remaining_length, message)
else
MQTT.Utility.debug(
"MQTT.client:handler(): Incorrect remaining length: " ..
remaining_length .. " ~= message length: " .. #message
)
end
index = index + remaining_length + 1
end
-- Check for any left over bytes, i.e. partial message received
if (index ~= (#buffer + 1)) then
local error_message =
"MQTT.client:handler(): Partial message received" ..
index .. " ~= " .. (#buffer + 1)
if (MQTT.ERROR_TERMINATE) then -- TODO: Refactor duplicate code
self:destroy()
error(error_message)
else
MQTT.Utility.debug(error_message)
end
end
end
end
return(nil)
end
The MQTT Client library is from https://github.com/geekscape/mqtt_lua (https://github.com/geekscape/mqtt_lua)
If someone is interested in the modified code and an installation instruction please let me know.
Thanks for help
-
Hi, Its not a problem. You PLC code part not long, there is not blocking network functions (just check MQTT.Utility.socket_receive for shure). PLC script executes, by default, at 50 ms intervals. There is no critical functionality for machine, just some UI background and support checks. If 50 ms is not enough for MQTT - then try increase it.
-
I check mqttlua/utility, socket_receive can be blocked by empty receive buffer, but socket creaed as socket.select({socket_client}, nil, 0.001), where 0.001 - 1ms is timeout value. So I assume there are no blocking processes exist.
-
Thanks for the answer - that are good news. So I will try to implement a Mach4 > MQTT > Arduino > Modbus Bridge to control my VFD via ethernet. Than i have a good practical test case to see if eversthing works fine.
-
Wow! Why so difficult path? I use direct ModbusTCP from Mach4 and all works fine.
-
??? - okay I just find these Plugin for Mach3 - can you give me a direction where I can find this? You're totally right that this way will be much easier ....
-
Hm, this is problem with documentation. I found all by my self with testing.
Plugin installed and activated by default. In configuration you setup exchanges - command, first register and count. As result you have virtual IO. Bits can be mapped to the signals, words is mapped to registers. From script bits can be accesed as local handler, rc = mc.mcIoGetHandle(inst, "PLC/"..name); local value, rc = mc.mcIoGetState(handler). Registres like local handler, rc = mc.mcRegGetHandle(inst, "PLC/"..name); local value, rc = mc.mcRegGetValue(self.h). All this functions described in manual, Get/Set supported.
Exchange speed pretty fast with some CPU load. Main limitation - Modbus serial not supported.
-
Hi,
are you sure, I've used RS485 to communicate with VFDs over Modbus both RTU and ASCII protocols
are supported.
Craig
-
Sorry, I miss something.
-
Hi,
sorry, my post wasn't clear.
Mach4's Modbus plugin can handle serial communication, certainly serial RTU and serial ASCII.
Craig
-
Yes. Thats why I say, I have missed this options :)
-
Hi @SebDE,
Is your offer of the modified code and the installation instructions still valid? I would really appreciate it if it was...
I have been messing about with MQTT and Node-Red recently - if I could use this network based/ loosely coupled system to control my cutting table - fantastic!
Thanks
Teak
-
Hi Seb
I decided to follow the initial instructions you posted...
I got the library loaded and the errors fixed in the library that were not compatible with the current version of Mach 4... and I got Mach 4 to post a massage the MQTT server (I attached a routine to a button for testing). This worked fine - but I could not get Mach 4 to respond to an incoming message - did you manage to get this to work?
This is the code I added to the screen load routine...
function mqttCallback(
topic, -- string
message) -- string
wx.wxMessageBox("mqttCallback");
--mc.mcCntlLoadGcodeFile(inst, "C:\\temp\\AliLive6.nc")
--mc.mcCntlSetLastError(inst, "Topic: " .. topic .. ", message: '" .. message .. "'");
mc.mcCntlSetLastError(inst, "message - test");
end
function SendMessage(msg)
--wx.wxMessageBox("mqttCallback");
mc.mcCntlSetLastError(inst, msg);
mqtt_client:publish("mach4", msg)
end
function MQTT_Connect()
--mqtt_client = mqtt.client.create("192.168.0.55", nil, mqttCallback)
mqtt_client = mqtt.client.create("192.168.0.3", nil, mqttCallback)
mqtt_client:connect("mach4")
mqtt_client:publish("mach4", "online")
mqtt_client:subscribe({"mach4/set"})
mqtt.Utility.set_debug(true)
end
MQTT_Connect()
I also added this to the screen load routine...
--socket module
package.loaded.socket = nil
socket = require "socket"
--MQTT module
package.loaded.MqttModul = nil
mqtt = require "mqtt_library_mod"
and I added this to the beginning of the PLC script...
mqtt_client:handler()
Alas I have not been able to get Mach4 to respond to a message from my Node-Red server. I would be really cool if I could post a gcode file name to Mach4 and tell it 'go'...
BTW...this is my first attempt to do anything with Mach4 - so its quite possible that I have made some simple mistakes. Also - I am trying to do this with the demo version, since this kind of thing is the reason I would upgrade to Mach4 - I want to see if I could get it working first - it maybe the reason its not working!!
-
i'm interested in setting up mqtt with mach4. is the bidirectional communication working?
-
I haven't done anything since my last post... I could not get Mach4 to respond to an incoming message.
I notice in one of Newfangled promotional videos they talk about using MQTT with Mach4 - so maybe it is possible - maybe they have it working (in both directions)... Who knows!...they don't seem to pay much attention to this forum!
-
I was able to get Mach to respond to incoming messages. Here is what worked for me.
Pretty much just a copy of what has already been posted.
Add mqtt_library.lua and utility.lua to modules folder in Mach4 directory.
Edit the mqtt_library, search for the following lines, and replace as shown:
if (return_code <= table.getn(MQTT.CONACK.error_message)) then -----> if (return_code <= #MQTT.CONACK.error_message) then
local topic_count = table.getn(outstanding[2]) -----> local topic_count = #outstanding[2]
Added the following to the end of the Screen Load Script:
---------------------------------------------------------------
--MQTT module
package.loaded.MqttModul = nil
mqtt = require "mqtt_library"
--Socket module
package.loaded.socket = nil
socket = require "socket"
---------------------------------------------------------------
function mqttCallback(
topic, -- string
message) -- string
mc.mcCntlSetLastError(inst,"".. topic ..": ".. message .."") --Example, Node-Red: Cycle Stop
end
function SendMessage(msg)
mc.mcCntlSetLastError(inst, msg);
mqtt_client:publish("Mach4",msg)
end
function MQTT_Connect()
mqtt_client = mqtt.client.create("192.168.1.44", nil, mqttCallback)
mqtt_client:connect("mach4")
mqtt_client:publish("mach4", "online")
mqtt_client:subscribe({"Node-Red"})
mqtt.Utility.set_debug(true)
end
MQTT_Connect()
Added this line to plc script.
mqtt_client:handler()
To test sending a message to Node-red I changed the CycleStop function in the Screen Load Script as shown below.
This publishes "Cycle Stopped" when the Stop button is pressed in Mach.
---------------------------------------------------------------
-- Cycle Stop function.
---------------------------------------------------------------
function CycleStop()
mc.mcCntlCycleStop(inst);
mc.mcSpindleSetDirection(inst, 0);
mc.mcCntlSetLastError(inst, "Cycle Stopped");
msg = "Cylce Stopped";
SendMessage(msg)
end
To test sending a message/command to Mach from Node-Red I added the following to the plc script.
This pulls the incoming message from the LastError dro. Then using the conditional statement it will stop or start the machine depending on the received message.
local last_error = mc.mcCntlGetLastError(inst);
if (last_error == 'Node-Red: Cycle Stop') then
CycleStop()
elseif (last_error == 'Node-Red: Cycle Start') then
CycleStart()
end
Hope this helps.
-Dustin
-
Hi Dusty91...Sorry for the delay in getting to this...
I did think that trying to trigger a process from and incoming MQTT could cause a threading problem i.e - the message arrives on a thread that does not have access to the main UI thread. So the message actually arrives - but it is difficult to 'see' it. Your approach of using the 'last_error' could be a way around this problem - and you seem to have got it working...I haven't tried it yet - when I get to it, I will try your approach.
Have you been looking at MQTT and Node-Red before you read this post or did you try all this once had read the post? I would be interested in hearing what you are doing with it - and whether you are already integrating MACH4 with you workflows? My principle idea is to integrate my cutting bench with an indexing machine - so the whole process can run all day using some kind of hopper system - is anyone doing this already do you know?
-
Hi made this work to send messages from Mach 4 thanks all!
has any one been able to have it send tool change messages? i have not found that part of the code yet.
cheers
Raptor
-
So ive managed to get this to work receiving mqtt messages too. I setup estop remotely and also being able to set a screen text box from an mqtt message. Tool change is still eluding me however
-
Will love a copy of the install instruction as I am in the progress of integrating MQTT for Mach4 with my existing mosquito environment. Thanks!
-
Will love a copy of the install instruction as I am in the progress of integrating MQTT for Mach4 with my existing mosquito environment. Thanks!
No need, using luamqtt (https://github.com/xHasKx/luamqtt) works great.