Dumping a file directly into a texteditor
Three items that caught my fancy recently...
Serial Communication with LB
David Drake reviews Image321.DLL
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
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,_ BaudRate as ushort,_ ByteSize as char,_ Parity as char,_ StopBits as char,_ 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,_ XoffChar as char,_ XonLim as char,_ XoffLim as char,_ PeChar as char,_ EofChar as char,_ EvtChar as char,_ 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)
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:
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 !
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
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$.
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)
print "comm problem";ComError$ Log$ = Log$+ComError$+chr$(13) goto [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
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
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"
[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]