Send and receive Data from the serial ComPort , no lib

Share your advanced PureBasic knowledge/code with the community.
User avatar
Rings
Moderator
Moderator
Posts: 1435
Joined: Sat Apr 26, 2003 1:11 am

Send and receive Data from the serial ComPort , no lib

Post by Rings »

Code updated for 5.20+. The SerialPort library can handle this as well.

After answering the same boring questions again, i have written these small example how to read and receive Data over the serial Connector(ComPort) with only API's and with a Timer driven non blocking Receiving.
I have written this to test a Modem and its settings (AT-Hayes-Commands)

Feel free to fit your needs .

Rings

Code: Select all

;  ComPort (RS232) example , Write and Read-data, timer driven
;  good for a small terminal or a modem-configurator
;  Windows only, done on january, 14th. 2004
;  Copyright 2004 Siegfried Rings (CodeGuru)
;
OnErrorGoto(?Errorhandling) ;Enable Errorhandling (enable Linenumbering!)

;User Structure for all the ComPort-Stuff
Structure structComport
  Port.l
  Inbuffersize.l
  OutBuffersize.l
  Handle.l
  ct.COMMTIMEOUTS
  dcb.DCB
  Receivebuffer.l
  ReceivebufferLength.l
EndStructure

Enumeration
  #Gadget_TextSend
  #Gadget_TextReceive
  #Gadget_ButtonSend
  #Gadget_ButtonReceive
EndEnumeration

#Win=1
#ReceiveSendbuffer=1
#MaxReceiveBuffer=1024


Procedure.l SR_OpenComPort(*RS232.structComport)
  *RS232\Handle =0
  Device.s="COM"+Str(*RS232\Port)+":"
  Result = CreateFile_(@Device, $C0000000, 0, 0, 3, 0, 0); create and open a existing device/file ;)
  If Result = -1
    MessageRequester("info", "Com Port " + Str(*RS232\Port) + " not available. Use Serial settings (on the main menu) To setup your ports.", 48)
    ProcedureReturn 0
  EndIf
  If Result
    *RS232\Handle=Result
    mydcb.DCB
    If GetCommState_(*RS232\Handle, @mydcb) ;First retrieve the Settings from ComPort
      
      ;Set Baudrate etc.
      mydcb\Baudrate=*RS232\dcb\Baudrate ;Change the Settings to our needs
      mydcb\Bytesize=*RS232\dcb\Bytesize
      mydcb\Parity=*RS232\dcb\Parity
      mydcb\StopBits=*RS232\dcb\StopBits
      Result=SetCommState_(*RS232\Handle, @mydcb) ;and write them back to Comport
      ;Debug "SetCommState="+Str(result)   
      
      Result=SetCommTimeouts_(*RS232\Handle,*RS232\ct) ;set the Timeouts set longer for problems :)
      ;Debug "SetCommTimeouts="+Str(Result)
      
      ;Set In/Outbuffers
      If result
        Result=SetupComm_(*RS232\Handle,*RS232\Inbuffersize,*RS232\Outbuffersize)
      EndIf
    EndIf
  Else
    CloseHandle_(Result)
  EndIf
  ProcedureReturn *RS232\Handle
EndProcedure

Procedure SR_CloseComPort(*RS232.structComport)
  If *RS232\Handle
    CloseHandle_(*RS232\Handle)
    *RS232\Handle=0
  EndIf
EndProcedure

Procedure SR_WriteComPort(*RS232.structComport,Rawdata.l,RawDataLength.l)
  If *RS232\Handle=0
    ProcedureReturn
  EndIf
  Written.l
  Result=WriteFile_(*RS232\Handle,RawData,RawDataLength,@Written,0)  ;WriteData now to ComPort
  FlushFileBuffers_(*RS232\Handle);Release buffer directly
  ProcedureReturn written
EndProcedure

Procedure SR_ReadComPort(*RS232.structComport)
  If *RS232\Handle=0
    ProcedureReturn
  EndIf
  Repeat
    Result=ReadFile_(*RS232\Handle , *RS232\Receivebuffer+Offset, 256, @RetBytes, 0) ;Read content
    If RetBytes>0
      Offset=Offset+RetBytes
    EndIf
  Until RetBytes=0 Or Offset>#MaxReceiveBuffer
  *RS232\ReceivebufferLength=Offset
  ; FlushFileBuffers_(*RS232\Handle);Release buffer directly
  ProcedureReturn Offset
EndProcedure


;- Begin of MainCode

