ARTICLE : Serial Communication with Liberty Basic
by Dean Jolly

Home

Dumping a file directly into a texteditor
Three items that caught my fancy recently...
Serial Communication with LB
David Drake reviews Image321.DLL
Translating Documentation
Implementing an editor with Scintilla.dll

[Ed: Dean points out that the material in this article is not aimed at beginners who are just starting out using the serial port. I wanted to point out two other excellent resources if you fall into that category, use them as primers to serial communications and they will make a great foundation on which to launch into the following article by Dean.

1) Serial Communications by Herman in Newsletter 49
2) Serial Primer by Neil Trimblay - available at the following URL - http://lbdev.5u.com/

Enjoy the article.]

Serial Communication with Liberty Basic

Abstract

Attempting to communicate with a serial device, other than a modem,(such as an I/O board) using the standard LB2 commands gives at best poor or unreliable results. Far better results are obtained using LB3! Under LB2, the most common problem being a delay between sending a command to the device and having the action performed ( such as toggling an output). This delay ranges from several hundred milliseconds to several seconds. An other and more equally serious problem is that reading the port sometimes freezes the system. It appears that these problems are caused by the interaction of LB2 and Windows. The solution to the problems is to call the communication functions in the USER DLL.


Communicating using the DLL with LB2 (after going through all of this I can guarantee you will want to do it with LB3 )

To communicate using the available functions requires several steps

1- create an instance of the needed DCB structure
2- open the User.DLL
3- build the DCB
4- open the comm port
5- read or write to the port
6- close the port when done
7- close the DLL

For those of you still using LB2, it looks like this: NB You must do things in this order!

*1- create an instance of the DCB structure ( very long structure )

 struct myDCB,_
     ID as char[1],_
     BaudRate as ushort,_
     ByteSize as char[1],_
     Parity as char[1],_
     StopBits as char[1],_
     RlsTimeOut as ushort,_
     CtsTimeOut as ushort,_
     DsrTimeOut as ushort,_
     fBinary as ushort,_
     fRtsDisable as ushort,_
     fParity as ushort,_
     fOutxCtsFlow as ushort,_
     fOutxDsrFlow as ushort,_
     fDummy as ushort,_
     fDtrDisable as ushort,_
     fOutX as ushort,_
     fInX as ushort,_
     fPeChar as ushort,_
     fNull as ushort,_
     fChEvt as ushort,_
     fDtrflow as ushort,_
     fRtsflow as ushort,_
     fDummy2 as ushort,_
     XonChar as char[1],_
     XoffChar as char[1],_
     XonLim as char[1],_
     XoffLim as char[1],_
     PeChar as char[1],_
     EofChar as char[1],_
     EvtChar as char[1],_
     TxDelay as ushort
     ' end of structure

'*2- open dll
     open "user" for dll as #user
     
'*3- call OpenComm 'opens port
     com$ = "COM1"+chr$(0)
 CallDll #user,"OpenComm",_
     com$ as ptr,_
     1024 as ushort,_ 'output buffer size
     128 as ushort,_ 'input buffersize
     idComDev as ushort 'this id is used everytime the
     'port is accessed
     
     '*4- build DCB 
     buildCom$ = "COM1:9600,n,8,1"+chr$(0) 'required port settings
 CallDll #user,"BUILDCOMMDCB",_
     buildCom$ as ptr,_
     myDCB as struct,_ ' defined above
     Bresult as ushort ' if negative = error
 if Bresult < 0 then
     
     initport$="error" ' variable used to check for error
     else
     initport$ = "OK"
     end if
     myDCB.fNull.struct = 1 ' specifies that null char 
     'are to be discarded
     '*5- set com state must do this before using the port...
CallDll #user,"SETCOMMSTATE",_
     myDCB as struct,_
     setResult as ushort ' if negative = error
     if setResult < 0 then
     
     initport$ = "error"
     else
     initport$="OK"
     end if
     '* >> serial port COM1 is now ready to communicate <<


