Android Question Simple MQTT request (I think)

Harris

Expert
Licensed User
Longtime User
Erel suggested that I use MQTT for the following:

I have a scale controller that posts the current weight on a com port.
The com port is connected to a serial to ethernet converter.
The ethernet converter is connected to an Access Point.
The ethernet converter is set to tcp (address and port) 10.31.199.3:10001

If you telnet through the AP to the converter (using address above), you see it broadcasting the current scale weight value every second (ie. 120000 or 0 - if nothing is on the truck scale).

Question: How do I code my B4A app to get this value (over wifi of course)? Using MQTT or any other simple method.

Since this is such a common task for many of you, I thought I would ask first before I take the wrong garden path trying to figure it out on my own (wasting as much time as humanly possible).

If this post does not produce results, I guess, I suppose, when all else fails, if I must -- I could always read the manual (tutorial on MQTT and learn it as our host strongly recommended).

Thanks for your expert input.

Harris
 

freedom2000

Well-Known Member
Licensed User
Longtime User
Not so simple question !

Basically, there are two main parts in MQTT. One of them is mqtt broker and other one is mqtt client. Broker is the boss. It manages all clients so you have to connect to the broker to either send (publish) or get (subscribe) messages.
So clients could be publisher, subscriber or both.

The broker acts as a "man in the middle", it can use tcp, so should work with your use case


mqtt_publisher_subscriber-1.png
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
If you telnet through the AP to the converter (using address above), you see it broadcasting the current scale weight value every second (ie. 120000 or 0 - if nothing is on the truck scale).

Question: How do I code my B4A app to get this value (over wifi of course)?
I would guess you can use AsyncStream from the Network library to connect to the Device and listen to the Data incoming...

something like

B4X:
Sub Process_Globals
    Dim sock As Socket ' Network lib
    Dim AStreams As AsyncStreams ' RandomAccesfile lib
End Sub
Sub btnConnect_Click
    sock.Initialize("Scalecontroller")
    sock.Connect("10.31.199.3",10001,15000)
eb sub

Sub Scalecontroller_Connected (Successful As Boolean)
    Log($"Scalecontroller_Connected(${Successful})"$)
    AStreams.Initialize(sock.InputStream, sock.OutputStream, "Scale")
End Sub


Sub Scale_NewData (Buffer() As Byte)
    Dim msg As String
    msg = BytesToString(Buffer, 0, Buffer.Length, "UTF8")
    ToastMessageShow(msg, False)
    Log(msg)
End Sub
Sub Scale_Error
    ToastMessageShow(LastException.Message, True)
    Log("Scale_Error: "&LastException.Message)
End Sub
Sub Scale_Terminated
    Log("Scale_Terminated")
end sub
 
Upvote 0

Harris

Expert
Licensed User
Longtime User
Thanks guys...

@DonManfred - I shall implement this code and try it. Seems simple enough.

Then, I suspect, when form is closed, put this in the Pause method?

if sock.connected then
sock.close
end if

I could complicate this by getting the value and putting it on the server for the app to retrieve from there... but why?
If the network is not available - at least the AP generally will be in the scale shack - providing me with the data needed to create a record and send later when channel to server has been restored. Needs to be bullet proof - or at least rubber bullet resistant.

I shall follow up after testing. Winter storm moving in here in the arctic - imagine that! Trucking has been shut down but they will let me drive to the scale (1 km away).

Thanks
 
Upvote 0

Harris

Expert
Licensed User
Longtime User
Works like a charm....

B4X:
Sub StartScale  '  called from Resume...
  
    sock.Initialize("Scalecontroller")
    sock.Connect("10.131.199.3",10001, 10000)  ' Try to connect for up to 10 seconds.  If fail to connect, go to Plan B - SetWeight sub..
    ToastMessageShow(" Connecting to Scale Controller... ",False)

End Sub

Sub Scalecontroller_Connected (Successful As Boolean)
    Log($"Scalecontroller_Connected: (${Successful})"$)
    If Successful Then
        AStreams.Initialize(sock.InputStream,   Null, "Scale")  ' Init the IN stream - no out stream required
    Else
        ToastMessageShow(" Connection to Scale Controller Failed! ",True) ' If AP or something broken...  Ask for manual input weight next
        SetWeight      
    End If      
  
End Sub


Sub Scale_NewData (Buffer() As Byte)
    Dim w As String = ""
    w  = BytesToString(Buffer, 0, Buffer.Length, "UTF8")  ' temp var for processing in buffer
       Log("length of w: "&w.Length&" string: "&w)  ' show the length and what is inside...
  
