MQTT

Updated 0n April 3, 2015 See at bottom

The best system to interchange data between different systems remains MQTT and there are about libraries for all operating systems and for all development systems. MQ Telemetry Transport (MQTT) is a lightweight broker-based publish/subscribe messaging protocol designed to be open, simple, lightweight and easy to implement.

These characteristics make it ideal for use in constrained environments, for example, but not limited to where the network is expensive, has low bandwidth or is unreliable or when run on an embedded device with limited processor or memory resources.

I wanted to try my hand from here in the thing and I started with good examples on the net and converting the various sources to get a library working with the protocol 3.1 This protocol has only a few restrictions in the use of wildcard characters but lends itself to interchange messages string between systems without great waste of resources and every system support it.

Features of the protocol include the publish/subscribe message pattern to provide one-to-many message distribution and decoupling of applications, a messaging transport that is agnostic to the content of the payload and the use of pure TCP/IP to provide basic network connectivity. Three qualities of service for message delivery ar supported:

  • "At most once", where messages are delivered according to the best efforts of the underlying TCP/IP network. Message loss or duplication can occur. This level could be used, for example, with ambient sensor data where it does not matter if an individual reading is lost as the next one will be published soon after.
  • "At least once", where messages are assured to arrive but duplicates may occur.
  • "Exactly once", where message are assured to arrive exactly once. This level could be used, for example, with billing systems where duplicate or lost messages could lead to incorrect charges being applied.

I started from the library for Wiz550io that mounts the W5500 and I created an additional module called MQTT.BAS.

You can download Firewing library modules here: MQTT Library

This library is compatible with this shields or eth module

Hardware changes necessary to use the IOShield-A (contributed by Jerry Messina)

For the debugging I used two programs written in VB.NET that reference the open source library M2Mqtt (m2mqtt.codeplex.com) which among other things allows to get executables for Windows / Linux (Mono) and Windows Phone (7.8 and 8.1) a boon. However, nothing prevents you from using other development systems since this protocol is well established and there are a lot of libraries/sources for Android and iOS.

You can download vb.net windows client examples here: VB.NET test client

To use MQTT need a server named "Broker" and even in this case there are open source libraries for all operating systems and I used mosquitto for windows that provides two of his application to send new and receive messages from the command line. Mosquitto also provides a service broker on line for any test but after a couple of weeks has banned my IP address.

In this protocol is defined "Topic" a message queue and one of the most important features is the ability to define a Topic (named WillTopic ) to send a message (named WillMessage) to other connected clients that occurred a single client disconnection. If we add the fact that everyone can have a broker in your own home and can make visible the internet service by opening a single port of a router .. hello internet of things.

You can download FW test programs here: FW test programs

Clearly not going to go into too much detail of MQTT but would definitely recommend to take a ride on the Internet to gather information and in a week or so you'll be more than satisfied.

MQTT Reader program

#option WIZ_DHCP_DEBUG     = false
#option WIZ_DNS_DEBUG      = false
#option MQTT_DEBUG         = false
#option MQTT_DUMP          = false

#option USE_DHCP           = true
#option USE_DNS            = false
#option WIZ_SCS_PIN        = _D10   
#option WIZ_RST_PIN        = _D9     
#option TCP_PORT_ROM_ADDR  = 0

imports "W5500-Consts"
imports "W5500-Utils"
imports "W5500"
imports "MQTT"

Private Sub ConfigDisplay()
    Console.Write(13,10)
    Console.Write("MAC Address ",SARtoString(),13,10)
    Console.Write("IP Address  ",SIPRtoString(),13,10)
    Console.Write("Subnet Mask ",SUBRtoString(),13,10)
    Console.Write("Gateway     ",GARtoString(),13,10)
    Console.Write(13,10)    
End Sub

