Hello Guest it is April 19, 2024, 10:41:20 PM

Author Topic: Mach4 MQTT Client  (Read 4876 times)

0 Members and 1 Guest are viewing this topic.

Mach4 MQTT Client
« 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

If someone is interested in the modified code and an installation instruction please let me know.

Thanks for help
Re: Mach4 MQTT Client
« Reply #1 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.
Re: Mach4 MQTT Client
« Reply #2 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.
Re: Mach4 MQTT Client
« Reply #3 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.
Re: Mach4 MQTT Client
« Reply #4 on: March 15, 2018, 09:58:50 AM »
Wow! Why so difficult path? I use direct ModbusTCP from Mach4 and all works fine.
Re: Mach4 MQTT Client
« Reply #5 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 ....

Re: Mach4 MQTT Client
« Reply #6 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.
Re: Mach4 MQTT Client
« Reply #7 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
'I enjoy sex at 73.....I live at 71 so its not too far to walk.'
Re: Mach4 MQTT Client
« Reply #8 on: March 16, 2018, 01:40:22 AM »
Sorry, I miss something.
Re: Mach4 MQTT Client
« Reply #9 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
'I enjoy sex at 73.....I live at 71 so its not too far to walk.'