'  Note: serial to ethernet device has a 2k buffer. This will be sent on first open but string is not 17 chars long (2017) - so ignore...

    If  w.Length = 17 Then    ' A valid string IS 17 characters long (no more - no less)
       For i = 0 To w.Length-1  ' this was used to see exactly what the string was made of (for testing).  Many space characters in it...
          Log(" i: "&i& "  ["&w.SubString2(i,i+1)&"]")  ' what is each character in string
          Log(" i: "&i& "  ["&Buffer(i)&"]")                   ' what is each buffer byte value
        Next
      
        wghtmsg = w.SubString2(3, 15)  '  this is the center (valid weight) portion of the passed buffer. The rest are control - info characters
      
        If w.SubString2(2,3) = Chr(34) Then  '     if byte 3 is a " , then the value of weight is negative .  ( you can't """ to do this, hence the Chr(34) )
           wghtmsg = "-"& wghtmsg.Trim  ' append a " - " (minus sign) to the trimmed weight value (get rid of spaces fore and aft)
        End If
      
        If w.SubString2(2,3) = "(" Then    ' if this char is at location, it means the scale has not yet stabilized (truck is driving on and shaking it)
           wghtmsg = "*"&wghtmsg.Trim&"*"  ' Add " * " to the string to indicate Not Stable
        End If
      
        wghtmsg = wghtmsg.Trim  ' trim this public var again
      
        lblweight.Text = wghtmsg.Trim&" lb"   ' add " lb" to the label text (not really needed)
    End If
  
    Log("  weight: "&lblweight.Text)
End Sub

Sub Scale_Error
    ToastMessageShow("Scale Error: "&LastException.Message, True)
    Log("Scale_Error: "&LastException.Message)
End Sub

Sub Scale_Terminated
    Log("Scale_Terminated")
    If AStreams.IsInitialized Then   ' close everything if terminated.  Also - close in Pause!
        AStreams.Close
        sock.Close
    End If

End Sub

Sub SetWeight   ' if mobile device can't reach AP, then have driver type in the weight
                       '  from the digital display on the scale controller (which most always works - 99.8%)

  Dim i As Int
  Dim xlo As String
  Dim kbrd As Phone
     
  xlo = "" ' NumberFormat2(lo ,1,1,1,False)

  Dim param As BD_InputBoxParams
  Dim bd As BetterDialogs
  param.Initialize
  param.Default = xlo
  param.InputType = param.INPUT_TYPE_DECIMAL_NUMBERS
  param.InputTextSize = 24
  param.Question = "Provide The Vehicle Weight"
  param.QuestionTextSize = 28
  param.SpaceBetween = 15dip
  param.Multiline = False
  i = bd.InputBox(" Controller Connection Failed! ",param,"OK","Cancel","",DefCM.BLC)

  If i = DialogResponse.POSITIVE  Then
        wghtmsg = param.Answer
  End If  
 
  kbrd.HideKeyboard(Activity)

End Sub

Before committing the value to a record - I check that it is a Number ( If IsNumber(wghtmsg) then ' proceed else ' NaN - return )

This functions really well. It retires an old (non-supported) software system running on XP and requiring 2 old PC's to operate.
It was also so very simple to implement. No huge learning curve (for now anyway).

This was (probably) the first/second time I asked for a expert solution before going off on a tangent and getting frustrated / nowhere.
I can't thank you enough for this 99.9 percent code solution!!!
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
Done and Done!

Thanks Again.
Thank you for the donation! :)

PD: Your thread just came online at a point i actually tried to use Async-Stream for another project...
My testcode was/is similar to the solution searched here... So i decided to change my testcode to match your issue and posted it here. Yesterday it was the first time i worked with Asyncstream. And honestly; It was a pleasure to post and to see it is a Solution for you.
 
Last edited:
Upvote 0

Harris

Expert
Licensed User
Longtime User
It was a pleasure to post and to see it is a Solution for you
Believe me, the pleasure and delight was all mine! It is such a treat to be shown something you have no idea of (pointed in the right direction) and take it to the finish line based on ones' particular needs. The solution was actually SO simple - WHEN you know what to do / where to look / start. Would be nice if all new / foreign tasks could be so easily solved - by reaching out. You help so many here as it is your generous nature to share your extensive knowledge.

On behalf of this (the best) community, we thank you and all others that support us in our time of need.
 
Upvote 0
Top