#if USE_DHCP
   imports "W5500-DHCP"
   sub OnUpdate() Handles W5500_DHCP.OnUpdateEvent
       ConfigDisplay()
   End Sub

   sub OnConflict() Handles W5500_DHCP.OnConflictEvent
   End Sub
#endif

// ##################################################################################

// ##################################################################################

Dim sockreg         As Byte

Private Const 
    BrokerAddr(4)   As Byte     = {192,168,1,50},
    Topic1                      = "/mqtt/topic1",
    Topic2                      = "/mqtt/topic2",
    TopicNum        As Byte     = 2, 
    WillTopic                   = "/mqtt/willtopic"  

Private Dim 
    MessQueue1      As String(256),
    MessQueue2      As String(256),
    WillMessage     As String(256)

private sub Main()     
   #if USE_DHCP = true       
       Console.Write("Now find the DHCP server.. ",13,10)    
       If DHCPTask(3) = false Then
           Console.Write("DHCP error, so must set static parms.. ",13,10)
           ConfigDisplay()
       End If    
   #else
       Console.Write("Using static config.. ",13,10)    
       ConfigDisplay()
   #endif

   DelayMS(1000)

   console.writeline("")
   console.writeline("My IP Address is " + SIPRtoString())
   DelayMS(2000)
   sockreg = 0

   MessQueue1= ""
   MessQueue2= ""
   WillMessage = "Sorry, see you later"

   If Not MQTTSetBroker(sockreg, addressof(BrokerAddr), 1883, &H00) Then
      console.writeline("MQTT: Errors into configuration parms")
      W5500.Close(sockreg)
   Else
      console.writeline("MQTT: Broker parms is ok..")
      console.writeline("MQTT: Now store 2 subscriptions in memory")	
      If MQTTAddSubscription(addressof(Topic1),
                             QOS_LEVEL_EXACTLY_ONCE,
                             addressof(MessQueue1), 
                             SizeOf(MessQueue1)) Then
         console.writeline("MQTT: Subscriptions added successfully on Topic1")
      Else
         console.writeline("MQTT: Unable to set subscription on Topic1")
      End If

      If MQTTAddSubscription(addressof(Topic2),
                             QOS_LEVEL_EXACTLY_ONCE,
                             addressof(MessQueue2), 
                             SizeOf(MessQueue2)) Then
          console.writeline("MQTT: Subscriptions added successfully on Topic2")
      Else
          console.writeline("MQTT: Unable to set subscription on Topic1")
      End If


      console.writeline("MQTT: Now can try a connection to broker")

      If MQTTConnect("MyMQTTReader", false, false, 1, addressof(WillTopic), addressof(WillMessage)) Then 
         console.writeline("MQTT: MyReader is now connected to broker")            
         console.writeline("MQTT: Now send subscriptions to broker")            
         If MQTTSubscribe() Then
            console.writeline("MQTT: Subscriptions are made")            
            console.writeline("All is done!")            

            While true
               MQTTService()
               if MQTTIsQueueChanged(0) Then
                  console.writeline("A new message on MessQueue1")
                  console.writeline(">", MessQueue1) 
               End If
               If MQTTIsQueueChanged(1) Then
                  console.writeline("A new message on MessQueue2")
                  console.writeline(">", MessQueue2) 
               End If
            End While                
         Else
            console.writeline("MQTT: Unable to set subscriptions on broker")
         End If
      Else
         console.writeline("MQTT: Unable to set connection")
      End If
   End If

   While true
   End While
end sub

MQTT Sender program

#option WIZ_DHCP_DEBUG     = false
#option WIZ_DNS_DEBUG      = false
#option MQTT_DEBUG         = false
#option MQTT_DUMP          = false

#option USE_DHCP           = true
#option USE_DNS            = false
#option WIZ_SCS_PIN        = _D10   
#option WIZ_RST_PIN        = _D9     
#option TCP_PORT_ROM_ADDR  = 0

imports "W5500-Consts"
imports "W5500-Utils"
imports "W5500"
imports "MQTT"

