Serial Communications via API

Linux specific forum
larry
New User
New User
Posts: 9
Joined: Fri Sep 02, 2005 7:31 pm

Serial Communications via API

Post by larry »

Well, until some native support is provided, I am trying to implement serial communications via API functions. I am close, but have one problem. First I open the port with:
fd = open_("/dev/ttyS0", #O_NONBLOCK | #O_NOCTTY | #O_RDWR , 0)
I can write quite successfully with: (can see time on receving computer)
n=write_(fd,FormatDate("%hh:%ii:%ss",Date())+Chr(10),9)
But this read always returns -1:
n=read_(fd,@buf,1)

I set miscellaneous port parameters with stty. I can receive data, character by character, in a terminal by cat "/dev/ttyS0" . Why can I receive data in terminal but not in my program? Thanks for your help :!:
larry
New User
New User
Posts: 9
Joined: Fri Sep 02, 2005 7:31 pm

Post by larry »

Got it! :D
Even with O_RDWR, you need cread in stty command. It is reading and writing perfectly. I hope this helps everyone who has been looking for serial communications.
Beach
Enthusiast
Enthusiast
Posts: 677
Joined: Mon Feb 02, 2004 3:16 am
Location: Beyond the sun...

Post by Beach »

Great! Could you please post a complete example? I would like to fool around with my X-10 firecracker, which has a serial interface.
-Beach
larry
New User
New User
Posts: 9
Joined: Fri Sep 02, 2005 7:31 pm

Post by larry »

Here is a pretty simple example. Set your port parameters in the stty command. Good Luck 8)

Code: Select all

;
; ------------------------------------------------------------
;
;   PureBasic - Serial Communications
;   Larry Duarte
;
; ------------------------------------------------------------
;

#O_ACCMODE =    03    
#O_RDONLY  =    00
#O_WRONLY  =    01
#O_RDWR    =    02
#O_CREAT   =  0100 
#O_EXCL    =  0200 
#O_NOCTTY  =  0400 
#O_TRUNC   =  01000 
#O_APPEND  =  02000
#O_NONBLOCK =  04000
#O_NDELAY   =  #O_NONBLOCK
#O_SYNC     =  010000
#O_FSYNC    =  #O_SYNC
#O_ASYNC    =  020000

buf.s=Space(5)
out.s=Space(10)