After doing all of this, you still haven't really done anything with the port here are the subroutines to write, read and flush the port

'*6a- Writting to the port is done this way :
   
'************************************
     '* [write] *
     '* sub #1 write to port *
     '* uses User.dll function WriteComm *
     '* var *
     '* write.Length, write.String$ *
     '* returns write.result *
     '************************************
     [write]
     write.Length = 0 ' reset this to 0
 write.String$ = "anything you want to send to the port"
 write.Length = len(write.String$) ' calc length
 CallDll #user,"WriteComm",_
     idComDev as ushort,_ ' obtained fromn opencomm
     write.String$ as ptr,_ ' string sent to port
     write.Length as ushort,_ ' number of bytes to send
     write.Result as ushort ' number of bytes sent
     if write.Result <> write.Length then
     
     Notice "Error"+chr$(13)+"problems writting to port"
     end if
 write.String$ = "" ' reset
     return
     '6b -And Reading the port is done as follows:
     '************************************
     '* [read] *
     '* sub #2 read port *
     '* uses User.dll function readComm *
     '* var *
     '* read.Buffer$ *
     '* read.Bytes *
     '* read.Nb *
     '* read.Txt$ * 
     '************************************
     [read]
 read.Buffer$= space$(128) + chr$(0) ' buffer for reading port
     read.Bytes = 2 ' number of bytes to read 
   
 read.Nb = 0 ' number of bytes actually 
     ' read
     read.Txt$ = "" ' result of reading port
 ' loop until there is something in the buffer i.e read.Nb > 0
     while read.Nb = 0
     CallDll #user,"readComm",_
     idComDev as ushort,_ 'obtained from opencomm
     read.Buffer$ as ptr,_ ' read buffer
     read.Bytes as ushort,_ ' number of bytes to read
     read.Nb as ushort ' number of bytes read
 ' for d = 1 to 1000
     ' next d
     ' small delay; may (or may not) be necessary
     wend
     read.Txt$ = Trim$(read.Buffer$) ' remove blanks,Chr$(13)& 
     ' Chr$(0)
 return

6c) flushing the port buffers: Often used before reading the port

'************************************
     '* [flush] *
     '* sub #3 flush port buffer *
     '* uses User.dll function readComm *
     '* var *
     '* flush.Q * 
     '************************************
' flushes queues i.e serial port buffers
     [flush]
     fnQueue = flush.Q '0 = transmission
     '1 = receiving queue
 CallDll #user,"FlushComm",_
     idComDev as ushort,_
     fnQueue as ushort,_
     flushResult as ushort ' 0 if ok
   
 if flushResult <> 0 then
     Notice " Error" + chr$(13) + _
     "problem flushing queue"+str$(flushQ)
     end if


Finally the port must be closed before exiting the program

'7-Finally closing the port 
 CallDll #user,"CloseComm",_
     idComDev as ushort,_
     result as void
     
     close #user 

Now lets look at things under LB3!

Carl has done us a favor, by building into LB3 most of the work for the serial port. As described in the new documentation, LB3 uses Windows communication API.

With LB3 there are only 4 steps needed to use the serial port!

Before using the port you may want to change the size of the buffers. This must be done before opening the port. Just set Com variable to the size you want i.e Com = 8096 ( 8k)

1- Open port

 Open "COMn:baud,parity,data,stop,CS,Ds,PE,RS" for random as #com

For I/O boards I have found that the following usually works:

 Open "COM1:9600,N,8,1,CS0,DS0,RS"

i.e baud rate 9600, No parity, data length 8 bits, 1 stop bit both the CTS and DSR should be set to zero the default is 1000 millisecond time out, this often causes the program to hang. With the io boards that I use, I have found that only CS0 and DS0 work. and finally the request to send (RS) is disabled


2- Write to the port: Nothing could simpler, just print to it !

 print #com,message$

