Serial Port response
Serial Port response
I have a PB application running in Windows Pro64. It communicates with an Arduino via a serial port. When the Arduino sends a message, the PB application does not get the message until I move the cursor within the PB window. Is there any way to get the PB application to respond the message as soon as it arrives? I have tried searching the forum as well as the PB documentation and have not been successful in finding anything helpful.
Re: Serial Port response
Sounds like you have an event loop using WaitWindowEvent() insted of WindowEvent()
Anyway, probably best to monitor the port in a Thread.
Anyway, probably best to monitor the port in a Thread.
Re: Serial Port response
You need Threads and PostEvent...
Here an example with ASCII Kommunikation
Link: viewtopic.php?f=13&t=73812&start=30#p546215
EndOfText is defined as #CRLF$ (13)(10). See InitComport
Update v0.06
Here an example with ASCII Kommunikation
Link: viewtopic.php?f=13&t=73812&start=30#p546215
EndOfText is defined as #CRLF$ (13)(10). See InitComport
Update v0.06
Code: Select all
;-TOP
; Comment : Comport Manager Over Thread and Callback
; Author : mk-soft
; Version : v0.06
; Created : 26.01.2018
; Updated : 25.09.2020
; *****************************************************************************
CompilerIf #PB_Compiler_Thread = 0
CompilerError "Use Option Threadsafe!"
CompilerEndIf
Prototype ProtoReceiveCB(Text.s)
Prototype ProtoStatusCB(Status, *ComData)
Enumeration
#ComThread_Stopped
#ComThread_Startup
#ComThread_Running
EndEnumeration
Enumeration
#ComStatus_Nothing
#ComStatus_OpenPort
#ComStatus_ClosePort
#ComStatus_ErrorOpenPort
#ComStatus_ErrorSend
#ComStatus_ErrorReceive
#ComStatus_ErrorDataSize
EndEnumeration
Structure udtComData
; Header
ThreadID.i
Exit.i
Status.i
; Port Data
ComID.i
Port.s
Baud.i
Parity.i
DataBit.i
StopBit.i
Handshake.i
BufferSize.i
; End Of Text
EndOfText.s
; Send Data
SendSignal.i
SendCount.i
SendText.s
SendError.i
; Receive data
ReceiveCount.i
ReceiveText.s
ReceiveError.i
; Callback
*StatusCB.ProtoStatusCB
*ReceiveCB.ProtoReceiveCB
EndStructure
Procedure thComport(*ComData.udtComData)
Protected *Send, *Receive, SendText.s, SendLen, ReceiveText.s, ReceiveLen, Pos
With *ComData
; Startup
\Status = #ComThread_Startup
\SendCount = 0
\ReceiveCount = 0
\ComID = OpenSerialPort(#PB_Any, \Port, \Baud, \Parity, \DataBit, \StopBit, \Handshake, \BufferSize, \BufferSize)
If \ComID
\Status = #ComThread_Running
Else
If \StatusCB
\StatusCB(#ComStatus_ErrorOpenPort, *ComData)
EndIf
\Status = #ComThread_Stopped
ProcedureReturn 0
EndIf
If \StatusCB
\StatusCB(#ComStatus_OpenPort, *ComData)
EndIf
*Send = AllocateMemory(\BufferSize)
*Receive = AllocateMemory(\BufferSize)
; Loop
Repeat
If \SendSignal
SendText = \SendText + \EndOfText
SendLen = StringByteLength(SendText, #PB_Ascii)
If SendLen <= \BufferSize
PokeS(*Send, SendText, SendLen, #PB_Ascii)
If WriteSerialPortData(\ComID, *Send, SendLen) = 0
\SendError = SerialPortError(\ComID)
If \StatusCB
\StatusCB(#ComStatus_ErrorSend, *ComData)
EndIf
Else
\SendError = 0
\SendCount + 1
EndIf
Else
If \StatusCB
\StatusCB(#ComStatus_ErrorDataSize, *ComData)
EndIf
EndIf
\SendSignal = #False
EndIf
ReceiveLen = AvailableSerialPortInput(\ComID)
If ReceiveLen
ReceiveLen = ReadSerialPortData(\ComID, *Receive, ReceiveLen)
If ReceiveLen = 0
\ReceiveError = SerialPortError(\ComID)
If \StatusCB
\StatusCB(#ComStatus_ErrorReceive, *ComData)
EndIf
Else
\ReceiveError = 0
EndIf
ReceiveText + PeekS(*Receive, ReceiveLen, #PB_Ascii)
Repeat
pos = FindString(ReceiveText, \EndOfText, 1, #PB_String_NoCase)
If pos
\ReceiveText = Left(ReceiveText, pos - 1)
ReceiveText = Mid(ReceiveText, pos + Len(\EndOfText))
\ReceiveCount + 1
If \ReceiveCB
\ReceiveCB(\ReceiveText)
EndIf
EndIf
Until pos = 0
EndIf
Delay(10)
Until \Exit
; Shutdown
CloseSerialPort(\ComID)
If \StatusCB
\StatusCB(#ComStatus_ClosePort, *ComData)
EndIf
FreeMemory(*Send)
FreeMemory(*Receive)
\Status = #ComThread_Stopped
\ComID = 0
\Exit = 0
ProcedureReturn 1
EndWith
EndProcedure
; *****************************************************************************
; Threaded String Helper
Procedure AllocateString(String.s)
Protected *mem
*mem = AllocateMemory(StringByteLength(String) + SizeOf(Character))
If *mem
PokeS(*mem, String)
EndIf
ProcedureReturn *mem
EndProcedure
Procedure.s FreeString(*Mem)
Protected result.s
If *Mem
result = PeekS(*Mem)
FreeMemory(*Mem)
EndIf
ProcedureReturn result
EndProcedure
; *****************************************************************************
CompilerIf #PB_Compiler_IsMainFile
Global ComData.udtComData
Enumeration EventCustomValue #PB_Event_FirstCustomValue
#My_Event_NewData
#My_Event_NewState
EndEnumeration
; ---------------------------------------------------------------------------
Procedure ReceiveCB(Text.s)
PostEvent(#My_Event_NewData, 0, 0, 0, AllocateString(Text))
EndProcedure
;---------------------------------------------------
Procedure MyEventNewDataCB()
Protected Text.s
Text = FreeString(EventData())
AddGadgetItem(0, -1, Text)
SetGadgetState(0, CountGadgetItems(0) - 1)
SetGadgetState(0, -1)
EndProcedure
BindEvent(#My_Event_NewData, @MyEventNewDataCB())
; ---------------------------------------------------------------------------
Procedure StatusCB(Status, *ComData.udtComData)
PostEvent(#My_Event_NewState, 0, 0, Status, *ComData)
EndProcedure
;------------------------------------------------------------------------
Procedure MyEventNewStateCB()
Protected Text.s, Status, *ComData.udtComData
Status = EventType()
*ComData = EventData()
Select Status
Case #ComStatus_OpenPort
Text = "ComStatus: Open Port " + *ComData\Port
Case #ComStatus_ClosePort
Text = "ComStatus: Close Port " + *ComData\Port
Case #ComStatus_ErrorOpenPort
Text = "ComError: Open Port " + *ComData\Port
Case #ComStatus_ErrorSend
Text = "ComError Send: Port " + *ComData\Port + " - ErrorCode " + *ComData\SendError
Case #ComStatus_ErrorReceive
Text = "ComError Receive: Port " + *ComData\Port + " - ErrorCode " + *ComData\ReceiveError
EndSelect
If Bool(Text)
StatusBarText(0, 0, Text)
EndIf
EndProcedure
BindEvent(#My_Event_NewState, @MyEventNewStateCB())
; ---------------------------------------------------------------------------
Procedure InitComport()
With ComData
If \Status
ProcedureReturn 2 ; Always running
EndIf
\Port = "COM4"
\Baud = 115200
\Parity = #PB_SerialPort_NoParity
\DataBit = 8
\StopBit = 1
\Handshake = #PB_SerialPort_NoHandshake
\BufferSize = 2048
\EndOfText = #CRLF$
\StatusCB = @StatusCB()
\ReceiveCB = @ReceiveCB()
\ThreadID = CreateThread(@thComport(), ComData)
If Not \ThreadID
StatusBarText(0, 0, "Comport " + \Port + ": Error Create Thread")
ProcedureReturn 0 ; Error create thread
Else
ProcedureReturn 1 ; ok
EndIf
EndWith
EndProcedure
Procedure Main()
Protected Event, Text.s, i
If OpenWindow(0, #PB_Ignore, #PB_Ignore, 800, 600, " Test Comport", #PB_Window_SystemMenu)
CreateStatusBar(0, WindowID(0))
AddStatusBarField(#PB_Ignore)
ListViewGadget(0, 0, 0, 800, 540)
StringGadget(1, 5, 545, 590, 25, "")
ButtonGadget(2, 605, 545, 90, 25, "Send")
ButtonGadget(3, 695, 545, 90, 25, "On/Off")
AddKeyboardShortcut(0, #PB_Shortcut_Return, 1000)
CreatePopupMenu(1)
MenuItem(101, "Copy ListView")
Repeat
Event = WaitWindowEvent()
Select Event
Case #PB_Event_CloseWindow
If ComData\Status
MessageRequester("Info", "Comport Is Open!", #PB_MessageRequester_Warning)
Else
Break
EndIf
Case #PB_Event_Gadget
Select EventGadget()
Case 0
If EventType() = #PB_EventType_RightClick
DisplayPopupMenu(1, WindowID(0))
EndIf
Case 2 ; send button
If ComData\Status = #ComThread_Running And ComData\SendSignal = #False
ComData\SendText = GetGadgetText(1)
ComData\SendSignal = #True
EndIf
Case 3 ; on/off button
If ComData\Status
ComData\Exit = 1
Else
InitComport()
EndIf
EndSelect
Case #PB_Event_Menu
Select EventMenu()
Case 101 ; popup menu
Text = ""
For i = 0 To CountGadgetItems(0) - 1
text + GetGadgetItemText(0, i) + #CRLF$
Next
SetClipboardText(Text)
EndSelect
EndSelect
ForEver
EndIf
EndProcedure : Main()
CompilerEndIf
Last edited by mk-soft on Sat Sep 03, 2022 11:17 am, edited 1 time in total.
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
Re: Serial Port response
Hi Paul and mk-soft,
Thanks for your responses. I tried replacing WaitWindowEvent with WindowEvent, as shown below, based on Paul's 1st suggestion. There was no change in the operation as I described in my post - no bytes received until I move the cursor within the window.
As far as mk-soft's suggestion... All I would like to do is simply notify the main repeat loop when a byte has been received. I do not need to receive or transmit data in a thread. I am quite new to Windows programming as well as PB so I am a little bit leery of diving into a complex thread implementation.
Thanks for your responses. I tried replacing WaitWindowEvent with WindowEvent, as shown below, based on Paul's 1st suggestion. There was no change in the operation as I described in my post - no bytes received until I move the cursor within the window.
Code: Select all
;Event = WaitWindowEvent()
Event = WindowEvent()
If Event = 0
Delay(10)
Continue
EndIf
Re: Serial Port response
I'm sure you understand it is much easier to point someone in the right direction if they post some actual code instead of pseudo code.
Why not post what you have so far?
Why not post what you have so far?
Re: Serial Port response
It is never good to process asynchronous communication (like comports) in the event loop of the GUI.
This can be interrupted, for example when the menu window is open. And, there is also no event from comports.
It is always better to program communication in threads. And it's not that bad to work with threads and it's not that difficult to handle them, if you do it right. I also think the handling of structures is an important part of programming and belongs to the basics.
It also makes little sense "I only want to receive one byte". Late when it should be more than one byte the problems start. So first create a concept of how the data should be expanded.
Programming the messages in ASCII in Arduino has the advantage that you can also test them in the terminal. For data I find a better solution than Modbus/TCP (Ethernet), because there are ready made codes for most controllers and the programming of the Modbus/TCP interface is very easy.
Thank to DeepL.com
This can be interrupted, for example when the menu window is open. And, there is also no event from comports.
It is always better to program communication in threads. And it's not that bad to work with threads and it's not that difficult to handle them, if you do it right. I also think the handling of structures is an important part of programming and belongs to the basics.
It also makes little sense "I only want to receive one byte". Late when it should be more than one byte the problems start. So first create a concept of how the data should be expanded.
Programming the messages in ASCII in Arduino has the advantage that you can also test them in the terminal. For data I find a better solution than Modbus/TCP (Ethernet), because there are ready made codes for most controllers and the programming of the Modbus/TCP interface is very easy.
Thank to DeepL.com
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
Re: Serial Port response
Hi mk-soft and others,
For now I am taking the easy way out by using a WindowTimer which generates an event every 50msec. I will take a look at threads as I learn more about PB.
For now I am taking the easy way out by using a WindowTimer which generates an event every 50msec. I will take a look at threads as I learn more about PB.