B4J Question Communicate with com port in a web server

hung

Active Member
Licensed User
Longtime User
Here is what I want to do:
1. set up a web server waiting to http request like : http://localhost:8888/getdata
2. in handler getdata, use jSerial to get data from COM Port
3. response http request with the data

When I run http://localhost:8888/, the root page returns ok. When I create a UI to "getdata", I could get data from com port.

But if I run http://localhost:8888/getdata, trying to getdata then return a web page, Server does not response.

When debug with F8, after page request from browser, I could see getdata returned data, but the web page does not send back to browser. Any hints?

Sample code as below
B4X:
Sub AppStart (Args() As String)
    srvr.Initialize("srvr")
    srvr.Port = 8888
    
    srvr.StaticFilesFolder = File.Combine(File.DirApp, "www")
    srvr.LogsFileFolder = File.Combine(File.DirApp, "logs")
    srvr.AddHandler("/getdata","getdata", False)

    srvr.Start
    Log("Server started")
    StartMessageLoop
End Sub

B4X:
Sub Handle(req As ServletRequest, resp As ServletResponse)
    mreq = req
    mresp = resp
    
    openport
    cmdflag = False
    getdata   
    Do While cmdflag = False
        Sleep(500)
        Log(cmdflag)
    Loop
    closeport
    Do While cmdflag = False       
    Loop
    
    resp.SetHeader("Access-Control-Allow-Origin", "*") ' allow cross domain call

    resp.Write(cmdrslt)
End Sub

B4X:
Sub openport()
    sp.Initialize("")
    sp.Open("COM19")
    sp.SetParams(sp.BAUDRATE_2400, sp.DATABITS_7, sp.STOPBITS_1, sp.PARITY_EVEN )
    astream.Initialize(sp.GetInputStream, sp.GetOutputStream, "astream")
End Sub

Sub closeport()
    sp.Close
End Sub

Sub astream_NewData (Buffer() As Byte)
    cmdrslt = BytesToString(Buffer, 0, Buffer.Length, "UTF-8")
    cmdflag = True
End Sub

Sub getdata()

    Dim buffer() As Byte
    buffer = bc.HexToBytes("020103") ' byte command to get data
    astream.Write(buffer)

End Sub
 

DonManfred

Expert
Licensed User
Longtime User
 
Upvote 0

hung

Active Member
Licensed User
Longtime User
Thanks.

In the main web server routine, after adding handler, there is a StartMessageLoop already.

If in the handler class I add another StartMessageLoop, will that conflict with the web server's StartMessageLoop?
 
Upvote 0

hung

Active Member
Licensed User
Longtime User
No conflict. Each connection is managed by its own thread so it can have its own message loop.
You are right. That works and no conflict with the main message loop.

Getting there.
 
Upvote 0

hung

Active Member
Licensed User
Longtime User
This is difficult. Need helps from you again.

B4X:
Sub Handle(req As ServletRequest, resp As ServletResponse)
    mreq = req
    mresp = resp   
    getdata   
    StartMessageLoop   
End Sub

Sub getdata()
    openport
    cmdOne   
End Sub

Sub openport()
    comport = mreq.GetParameter("comport")
    cmd = mreq.GetParameter("cmd")
    cmdrslt = ""
    sp.Initialize("")
    sp.Open(comport)
    sp.SetParams(sp.BAUDRATE_2400, sp.DATABITS_7, sp.STOPBITS_1, sp.PARITY_EVEN )
    sp.ReadingThreadInterval = 500
    astream.Initialize(sp.GetInputStream, sp.GetOutputStream, "astream")
End Sub

Sub closeport()
    If astream.IsInitialized Then
        astream.Close
    End If
    sp.Close
End Sub

Sub cmdOne()
    Dim buffer() As Byte
    buffer = bc.HexToBytes("4142434445")
    astream.Write(buffer)
End Sub

Sub showdata()
    Dim rslt As String
    mresp.SetHeader("Access-Control-Allow-Origin", "*") ' allow cross domain call
    mresp.ContentType = "application/json"
    
    rslt = $"{"rslt":""$ & cmdrslt & $""}"$

    mresp.Write(rslt)
End Sub

Sub astream_NewData (Buffer() As Byte)
    cmdrslt = cmdrslt & BytesToString(Buffer, 0, Buffer.Length, "UTF8")
    Log("rslt:" & cmdrslt)
    showdata
    closeport
    StopMessageLoop
End Sub

When debug, only when put a breakpoint inside cmdOne, then use browser access. When IDE stopped inside cmdOne, press F5 or F10 to continue, then could see response sends to browser correctly.

But when run, then use browser to access http://localhost:8888/getdatanow, browser cursor keep spinning. Never end.
 
Upvote 0

hung

Active Member
Licensed User
Longtime User
You should test it in release mode. In debug mode everything is executed on the main thread.

Do you see this line in the logs?
Log("rslt:" & cmdrslt)

Not seeing it in Release mode.
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
This means that you are not reading the data.

The design here is wrong. You should move the serial related code to the main module, call it with CallSubDelayed (pass Me so you will have a reference to the correct instance) and call back to the passed instance with CallSubDelayed again.

This way, the serial port will be managed from the main thread.
 