3- Reading the port: Only a few more lines of code are needed

 
     while lof(#com) < NbofBytesToRead
     'do nothing
     wend
     readport$ = input$(#com,lof(#com)) 

4- Finally closing the port

 Close #com

In the lab that I work, I use IO boards from ONTRAK control systems. The boards are called ADRs. They have 8 relay outputs, 4 contact or TTL inputs and an input that can count events. The following is the bare bones program ( no gui )that I use to test the boards

To help me track what's going on with the boards and/or com errors, every step concatenates the LOG$.

'Progarm ADR
Log$ = ""
open "com1:9600,n,8,1,cs0,ds0,rs" for random as #com
     Log$ = "Opening com1"+chr$(13)
     oncomerror [error]
ADR$="2" 'board address from 0 to 9 change as needed
print " all relays being set"
     for R = 0 to 7
     f$= setRelay$(ADR$,R) : print R;f$
     for t = 1 to 2000: next t
     next R
     'test read
     print " reading port with interrupt request"
     s= setIE(ADR$)
     result$=readADR$(ADR$)
 print "board : ";left$(result$,1)
     print "input : ";mid$(result$,2)
     print " resetting all relays"
     for R = 0 to 7
     r$ = resetRelay$(ADR$,R)
     print R;r$
     for t = 1 to 2000 : next t
     next R

  
     print " event counter test "
     print " a) clearing counter"
     c = clearCounter(ADR$)
print" b) setting counter to 5"
     sc = setCounter(ADR$,5)
     print" c) please trigger 5 times"
     print " waiting for counter interrupt... "
     dat$ = readCounter$(ADR$)
     print" >> counter toggled <<"
     print" board nb "; left$(dat$,1)
     print" input nb "; mid$(dat$,2,1)
goto [quit]
[error]
 print "comm problem";ComError$ 
     Log$ = Log$+ComError$+chr$(13)
     
     goto [quit]

  
[quit]
' save Log$ incase of bugs or problems
 Open "LogBook" for output as #lg
     print #lg,Log$
     close #lg
print " all done "
     close #com
     End
' sets i.e closes relay
     function setRelay$(board$,relay)
 message$=board$+"SK"+str$(relay)+chr$(13)
     print #com,message$
     
     setRelay$ = "true" 
     Log$ = Log$+message$+chr$(13) 
     
     
     end function
'resets opens relay
     function resetRelay$(board$,relay)
     message$=board$+"RK"+str$(relay)+chr$(13)
     print #com,message$
     
     resetRelay$ = "true" 
     Log$ = Log$+message$+chr$(13)
     end function
'sets interrupt
     function setIE(board$)
     it$ = board$+"IE"+chr$(13)
     print #com,it$
     setIE=1
     Log$ = Log$+"interrupt enabled"+chr$(13)
     end function
'reads io board
function readADR$(board$)
 while lof(#com) < 3 ' the ADR always sends back 3 digits
     wend
     readADR$ = input$(#com,lof(#com))
     Log$ = Log$+"read board"+chr$(13) 
     end function
'clears 16bit counter
function clearCounter(board$)
 ct$ = board$+"CE"+chr$(13)
     print #com,ct$
     clearCounter = 1
     Log$=Log$+"counter cleared"+chr$(13)
     end function
'sets counter to specified value
function setCounter(board$,count)
     ct$ = board$+"TL"+str$(count)+chr$(13)
     print #com,ct$
     setCounter = count
     Log$ = Log$ +"Counter set to : "+str$(count)+chr$(13)
     end function
'reads counter
     function readCounter$(board$)
 while lof(#com) < 3
     wend
     readCounter$ = input$(#com,lof(#com))
     Log$ =Log$+"read counter"
end function


[Ed: Because word wrap may have distorted the code above, the code and the entire article are also available as an attachment. It is the file titled : SerialComInLB.txt]

Home

Dumping a file directly into a texteditor
Three items that caught my fancy recently...
Serial Communication with LB
David Drake reviews Image321.DLL
Translating Documentation
Implementing an editor with Scintilla.dll