Serial Port comunication witch arduino

Just starting out? Need help? Post your questions and find answers here.
infratec
Always Here
Always Here
Posts: 6873
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Serial Port comunication witch arduino

Post by infratec »

If you can not find out, why my code is not working, then you did not understand how your arduino communicate.
If you don't know this, then you run in much more trouble if you start with threads.

First you have to know what it answers when you send your commands. (all bytes of it, not only the readable text)
You have to know when the answer is finished.

All other things are 'fishing blind'.
User avatar
mk-soft
Always Here
Always Here
Posts: 5398
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Serial Port comunication witch arduino

Post by mk-soft »

What I'm missing here is the description of the protocol. Normally there is always a document how the protocol is structured.
As it looks it is ASCII and with end character #CRLF$. Otherwise my example could not work as a thread with the end character 'CRLF' set.

So it applies:
Without a description of the protocol, you don't have to start at all.
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
User avatar
mk-soft
Always Here
Always Here
Posts: 5398
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Serial Port comunication witch arduino

Post by mk-soft »

I have extended the example so that you can copy the content of the ListView to the clipboard with Context.

Let's see what the output of the Comport delivers ?!

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:14 am, edited 3 times 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
ludoke
Enthusiast
Enthusiast
Posts: 153
Joined: Fri Jul 08, 2016 5:35 pm
Location: Essen (Belgium)

Re: Serial Port comunication witch arduino

Post by ludoke »

many thanks,

the description of the protocol,I have no idea.
Maybe i think a bit too simple,I suppose the Arduino works well that it is ok.

The arduino receives textstrings with gcode commands for example: G2 X6.962 Y6.4654 I0.25 J-0.433
from the PC ,when the arduino is ready is sends "ok" ,then the PC can send the next line of gcode ,if a "?" command is send ,the arduino anwers with the actual position of the machine.
This can be used to show the movement of a tool
There are many ohter commands and the arduino accept them ,so I think that it works fine.
Your code is a good start,but I still have to learn a lot.
User avatar
mk-soft
Always Here
Always Here
Posts: 5398
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Serial Port comunication witch arduino

Post by mk-soft »

What...

You must have a description, or you program the Arduino yourself. Somehow from where you must know what is sent and received.

Everything else is nonsense.
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
ludoke
Enthusiast
Enthusiast
Posts: 153
Joined: Fri Jul 08, 2016 5:35 pm
Location: Essen (Belgium)

Re: Serial Port comunication witch arduino

Post by ludoke »

Here some more info about the protocol.

https://github.com/gnea/grbl/wiki/Grbl- ... am-to-grbl
Streaming Protocol: Simple Send-Response [Recommended]

The send-response streaming protocol is the most fool-proof and simplest method to stream a G-code program to Grbl. The host PC interface simply sends a line of G-code to Grbl and waits for an ok or error: response message before sending the next line of G-code. So, no matter if Grbl needs to wait for room in the look-ahead planner buffer to finish parsing and executing the last line of G-code or if the host computer is busy doing something, this guarantees both to the host PC and Grbl, the programmed G-code has been sent and received properly. An example of this protocol is published in our simple_stream.py script in our repository.

more...
infratec
Always Here
Always Here
Posts: 6873
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Serial Port comunication witch arduino

Post by infratec »

as I have written:

You have also to know the bytes which you not see, like CR and LF

So use a terminal program which shows you also the hex bytes and tell us what is transmitted.

Do you see as last 'Ok' ?
Is it followed by a CR or a LF or both?

Or do you see a new prompt?
Is there a space after it?

If you don't know this, you will never get success. With or without thread.
And since we don't have this programm running, it is your job to investigate this.
ludoke
Enthusiast
Enthusiast
Posts: 153
Joined: Fri Jul 08, 2016 5:35 pm
Location: Essen (Belgium)

Re: Serial Port comunication witch arduino

Post by ludoke »

infratec,
maybe you are right,You are probably an advanced programmer.
The mksoft code also works very well.
But as far as I can test the simple communication seems to work.
The Arduino accepts all the commands.If a valid code has been processed the arduino will send "OK"
If there is an error, the arduino replies that an error has occurred.
While the arduino is moving an axis , I can send a "?" to request the position .
I can send as many "?" as possible the arduino keep sending the position of the machine .
What else can I do, it seems to work anyway.
User avatar
mk-soft
Always Here
Always Here
Posts: 5398
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Serial Port comunication witch arduino

Post by mk-soft »

Simply check whether control characters (ASCII < 32) are present.
See PB character table
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
ludoke
Enthusiast
Enthusiast
Posts: 153
Joined: Fri Jul 08, 2016 5:35 pm
Location: Essen (Belgium)

Re: Serial Port comunication witch arduino

Post by ludoke »

what do you mean?
the arduino sends a CR, LF character (13,10).
There are no other control chars.
As far as I understand the GBRL in the arduino just receives text,and answer with text + CR+LF.
infratec
Always Here
Always Here
Posts: 6873
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Serial Port comunication witch arduino

Post by infratec »

So there is no prompt?

You see an empty line for entering new commands?

Or something like in DOS you see c:\> for example, in linux maybe : $
ludoke
Enthusiast
Enthusiast
Posts: 153
Joined: Fri Jul 08, 2016 5:35 pm
Location: Essen (Belgium)

Re: Serial Port comunication witch arduino

Post by ludoke »

there is actually no promts, only 'ok' or error or another text message.
It's just sending, waiting for answers and responding accordingly.
in fact, just textstrings are sent back and forth.I don't know how to explain it differently.

when the GRBL is connected to the PC, a message is sent to the PC.
Grbl 1.1f ['$' for help]

Then the Arduino is ready to receive commands.
If a command is sent to the arduino, the arduino will check this command and if there is no error, the command will be executed.
If there is an incorrect command, the Arduino will send an error code to the PC.
When the Arduino finishes executing the command, it sends an "OK".
Now the PC can send a new command and so on.That's what needs to be done.
Of course other messages can be sent and received,but it is just text.
Some commands examples:
$G (view parser state) answer [GC:G0 G54 G17 G21 G90 G94 M5 M9 T0 F0 S0]
ok

$I (view build info) answer[ VER:1.1f.20170801:][OPT:V,15,128] ok
Last edited by ludoke on Mon Dec 16, 2019 10:10 pm, edited 1 time in total.
infratec
Always Here
Always Here
Posts: 6873
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Serial Port comunication witch arduino

Post by infratec »

So it always sends an OK at the end of the transmission.
But still: ist there a CR+LF behind?
ludoke
Enthusiast
Enthusiast
Posts: 153
Joined: Fri Jul 08, 2016 5:35 pm
Location: Essen (Belgium)

Re: Serial Port comunication witch arduino

Post by ludoke »

there is also een CR +LF after the 'OK'.
User avatar
mk-soft
Always Here
Always Here
Posts: 5398
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Serial Port comunication witch arduino

Post by mk-soft »

It can only be #CRLF$, otherwise it would not work with my thread example for Comport communication.
There is #CRLF$ entered as end identifier and passes the string without #CRLF$ to the callback.

When entering SendText and then the start signal SendSignal the end identifier #CRLF$ is automatically added and transmitted.
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
Post Reply