Private Sub ConfigDisplay()
    Console.Write(13,10)
    Console.Write("MAC Address ",SARtoString(),13,10)
    Console.Write("IP Address  ",SIPRtoString(),13,10)
    Console.Write("Subnet Mask ",SUBRtoString(),13,10)
    Console.Write("Gateway     ",GARtoString(),13,10)
    Console.Write(13,10)    
End Sub

#if USE_DHCP
   imports "W5500-DHCP"
   sub OnUpdate() Handles W5500_DHCP.OnUpdateEvent
       ConfigDisplay()
   End Sub

   sub OnConflict() Handles W5500_DHCP.OnConflictEvent
   End Sub
#endif

// ##################################################################################

// ##################################################################################

Dim sockreg         As Byte

Private Const 
    BrokerAddr(4)   As Byte     = {192,168,1,50},
    Topic1                      = "/mqtt/topic1",
    Topic2                      = "/mqtt/topic2",
    TopicNum        As Byte     = 2, 
    WillTopic                   = "/mqtt/willtopic"    

Private Dim 
    WillMessage     As String(256),
    MessQueue1      as string(256),
    MessQueue2      as string(256)

private sub Main()
   #if USE_DHCP = true       
       Console.Write("Now find the DHCP server.. ",13,10)    
       If DHCPTask(3) = false Then
           Console.Write("DHCP error, so must set static parms.. ",13,10)
           ConfigDisplay()
       End If    
   #else
       Console.Write("Using static config.. ",13,10)    
       ConfigDisplay()
   #endif

   DelayMS(1000)

   console.writeline("")
   console.writeline("My IP Address is " + SIPRtoString())
   DelayMS(2000)
   sockreg = 0

   MessQueue1= ""
   MessQueue2= ""
   WillMessage = "Sorry, see you later"

   If Not MQTTSetBroker(sockreg, addressof(BrokerAddr), 1883, &H00) Then
	   console.writeline("MQTT: Errors into configuration parms")
      W5500.Close(sockreg)
   Else
	   console.writeline("MQTT: Broker parms is ok..")
	   console.writeline("MQTT: Now connect to it")

      If MQTTConnect("MyMQTTSender", 
                     false, 
                     false, 
                     0, 
                     addressof(WillTopic), 
                     addressof(WillMessage) ) Then

         console.writeline("MQTT: MySender is now connected to broker")            
         If MQTTPublish(addressof(Topic1),"Hello world on Topic1") Then
            console.writeline("MQTT: Message published")
         Else
            console.writeline("MQTT: Unable to publish message")
         End If

         While true
            MQTTService()
         End While
      Else
         console.writeline("MQTT: Unable to set connection")
      End If
   End If

   While true
   End While
end sub

Updated 0n April 3, 2015

As rightly pointed out in this post by Jerry Messina the MQTT-Tick.bas module should be change as follows to prevent invalid keep-alive time:

Module MQTT-Tick.BAS


module Tick

private const _osc = _clock                             // alias for Code Explorer
private const _fosc as uinteger = _osc * 1000000 / 2     // instructions per second
private const _ms as ushort = _fosc / 1000               // instructions every 1 ms

private _ticks as uinteger = 0                             // increment every millisecond

// interrupt will increment tick every millisecond...
private Interrupt OnTimer(Pic.T2Interrupt)   
   _ticks += 1               ' increment tick       
   IFS0.7 = 0               ' clear interrupt flag
End Interrupt

public function Time() as uinteger
   disable(OnTimer)
   Time = _ticks
   enable(OnTimer)
end function

// alias for code compatibility with existing MQTT module
public dim ms as Time

// init module...
sub main()
   T2CON = 0                ' clear Timer 1 - no presccale
   PR2 = _ms                ' set timer period
   IFS0.7 = 0               ' clear timer interrupt flag     
   T2CON.15 = 1             ' start timer
   enable(OnTimer)
end sub

End module