Machsupport Forum

Mach Discussion => Mach4 General Discussion => Topic started by: SebDE on March 15, 2018, 03:07:07 AM

Title: Mach4 MQTT Client
Post 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:

Code: [Select]
---------------------------------------------------------------
-- 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:

Code: [Select]
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:

Code: [Select]
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
Title: Re: Mach4 MQTT Client
Post by: arhimedoffs on March 15, 2018, 08:13:42 AM
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.
Title: Re: Mach4 MQTT Client
Post by: arhimedoffs on March 15, 2018, 08:24:35 AM
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.
Title: Re: Mach4 MQTT Client
Post by: SebDE on March 15, 2018, 09:53:50 AM
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.
Title: Re: Mach4 MQTT Client
Post by: arhimedoffs on March 15, 2018, 09:58:50 AM
Wow! Why so difficult path? I use direct ModbusTCP from Mach4 and all works fine.
Title: Re: Mach4 MQTT Client
Post by: SebDE on March 15, 2018, 10:30:42 AM
 ??? - 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 ....

Title: Re: Mach4 MQTT Client
Post by: arhimedoffs on March 15, 2018, 10:43:50 AM
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.
Title: Re: Mach4 MQTT Client
Post by: joeaverage on March 15, 2018, 08:32:33 PM
Hi,
are you sure, I've used RS485 to communicate with VFDs over Modbus both RTU and ASCII protocols
are supported.

Craig
Title: Re: Mach4 MQTT Client
Post by: arhimedoffs on March 16, 2018, 01:40:22 AM
Sorry, I miss something.
Title: Re: Mach4 MQTT Client
Post by: joeaverage on March 16, 2018, 02:09:43 AM
Hi,
sorry, my post wasn't clear.

Mach4's Modbus plugin can handle serial communication, certainly serial RTU and serial ASCII.

Craig
Title: Re: Mach4 MQTT Client
Post by: arhimedoffs on March 16, 2018, 02:13:46 AM
Yes. Thats why I say, I have missed this options  :)
Title: Re: Mach4 MQTT Client
Post by: TeakBeaker on December 28, 2018, 04:29:57 AM
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
Title: Re: Mach4 MQTT Client
Post by: TeakBeaker on January 01, 2019, 02:47:42 AM
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...
Code: [Select]
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...
Code: [Select]
--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...

Code: [Select]
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!!
Title: Re: Mach4 MQTT Client
Post by: raptor_demon on January 03, 2019, 11:16:36 AM
i'm interested in setting up mqtt with mach4. is the bidirectional communication working?
Title: Re: Mach4 MQTT Client
Post by: TeakBeaker on January 08, 2019, 05:19:27 AM
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!
Title: Re: Mach4 MQTT Client
Post by: Dusty91 on January 11, 2019, 07:56:28 PM
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:
Code: [Select]
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:
Code: [Select]
---------------------------------------------------------------
--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.
Code: [Select]
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.
Code: [Select]
---------------------------------------------------------------
-- 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.
Code: [Select]
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
Title: Re: Mach4 MQTT Client
Post by: TeakBeaker on January 16, 2019, 04:41:41 AM
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?

Title: Re: Mach4 MQTT Client
Post by: raptor_demon on April 04, 2019, 04:28:07 PM
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
Title: Re: Mach4 MQTT Client
Post by: raptor_demon on April 07, 2019, 12:15:52 AM
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
Title: Re: Mach4 MQTT Client
Post by: jcassar on January 24, 2022, 12:43:23 PM
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!
Title: Re: Mach4 MQTT Client
Post by: jcassar on January 24, 2022, 04:35:54 PM
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.