If OpenWindow(0, 100, 100, 195, 265, #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget, "Serial Communications")

  CreateGadgetList(WindowID(0))
  TextGadget(3,10,50,150,20,"Receive Data")
  EditorGadget(1,10,70,150,100)
  TextGadget(4,10,180,150,20,"Send Data")
  StringGadget(2,10,200,150,20,"",#PB_Text_Center)  
  
  RunProgram("stty","-F "+"/dev/ttyS0"+" cread -echo -icanon","")
  fd = open_("/dev/ttyS0", #O_NONBLOCK | #O_NOCTTY | #O_RDWR , 0)
 
  Repeat
    EventID.l = WindowEvent()

    If ( sec<>Second(Date()))
      sec=Second(Date())
      out=FormatDate("%hh:%ii:%ss",Date())+Chr(10)
      n=write_(fd,out,9)
      If (n < 0)
        SetGadgetText(2,"write() of 4 bytes failed! "+ Str(stderr))
      Else
        SetGadgetText(2,out)
      EndIf
   EndIf   
    
    n=read_(fd,buf,1)
    If (n > 0)
      SetGadgetText(1,GetGadgetText(1)+Left(buf,1))
    EndIf
    
    If EventID = #PB_EventCloseWindow  
          Quit = 1
    EndIf

  Until Quit = 1

close_(fd) 

EndIf

End
User avatar
dhouston
Enthusiast
Enthusiast
Posts: 430
Joined: Tue Aug 21, 2007 2:44 pm
Location: USA (Cincinnati)
Contact:

Post by dhouston »

I tried the example but had some problems.

1. The constants appear to have come from /usr/include/bits/fcntl.h which uses octal. I don't think PB can digest octal so it saw them as decimal.

2. The title and flags are reversed in the OpenWindow call.

3. There's an underscore missing from #PB_EventCloseWindow

After fixing those, it works. Thanks for the roadmap.

I modified it to read the port name from a preference file. Here's a tested example.

Code: Select all

;
; ------------------------------------------------------------
;
;   PureBasic - LINUX Serial Communications 
;   based on post to PureBasic Linux Forum by Larry Duarte
;
; ------------------------------------------------------------
;
EnableExplicit

#O_RDONLY   = 0
#O_WRONLY   = 1
#O_RDWR     = 2
#O_NOCTTY   = $100
#O_NONBLOCK = $800
#TIOCMGET   = $5415
#TIOCMSET   = $5418
#SET_DTR    = %000000000010    ;$002
#CLR_DTR    = %111111111101    ;~#SET_DTR
#WNDW_MAIN  = 0
#EDT_RCV    = 1
#STR_TX     = 2
#DTR_LED    = 3
#STATUS     = 4

Define.s buf=Space(5),out=Space(10),dev
Define.l EventID,sec,n,Quit,returncode,fd,flags

If OpenWindow(#WNDW_MAIN,100,100,188,265,"Serial Comms",#PB_Window_MinimizeGadget|#PB_Window_ScreenCentered)

  CreateGadgetList(WindowID(#WNDW_MAIN))
  TextGadget(#PB_Any,10,50,150,20,"Receive Data")
  EditorGadget(#EDT_RCV,10,70,168,100)
  TextGadget(#PB_Any,10,180,150,20,"Send Data")
  StringGadget(#STR_TX,10,200,168,20,"",#PB_Text_Center) 
  
  ;get dev from preferences file
  OpenPreferences("serial.prf"):PreferenceGroup("port")
    dev=ReadPreferenceString("dev","/dev/ttyS0")
  ClosePreferences()
  TextGadget(#PB_Any,10,5,200,22,dev)
  TextGadget(#PB_Any,10,30,50,22,"DTR"):TextGadget(#DTR_LED,40,30,18,18,"",#PB_Text_Border)
  TextGadget(#PB_Any,65,30,50,22,"Flags"):TextGadget(#STATUS,100,30,77,22,RSet(Bin(flags),9,"0"),#PB_Text_Border)
  RunProgram("stty","-F "+dev+" 19200 cread -echo -icanon","")
  fd=open_(dev,#O_NONBLOCK|#O_NOCTTY|#O_RDWR,0)
  
  If (fd<0) 
    MessageRequester("ERROR!","Unable to open "+dev)
  Else
    Repeat
    
      EventID=WindowEvent()

      If (sec<>Second(Date())) 
        sec=Second(Date())
        out=FormatDate("%hh:%ii:%ss",Date())+Chr(10)
        n=write_(fd,out,9)
        If (n<0)
          SetGadgetText(#STR_TX,"Write() failed! ")
        Else
          SetGadgetText(#STR_TX,Left(out,8))
        EndIf
        returncode=ioctl_(fd,#TIOCMGET,@flags) 
        SetGadgetText(#STATUS,RSet(Bin(flags),9,"0"))
        If (flags & 2)=#SET_DTR
          flags & #CLR_DTR
          SetGadgetColor(#DTR_LED,#PB_Gadget_BackColor,RGB(0,255,0))
        Else  
          flags | #SET_DTR
          SetGadgetColor(#DTR_LED,#PB_Gadget_BackColor,RGB(255,255,255))
        EndIf  
        returncode=ioctl_(fd,#TIOCMSET,@flags)
      EndIf   
   
      n=read_(fd,buf,1)
      If (n > 0)
        SetGadgetText(#EDT_RCV,GetGadgetText(#EDT_RCV)+Left(buf,1))
      EndIf
   
      If EventID = #PB_Event_CloseWindow 
        Quit = 1
      EndIf
            
    Until Quit=1
    close_(fd)
  
  EndIf

EndIf

End
Last edited by dhouston on Fri Feb 08, 2008 9:24 pm, edited 2 times in total.
User avatar
dhouston
Enthusiast
Enthusiast
Posts: 430
Joined: Tue Aug 21, 2007 2:44 pm
Location: USA (Cincinnati)
Contact:

Post by dhouston »

I need to toggle the DTR line to put the embedded hardware I'm working with into download mode. Someone gave me some C code for this. I'm hoping someone will be able to translate it to PB.

Code: Select all

#include <sys/ioctl.h>
int fd;
int status; /* a bitmap of the modem status line states */

/* fd = open(/dev/ttyS0, etc */

/* Get the existing status */
retcode = ioctl(fd, TIOCMGET, &status);
if ( retcode < 0 ) {
printf("Error!\n");
exit(1);
}
/* To raise DTR */
status = status | TIOCM_DTR;

/* Or to lower DTR */
status = status & ~TIOCM_DTR;

/* Apply the new status */
retcode = ioctl(fd, TIOCMSET, &status);
I found values for the constants. Here's my stab at a translation.

Code: Select all

  #TIOCMGET   = $5415
  #TIOCMSET   = $5418
  #SET_DTR    = %000000000010    ;$002
  #CLR_DTR    = %111111111101    ;~$002

  Define.s dev="/dev/ttyUSB1"
  Define.l fd
  Define.w status
  Define *status=@status 

  RunProgram("stty","-F "+dev+" 19200 cread -echo -icanon","")
  fd=open_(dev,#O_NONBLOCK|#O_NOCTTY|#O_RDWR,0)
    
  returncode=ioctl_(fd,#TIOCMGET,*status)
  If (status & 2)=#SET_DTR
    status & #CLR_DTR
  Else  
    status | #SET_DTR
  EndIf  
  returncode=ioctl_(fd,#TIOCMSET,*status)
It appears to read status but does not set it.

EDIT: The code above ran in the IDE but crashed when compiled.

Changing...

Code: Select all

  Define.w status
  Define *status=@status
to...

Code: Select all

  Define.w status
and passing @status instead of *status fixed it. I'm not sure why.

I've edited my earlier post (above) showing how to configure, open, read, write, close a serial port with this working code for DTR.
hellhound66
Enthusiast
Enthusiast
Posts: 119
Joined: Tue Feb 21, 2006 12:37 pm

Post by hellhound66 »

Removed.
FrankW
New User
New User
Posts: 1
Joined: Sun Feb 01, 2009 5:23 pm

Post by FrankW »

Hallo,

I'm totally new to Pure Basic.
I found it yesterday an tried it because I thought it would be a great thing to make tools and little programms I need for Linux AND Windows.

Unfortunaltely there is this problem with the serial port und linux :-(

I found this thread and tried it - with no success.
When compiling the example from above, my compiler complains, that it does not know open_ read_ and so on.

What do I have to do to make read_(...) open_(..) etc. available ?

Thank's
Frank
User avatar
dhouston
Enthusiast
Enthusiast
Posts: 430
Joined: Tue Aug 21, 2007 2:44 pm
Location: USA (Cincinnati)
Contact:

Post by dhouston »

I have added to Larry's basic code and posted it to http://www.purebasic.fr/english/viewtop ... 978#289978. It should show you what you need to do. AFAIK, the native PB serial port functions still do not work but the exam[le shows alternative methods that do work.
User avatar
dhouston
Enthusiast
Enthusiast
Posts: 430
Joined: Tue Aug 21, 2007 2:44 pm
Location: USA (Cincinnati)
Contact:

Post by dhouston »

I have updated my code to handle another variation in how dmesg lists ports for Moxa multi-port boards. The --parse-tty.txt-- section could be cleaner but I anticipate there will be other dmesg variations.

Code: Select all

;------------------------------------------------------------------------------------------------------
;
;   PureBasic - LINUX Serial Communications
;   based on post to PureBasic Linux Forum by Larry Duarte
;     http://www.purebasic.fr/english/viewtopic.php?t=16847&highlight=linux+serial
;
; -------------------------------------------------------------------------------------------------------
;
EnableExplicit

#O_RDONLY   = 0
#O_WRONLY   = 1
#O_RDWR      = 2
#O_NOCTTY   = $100
#O_NONBLOCK = $800
#TIOCMGET   = $5415
#TIOCMSET   = $5418
#SET_DTR    = %000000000010    ;$002
#CLR_DTR    = %111111111101    ;~#SET_DTR
#WNDW_MAIN  = 0
#EDT_RCV    = 1
#STR_TX     = 2
#DTR_LED     = 3
#STATUS =4
#CBO_PORTS=5
#PB_SerialPort_NoParity=0
#PB_SerialPort_NoHandshake=0

Procedure ScrollToEnd(gadget)              ;scrolls text window to last line
  Protected end_mark,*buffer, end_iter.GtkTextIter
  *buffer=gtk_text_view_get_buffer_(GadgetID(gadget))
  gtk_text_buffer_get_end_iter_(*buffer,@end_iter)
  ;gtk_text_buffer_place_cursor_(*buffer,@end_iter)
  end_mark=gtk_text_buffer_create_mark_(*buffer,"end_mark",@end_iter,#False)
  gtk_text_view_scroll_mark_onscreen_(GadgetID(gadget),end_mark)
EndProcedure

Define.s buf=Space(5),out=Space(10),dev,Pid,output,LineIn,found,ports,key,bad
Define.l EventID,sec,i,n,Quit,returncode,fd,flags,ps,dmesg,fgrep,tee,t,s,u,c,ComID,mode,*buffer
*buffer=AllocateMemory(10)

mode=1  ;0=Linux API, 1=Mattias Groß library. 2=Native PB

If OpenWindow(#WNDW_MAIN,100,100,188,235,"SerialTest",#PB_Window_MinimizeGadget|#PB_Window_ScreenCentered)

  TextGadget(#PB_Any,10,50,150,20,"Receive Data")
  EditorGadget(#EDT_RCV,10,70,168,100)
  TextGadget(#PB_Any,10,180,150,20,"Send Data")
  StringGadget(#STR_TX,10,200,168,20,"",#PB_Text_Center)
  TextGadget(#PB_Any,10,4,200,22,"Port:")
  ComboBoxGadget(#CBO_PORTS,42,0,135,27)
  TextGadget(#PB_Any,10,33,50,22,"DTR"):TextGadget(#DTR_LED,40,36,15,15,"",#PB_Text_Border)
  TextGadget(#PB_Any,65,33,50,22,"Flags"):TextGadget(#STATUS,100,33,77,22,RSet(Bin(flags),9,"0"),#PB_Text_Border)
  ;-----get PID-----ONLY WORKS FOR COMPILED APPLICATION
  ps=RunProgram("/bin/ps", "-A","",#PB_Program_Open|#PB_Program_Read)
  fgrep=RunProgram("/bin/fgrep", "linux-serial","",#PB_Program_Open|#PB_Program_Connect|#PB_Program_Read,ps)
  tee = RunProgram("/usr/bin/tee","PID.txt",GetCurrentDirectory(),#PB_Program_Open|#PB_Program_Connect|#PB_Program_Read,fgrep)
  While ProgramRunning(tee)
    Pid=StringField(Trim(ReadProgramString(tee)),1," ")
  Wend
  WaitProgram(tee)
  CloseProgram(ps):CloseProgram(tee)
  ;-----enum ports-----
  DeleteFile("tty.txt")
  dmesg = RunProgram("/bin/dmesg","","",#PB_Program_Open|#PB_Program_Read)
  fgrep = RunProgram("/bin/fgrep","tty","",#PB_Program_Open|#PB_Program_Connect|#PB_Program_Read,Dmesg)
  tee = RunProgram("/usr/bin/tee","tty.txt",GetCurrentDirectory(),#PB_Program_Open|#PB_Program_Connect|#PB_Program_Read,Fgrep)
  WaitProgram(tee)
  CloseProgram(Dmesg):CloseProgram(Fgrep):CloseProgram(Tee)
  ;-----parse tty.txt-----
  ports=""
  If ReadFile(0,"tty.txt")
    While Eof(0) = 0 
      LineIn=ReadString(0)+" "
      t=FindString(LineIn,"tty",1)
      If t>0
        s=FindString(LineIn," ",t)
        dev=Trim(Mid(LineIn,t,s-t))     
        If Right(dev,1)=":":dev=Left(dev,Len(dev)-1):EndIf
        If Not IsNumeric(Mid(dev,4,1))
          c=CountString(LineIn,"tty")
          If c=1      
            RunProgram("stty","-F /dev/"+dev+" 19200 cread -echo -icanon","")
            fd=open_("/dev/"+dev,#O_NONBLOCK|#O_NOCTTY|#O_RDWR,0)  
            If fd>0
              If FindString(ports,dev,1)=0
                If Len(ports):ports+",":EndIf
                ports+"/dev/"+dev
              EndIf  
              close_(fd)
            EndIf             
          ElseIf (c=2) 
            s=Val(Mid(dev,5,2))                                     ;first index
            t=FindString(LineIn,"tty",t+Len(dev))                   ;find second tty?xx
            c=Val(Mid(LineIn,t+4,2))                                ;second index
            If (c>s)
              For i=s To c
                dev=Left(dev,4)+Str(i)       
                RunProgram("stty","-F /dev/"+dev+" 19200 cread -echo -icanon","")
                fd=open_("/dev/"+dev,#O_NONBLOCK|#O_NOCTTY|#O_RDWR,0)  
                If fd>0
                  If FindString(ports,dev,1)=0
                    If Len(ports):ports+",":EndIf
                    ports+"/dev/"+dev
                  EndIf  
                  close_(fd)
                EndIf                 
              Next
            ElseIf FindString(LineIn,"-",1)>0
              MessageRequester("Serial Port Enumeration","Unexpected dmesg format - Copy tty.txt to roZetta author"+#CRLF$+LineIn)
            EndIf
          Else
            MessageRequester("Serial Port Enumeration","Unexpected dmesg format - Copy tty.txt to roZetta author"+#CRLF$+LineIn)
          EndIf  
        EndIf
      EndIf
    Wend
    CloseFile(0)
  EndIf 
  ;-----lock file----
  OpenPreferences("serial.prf"):PreferenceGroup("port")
    WritePreferenceString("ports",ports)
    dev=ReadPreferenceString("dev","NONE")  ;ADD THE dev KEY MANUALLY
  ClosePreferences()
  SetGadgetText(#CBO_PORTS,dev)
  Select mode
    Case 0
      RunProgram("stty","-F "+dev+" 19200 cread -echo -icanon","")
      fd=open_(dev,#O_NONBLOCK|#O_NOCTTY|#O_RDWR,0)
    Case 1
      fd=ComOpen(dev+":19200,N,8,1",0,1024,1024)
    Case 2
      fd=OpenSerialPort(#PB_Any,"/dev/"+dev,19200,#PB_SerialPort_NoParity,8,1,#PB_SerialPort_NoHandshake,1024,1024)
  EndSelect   
  If (fd<1)
   MessageRequester("ERROR!","Unable to open "+dev)
  Else
    Repeat
      EventID=WindowEvent()
      If (sec<>Second(Date()))
        sec=Second(Date())
        out=FormatDate("%hh:%ii:%ss",Date())+Chr(10)
        Select mode
          Case 0
            n=write_(fd,out,9)
            returncode=ioctl_(fd,#TIOCMGET,@flags)
            SetGadgetText(#STATUS,RSet(Bin(flags),9,"0"))
            If (flags & 2)=#SET_DTR
              flags & #CLR_DTR
              SetGadgetColor(#DTR_LED,#PB_Gadget_BackColor,RGB(255,0,0))
            Else 
              flags | #SET_DTR
              SetGadgetColor(#DTR_LED,#PB_Gadget_BackColor,RGB(0,255,0))
            EndIf 
            returncode=ioctl_(fd,#TIOCMSET,@flags)       
          Case 1
            n=ComOutput(fd,out)
            If sec % 2 = 1
              ComSetDTR(fd,1)
              SetGadgetColor(#DTR_LED,#PB_Gadget_BackColor,RGB(255,0,0))
            Else 
              ComSetDTR(fd,0)
              SetGadgetColor(#DTR_LED,#PB_Gadget_BackColor,RGB(0,255,0))
            EndIf 
          Case 2
            n=WriteSerialPortString(fd, out)
        EndSelect   
        If (n<1)
          SetGadgetText(#STR_TX,"Write() failed! ")
        Else
          SetGadgetText(#STR_TX,Left(out,8))
        EndIf
      EndIf
   
      Select mode
        Case 0
          n=read_(fd,buf,1)
        Case 1
          n=ComInput(fd,buf)         
        Case 2
          If AvailableSerialPortInput(fd)
            n=AvailableSerialPortInput(fd)
            n=ReadSerialPortData(fd, *buffer, n)
            buf=""
            For i=0 To n-1
              buf=buf+PeekS(*buffer+i)
            Next
          EndIf       
      EndSelect   
      If (n > 0)
        SetGadgetText(#EDT_RCV,GetGadgetText(#EDT_RCV)+Left(buf,1))
        ScrollToEnd(#EDT_RCV)
      EndIf
      If EventID = #PB_Event_CloseWindow
        Quit = 1
      EndIf
    Until Quit=1
    close_(fd)
  EndIf
EndIf

End
Post Reply