MyRS232.structComport ;set my own Structure For all the ComPorthandling
MyRS232\Port=2
MyRS232\dcb\Baudrate=9600
MyRS232\dcb\Bytesize=8
MyRS232\dcb\StopBits=1 ;#ONESTOPBIT
MyRS232\dcb\Parity=#NOPARITY

MyRS232\Inbuffersize=256 ;size is enough
MyRS232\OutBuffersize=256

MyRS232\ct\ReadIntervalTimeOut =200; in milliseconds '#MAXDWORD
MyRS232\ct\ReadTotalTimeoutMultiplier = 1
MyRS232\ct\ReadTotalTimeoutConstant = 1
MyRS232\ct\WriteTotalTimeoutConstant = 10
MyRS232\ct\WriteTotalTimeoutMultiplier = 1

MyRS232\Receivebuffer=AllocateMemory(#ReceiveSendbuffer,#MaxReceiveBuffer) ;Reserve mem For it
MyRS232\ReceivebufferLength=0


;- Open Window
hwnd=OpenWindow(#Win, 1, 300, 400, 400, "PureBasic Window", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
If hwnd
  ;- CreateGadgets
  StringGadget(#Gadget_TextSend, 5, 5, 200, 20, "AT")
  EditorGadget(#Gadget_TextReceive, 5, 55, 390, 340)
  ButtonGadget(#Gadget_ButtonSend,210,5,90,20,"Send")
  ButtonGadget(#Gadget_ButtonReceive,305,5,90,20,"Receive")
  
  ;- Setup Timer
  Result=SetTimer_(hwnd,1,100,0) ;Setup our timer For receiveing Data every 100 msecs
  
  ;- Setup and Open Comport
  Result=SR_OpenComPort(MyRS232)
  
  
  ;- Eventloop
  Repeat
    EventID.l = WaitWindowEvent()
    
    ;- Timer-Routine to read Data from ComPort
    If EventID =#WM_TIMER
      If MyRS232\Handle<>0
        Readed=SR_ReadComPort(MyRS232)
        If MyRS232\ReceiveBufferlength>0
          Buffer.s=Buffer.s+PeekS(MyRS232\ReceiveBuffer,MyRS232\ReceiveBufferlength)
          SetGadgetText(#Gadget_TextReceive,Buffer.s)
          MyRS232\ReceiveBufferlength=0
        EndIf
      EndIf
    EndIf
    
    If EventID =#PB_Event_Gadget
      GadgetNR = EventGadget()
      
      ;- Send Data to Comport
      If GadgetNR =#Gadget_ButtonSend
        SendText.s=GetGadgetText(#Gadget_TextSend) + Chr(13) ;add a CarriageReturn to send to modem
        ;Debug SendText
        Buffer.s=""
        SetGadgetText(#Gadget_TextReceive,Buffer.s);Reset inbuffer gadget
        Result=SR_WriteComPort(MyRS232,@SendText.s,Len(SendText.s)); Check Modem
        Debug "Characters sended:"+Str(Result)
      EndIf
      
      ;- Read Data from Comport
      If GadgetNR=#Gadget_ButtonReceive
        Readed=SR_ReadComPort(MyRS232) ; Read Incoming
        If Readed
          Debug "Characters readed="+Str(Readed)
          Buffer.s=Buffer.s+PeekS(MyRS232\ReceiveBuffer,MyRS232\ReceiveBufferlength)
          SetGadgetText(#Gadget_TextReceive,Buffer.s)
          MyRS232\ReceiveBufferlength=0 ;Shorten buffer now
        EndIf
      EndIf
    EndIf
    
  Until EventID = #PB_Event_CloseWindow
EndIf
SR_CloseComPort(MyRS232) ;Comport Close
End 

;- Error-handler
Errorhandling:
MessageRequester("Error","in line "+Str(ErrorLine() ),0)
End
SPAMINATOR NR.1
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

rings, you're the greatest, snippets are *always* welcome :-)
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
User avatar
NoahPhense
Addict
Addict
Posts: 1999
Joined: Thu Oct 16, 2003 8:30 pm
Location: North Florida

Re: Send and receive Data from the serial ComPort , no lib

Post by NoahPhense »

Hey Rings,

I'm at work and don't have the ability to pump this into 4.0, do you think
this will go with a little conversion?

- np
User avatar
NoahPhense
Addict
Addict
Posts: 1999
Joined: Thu Oct 16, 2003 8:30 pm
Location: North Florida

Re: Send and receive Data from the serial ComPort , no lib

Post by NoahPhense »

Ok, I've converted this to 4.0.. works nice.

But I want to add in an Append ..

Code: Select all

Procedure AppendFile(FileName.s, String.s)
  If OpenFile(Fn, FileName.s) 
      FileSeek(Fn, Lof(Fn)) 
      WriteStringN(Fn, String) 
      CloseFile(Fn) 
  EndIf
EndProcedure
But when I add it in to the end of the timer loop, it doesn't work well.

Any ideas?

- np
Intrigued
Enthusiast
Enthusiast
Posts: 501
Joined: Thu Jun 02, 2005 3:55 am
Location: U.S.A.

Post by Intrigued »

Can you post the PB 4.0 version, or would you be so kind as to post such?

TIA

RS232 loop back connector - There are several, which do you guys recommend I use for testing. (otherwise I have to drag the client's register home soon, I only want to do that one night, when I do the final test, if possible ;-))
Intrigued - Registered PureBasic, lifetime updates user
User avatar
NoahPhense
Addict
Addict
Posts: 1999
Joined: Thu Oct 16, 2003 8:30 pm
Location: North Florida

Post by NoahPhense »

Intrigued wrote:Can you post the PB 4.0 version, or would you be so kind as to post such?

TIA

RS232 loop back connector - There are several, which do you guys recommend I use for testing. (otherwise I have to drag the client's register home soon, I only want to do that one night, when I do the final test, if possible ;-))
I basically just converted the code above, there were only a few keywords
that needed changing.

I had a pretty tight deadline, so I ended up using VB with Crystal Reports.

I might come back to this at a later time. Because I like the tight code
that can be used to talk with the serial port.

- np
Intrigued
Enthusiast
Enthusiast
Posts: 501
Joined: Thu Jun 02, 2005 3:55 am
Location: U.S.A.

Post by Intrigued »

Sounds good. I'm about to continue with an application and I will need to "talk with" a cash register via RS232. So any help or direction, resources you can suggest I review... would be much appreciated. I am strictly using PB 4 now for what it's worth.

TIA (and for the follow-up)

ps. I have installed the MVCOM plugin and am looking that over. It seems like it would not be to hard to work with. I will need to take data from the cash register and then add such to a SQLite 2.8 database (2.8 is what is supported in a project I am working on). Getting the data from the cash register (my first time attempting RS232) is the biggest step for me. After I get the data into a file I should be good to go and thus be able to add the data into the SQLite database and then process the data as needed.
Last edited by Intrigued on Mon Oct 30, 2006 3:06 pm, edited 1 time in total.
Intrigued - Registered PureBasic, lifetime updates user
User avatar
Rings
Moderator
Moderator
Posts: 1435
Joined: Sat Apr 26, 2003 1:11 am

Post by Rings »

holy cow, only 4 lines to change for PB4:

Code: Select all

;  ComPort (RS232) example , Write and Read-data, timer driven
;  good for a small terminal or a modem-configurator
;  Windows only, done on january, 14th. 2004
;  updated for PB4.x october 2006
;  Copyright 2004/2006 Siegfried Rings (CodeGuru)
;
OnErrorGoto(?Errorhandling) ;Enable Errorhandling (enable Linenumbering!)

;User Structure for all the ComPort-Stuff
Structure structComport
 Port.l
 Inbuffersize.l
 OutBuffersize.l
 Handle.l
 ct.COMMTIMEOUTS
 dcb.DCB
 Receivebuffer.l
 ReceivebufferLength.l
EndStructure

Enumeration
 #Gadget_TextSend
 #Gadget_TextReceive
 #Gadget_ButtonSend
 #Gadget_ButtonReceive
EndEnumeration

#Win=1
;#ReceiveSendbuffer=1
#MaxReceiveBuffer=1024


Procedure.l SR_OpenComPort(*RS232.structComport)
 *RS232\Handle =0
 Device.s="COM"+Str(*RS232\Port)+":"
 Result = CreateFile_(@Device, $C0000000, 0, 0, 3, 0, 0); create and open a existing device/file ;)
 If Result = -1
  MessageRequester("info", "Com Port " + Str(*RS232\Port) + " not available. Use Serial settings (on the main menu) To setup your ports.", 48)
  ProcedureReturn 0
 EndIf
 If Result
  *RS232\Handle=Result
  mydcb.DCB
  If GetCommState_(*RS232\Handle, @mydcb) ;First retrieve the Settings from ComPort

   ;Set Baudrate etc.
   mydcb\Baudrate=*RS232\dcb\Baudrate ;Change the Settings to our needs
   mydcb\Bytesize=*RS232\dcb\Bytesize
   mydcb\Parity=*RS232\dcb\Parity
   mydcb\StopBits=*RS232\dcb\StopBits
   Result=SetCommState_(*RS232\Handle, @mydcb) ;and write them back to Comport
   ;Debug "SetCommState="+Str(result)   

   Result=SetCommTimeouts_(*RS232\Handle,*RS232\ct) ;set the Timeouts set longer for problems :)
   ;Debug "SetCommTimeouts="+Str(Result)

   ;Set In/Outbuffers
   If result
    Result=SetupComm_(*RS232\Handle,*RS232\Inbuffersize,*RS232\Outbuffersize)
   EndIf
  EndIf
 Else
  CloseHandle_(Result)
 EndIf
 ProcedureReturn *RS232\Handle
EndProcedure

Procedure SR_CloseComPort(*RS232.structComport)
 If *RS232\Handle
  CloseHandle_(*RS232\Handle)
  *RS232\Handle=0
 EndIf
EndProcedure

Procedure SR_WriteComPort(*RS232.structComport,Rawdata.l,RawDataLength.l)
 If *RS232\Handle=0
  ProcedureReturn
 EndIf
 Written.l
 Result=WriteFile_(*RS232\Handle,RawData,RawDataLength,@Written,0)  ;WriteData now to ComPort
 FlushFileBuffers_(*RS232\Handle);Release buffer directly
 ProcedureReturn written
EndProcedure

Procedure SR_ReadComPort(*RS232.structComport)
 If *RS232\Handle=0
  ProcedureReturn
 EndIf
 Repeat
  Result=ReadFile_(*RS232\Handle , *RS232\Receivebuffer+Offset, 256, @RetBytes, 0) ;Read content
  If RetBytes>0
   Offset=Offset+RetBytes
  EndIf
 Until RetBytes=0 Or Offset>#MaxReceiveBuffer
 *RS232\ReceivebufferLength=Offset
; FlushFileBuffers_(*RS232\Handle);Release buffer directly
 ProcedureReturn Offset
EndProcedure


;- Begin of MainCode

 MyRS232.structComport ;set my own Structure For all the ComPorthandling
 MyRS232\Port=1
 MyRS232\dcb\Baudrate=9600
 MyRS232\dcb\Bytesize=8
 MyRS232\dcb\StopBits=1 ;#ONESTOPBIT
 MyRS232\dcb\Parity=#NOPARITY
 
 MyRS232\Inbuffersize=256 ;
 MyRS232\OutBuffersize=256
 
 MyRS232\ct\ReadIntervalTimeOut =200; in milliseconds '#MAXDWORD
 MyRS232\ct\ReadTotalTimeoutMultiplier = 1
 MyRS232\ct\ReadTotalTimeoutConstant = 1
 MyRS232\ct\WriteTotalTimeoutConstant = 10
 MyRS232\ct\WriteTotalTimeoutMultiplier = 1
   
 MyRS232\Receivebuffer=AllocateMemory(#MaxReceiveBuffer) ;Reserve mem For it
 MyRS232\ReceivebufferLength=0


;- Open Window
hwnd=OpenWindow(#Win, 1, 300, 400, 400,"PureBasic SerialPortDemo Window", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
If hwnd
 If CreateGadgetList(WindowID(#Win))

  ;- CreateGadgets
  StringGadget(#Gadget_TextSend, 5, 5, 200, 20, "AT")
  ;StringGadget(#Gadget_TextReceive, 5, 55, 390, 340, "",#PB_String_Multiline  )
  EditorGadget(#Gadget_TextReceive,5, 55, 390, 340)
  ButtonGadget(#Gadget_ButtonSend,210,5,90,20,"Send")
  ButtonGadget(#Gadget_ButtonReceive,305,5,90,20,"Receive")

  ;- Setup Timer
  Result=SetTimer_(hwnd,1,100,0) ;Setup our timer For receiveing Data every 100 msecs

  ;- Setup and Open Comport
  Result=SR_OpenComPort(MyRS232)
 
 
  ;- Eventloop
  Repeat
   EventID.l = WaitWindowEvent()
 
   ;- Timer-Routine to read Data from ComPort
   If EventID =#WM_TIMER
     If MyRS232\Handle<>0
       Readed=SR_ReadComPort(MyRS232)
      If MyRS232\ReceiveBufferlength>0
        Buffer.s=Buffer.s+PeekS(MyRS232\ReceiveBuffer,MyRS232\ReceiveBufferlength)
        SetGadgetText(#Gadget_TextReceive,Buffer.s)
        MyRS232\ReceiveBufferlength=0
      EndIf
     EndIf
   EndIf
 
   If EventID =#PB_Event_Gadget
     GadgetNR = EventGadget()
     
     ;- Send Data to Comport
     If GadgetNR =#Gadget_ButtonSend
      SendText.s=GetGadgetText(#Gadget_TextSend) + Chr(13) ;add a CarriageReturn to send to modem
      ;Debug SendText
      Buffer.s=""
      SetGadgetText(#Gadget_TextReceive,Buffer.s);Reset inbuffer gadget
      Result=SR_WriteComPort(MyRS232,@SendText.s,Len(SendText.s)); Check Modem
      Debug "Characters sended:"+Str(Result)
     EndIf

     ;- Read Data from Comport
     If GadgetNR=#Gadget_ButtonReceive
      Readed=SR_ReadComPort(MyRS232) ; Read Incoming
      If Readed
       Debug "Characters readed="+Str(Readed)
       Buffer.s=Buffer.s+PeekS(MyRS232\ReceiveBuffer,MyRS232\ReceiveBufferlength)
       SetGadgetText(#Gadget_TextReceive,Buffer.s)
       MyRS232\ReceiveBufferlength=0 ;Shorten buffer now
      EndIf
     EndIf
   EndIf
   
  Until EventID = #PB_Event_CloseWindow
 EndIf
EndIf
SR_CloseComPort(MyRS232) ;Comport Close
End 

;- Error-handler
Errorhandling:
 MessageRequester("Error","in line "+Str(GetErrorLineNR() ),0)
 End
SPAMINATOR NR.1
Intrigued
Enthusiast
Enthusiast
Posts: 501
Joined: Thu Jun 02, 2005 3:55 am
Location: U.S.A.

Post by Intrigued »

Thanks Rings for your time on this one. Now I just need to take the guy's register home where I can practice. I can see it now... my neighbors saying (as I bring the register into my house), "doesn't he know this is not a commercially zoned area?".

(and thanks to MV's Mark for the condensed sample code offering he offered in another thread, for reading, writing the RS232 data!)

:wink:
Intrigued - Registered PureBasic, lifetime updates user
Marc
User
User
Posts: 28
Joined: Mon Jun 16, 2003 9:02 pm
Location: Paris - Villemer
Contact:

Post by Marc »

If you need some help, please contact me...

Regards
Marc from PARIS - MVCOM Library
Intrigued
Enthusiast
Enthusiast
Posts: 501
Joined: Thu Jun 02, 2005 3:55 am
Location: U.S.A.

Post by Intrigued »

Marc wrote:If you need some help, please contact me...

Regards
Thank you very much Mark. I may need too. I'm just waiting for my client to get the specific serial adapter cable for his Register. I'll post back how I'm coming along, or if I get stuck.

Thanks again, it's really appreciated!
Intrigued - Registered PureBasic, lifetime updates user
Marc
User
User
Posts: 28
Joined: Mon Jun 16, 2003 9:02 pm
Location: Paris - Villemer
Contact:

Post by Marc »

You may also need the Communication Specification (Protocol) of the Register.

Regards
Marc from PARIS - MVCOM Library
Intrigued
Enthusiast
Enthusiast
Posts: 501
Joined: Thu Jun 02, 2005 3:55 am
Location: U.S.A.

Post by Intrigued »

Marc wrote:You may also need the Communication Specification (Protocol) of the Register.

Regards
I'll check his manual (he printed off the .PDF) for this information. Thanks for the headsup. I hope he gets that cable in... I'm ready to roll.
Intrigued - Registered PureBasic, lifetime updates user
Lee87073
New User
New User
Posts: 4
Joined: Sun May 22, 2005 8:57 am
Location: Stourport On Severn Worcestershire

Question

Post by Lee87073 »

On trying this example recieved chars not in ascii ,is that unicode?, viewed my device with hyperterminal, then example recieves in ascii
have tried PB_Ascii flag on PeekS,still have to coneect with hyperterminal first to get required ascii?
User avatar
NoahPhense
Addict
Addict
Posts: 1999
Joined: Thu Oct 16, 2003 8:30 pm
Location: North Florida

Post by NoahPhense »

Thanks Rings..

I even got me a USB to Serial cable for testing. Cause my new LT doesn't
have the serial ports.. ;)

- np
Post Reply