Upvote 0

hung

Active Member
Licensed User
Longtime User
This means that you are not reading the data.

The design here is wrong. You should move the serial related code to the main module, call it with CallSubDelayed (pass Me so you will have a reference to the correct instance) and call back to the passed instance with CallSubDelayed again.

This way, the serial port will be managed from the main thread.
As we need to wait until http call before getdata from comport, can I put the getdata and relevant codes in main module, then in handler calss call the getdata once received http request ?
 
Last edited:
Upvote 0

hung

Active Member
Licensed User
Longtime User
Hi Erel,

After moving all serial related codes to main module, then in handler class did the following.

B4X:
Sub Handle(req As ServletRequest, resp As ServletResponse)
    CallSubDelayed3(Main, "getdata", req, resp)
    StartMessageLoop   
End Sub

In debug mode, I could see browser receives response ok.

But in release mode, the getdata complete but browser keep cursor running. Looks like the "resp" in main module cannot response properly. i.e. mresp.Write(rslt) does not work in Release mode, but log("done") works well. Did I pass wrong parameter from handler ?

B4X:
Sub Process_Globals
    Private srvr As Server
    Private mreq As ServletRequest
    Private mresp As ServletResponse
...
Sub getdata(req As ServletRequest, resp As ServletResponse)
    mreq = req
    mresp = resp
    openport
    cmdOne
End Sub

B4X:
Sub showdata()
    Dim rslt As String
    mresp.SetHeader("Access-Control-Allow-Origin", "*") ' allow cross domain call
    mresp.ContentType = "application/json"
    
    rslt = $"{"rslt":""$ & cmdrslt & $""}"$
    Log(rslt)
    mresp.Write(rslt)
    Log("done")
End Sub

Sub astream_NewData (Buffer() As Byte)
    cmdrslt = cmdrslt & BytesToString(Buffer, 0, Buffer.Length, "UTF8")
    Log(Buffer.Length)
    Log("rslt:" & cmdrslt)
    showdata
    closeport
    StopMessageLoop
End Sub
 
Upvote 0

hung

Active Member
Licensed User
Longtime User
See my previous answer. You need to pass Me and then use it to call a method in the class (with CallSubDelayed) that will send the response and stop the message loop.
Do you mean
B4X:
Sub Handle(req As ServletRequest, resp As ServletResponse)
    mreq = req
    mresp = resp
    CallSubDelayed2(Main, "getdata", Me)
    StartMessageLoop
End Sub
in handler? Then in main module, how could I use resp or mresp to write back to browser?

This in main module does not response to browser
B4X:
Sub getdata(thereq As class_handler)
    onereq = thereq
    openport
    cmdOne
End Sub
Sub showdata( )
    Dim rslt As String
    onereq.mresp.SetHeader("Access-Control-Allow-Origin", "*") ' allow cross domain call
    onereq.mresp.ContentType = "application/json"
    
    rslt = $"{"rslt":""$ & cmdrslt & $""}"$
    Log(rslt)
    
    onereq.mresp.Write(rslt)
    Log("done" & Asc(cmdrslt))
End Sub
 
Upvote 0

hung

Active Member
Licensed User
Longtime User
Thanks for Erel's help. Finally I got it working. After resetting the serial device, then change as below. The flow browser -> web server -> serial port -> web server -> browser works ok.

In handler class.
B4X:
Sub Class_Globals
    Public mreq As ServletRequest
    Public mresp As ServletResponse
End Sub

Sub Handle(req As ServletRequest, resp As ServletResponse)
    mreq = req
    mresp = resp
    Log("Receive Request")
    CallSubDelayed2(Main, "getdata", Me)
    StartMessageLoop
End Sub

Sub showdata(cmdrslt As String  )
    Dim rslt As String
    mresp.SetHeader("Access-Control-Allow-Origin", "*") ' allow cross domain call
    mresp.ContentType = "application/json"
    
    rslt = $"{"rslt":""$ & cmdrslt & $""}"$
    Log(rslt)
    
    mresp.Write(rslt)
    Log("done" & Asc(cmdrslt))
    StopMessageLoop
End Sub

In main module

B4X:
Sub getdata(callback As handler_g)
    g_callback = callback
    openport
    cmdOne
End Sub

Sub cmdOne()    
    cmdflag = False    
    cmdrslt = ""
    sendhex ("023031330331") ' cancel
...
    sendhex ("023031360334") ' reqcount
    cmdflag = True
End Sub

Sub sendhex(strhex As String)
    Dim buffer() As Byte
    buffer = bc.HexToBytes(strhex)
    Log("sendhex " & BytesToString(buffer, 0, buffer.Length, "UTF8") )
    astream.Write(buffer)
    Sleep(10) ' this allows a break for serial device
End Sub

Sub astream_NewData (Buffer() As Byte)
    Log(bc.HexFromBytes(Buffer))
    cmdrslt = cmdrslt & BytesToString(Buffer, 0, Buffer.Length, "UTF8")
    Log("rslt:" & cmdrslt)
    If cmdflag Then
        CallSubDelayed2(g_callback, "showdata", cmdrslt)       
        Log("cmd done")
        closeport
    End If
End Sub
 
Upvote 0
Top