Out of the box Mach3
provides some limited ways of communicating with
external devices over the serial port (not counting
MODBUS). Under Config
-> General Config you can select the baudrate and
either 8-N-1 or 7-N-2. You can then use the macro-call
SendSerial ("This is a test") to send a
string of data.
As you can see this is
quite straight forward but it's also quite limiting
since you don't have full control over the port - you
can't for example set the parity and you can't, to my
knowlege, receive data from a device.
Fortunately the Cypress
Enable scripting engine built into Mach3 allows you to
access commands and functions in external .dll files and
the intention with this page is to show an example of how that can be
done to achive a more flexible way of doing serial
communication.
The brute force method of
doing this would be to access the
Windows API directly
in order to gain access to the serial ports but this is
neither easy or straight forward and it is beyond my
knowlege level when it comes programming. A rescent
question on the Mach3 Yahoo user group sent me on a hunt
for a .dll library that would provide an easier
interface to the serial port than the Windows API method.
Basically the .dll provides an interface between our
application (the Mach3 macro/script) and the Windows API
so that we don't have to worry about the low level
stuff.
I stumbled across an old
(2003) article i the electronics magazine ELEKTOR where
they provided a .dll for serial communications. The .dll
is available for download from their web-site but I'm
not sure if I'm allowed to redistribute it so I'm going
to provide you with a
direct link to their Swedish site instead. You can
also get it from
Elektors english web-site but you need to create an
account in order to access the file there (which I
believe is free).
If you download the .zip
file from the Swedish site the archive contains a bunch
of files, the one we're specifically interested in here
is the one called Serial.dll. If you download
from the English site you'll get the .dll directly (020388-11.zip).
In order for Cypress
Enable to find the file you need to place it in one of
the following locations (I recommend the first):
1) Your Mach3 install
folder (usually C:\Mach3)
2) Your macro folder
3) In the Windows folder
Before we can use the
functions exported by the .dll we must tell Cypress
Enable that we are intending to use functions located in
an external library, what they are called and what type
of data they expect and return. Since the example file
provided in the .zip file is written in Delphi this took a bit
of reverse engineering and trial error for me to get
working in Cypress but I think I've got it now.
You can download a
copy of this file
here.
I won't go into all of the
available functions here but I'll try the cover the ones
most likely to be used and I'll do it in the order
you're likely to be calling them.
COMPortExists(n)
This function is used to
check if a port with a certain number is available or
not. This is done by passing in the number of the
comport that we're interested in and the funtion will
return 1 if the port exists and is free to use. It will
return 0 if the port either doesn't exists or is already
opened by another application (or can't be used for
whatever reason).
Example:
e = COMPortExists(1)
OpenCOM(n)
This function opens the
port with the number you pass to it and it returns that
very same number if the port is opened successfully. It returns 0
if the port couldn't be opened.
Example:
e = OpenCOM(1)
BaudRateSet(n)
This function sets the
baudrate for the currently opened port. It returns TRUE
if the operation is successful and FALSE if not. Any
number may be passed in but not all ports will allow non
standard baudrate.
Example:
e = BaudrateSet(115200)
ParitySet(n)
This function is used to
set the parity for the currently opened port. It returns
TRUE if successful and FALSE if not. The number passed
to the function corresponds to the various parity
settings like this:
0 = No parity
1 = Odd parity
2 = Even parity
3 = Mark
4 = Space
Example:
e = ParitySet(0)
BitsPerByteSet(n)
This function is used to
set the number of databits (usually 7 or 8) but anything
between 4 and 8 is allowed. It returns TRUE if
successful and FALSE if not.
Example:
e = BitsPerByteSet(8)
StopBitsSet(n)
This function is used to
set the number of stopbits to use. Possible values are
0, 1 and 2 for 1, 1.5 and 2 stopbits respectively. The
function returns TRUE if the operation was successful
and FALSE if not. Note that not all ports allow 1.5
stopbits in which case the function will return
FALSE.
Example:
e = StopBitsSet(0)
SendCharCOM(n)
This function is used to
send a character to the serial port and it returns 1 if
successfull. The .dll expects the data you pass it to be
of type Char but Cypress Enable doesn't have that
datatype so we have to pass it the data as type
Integer instead. Note that you can only pass one
character at the time.
Example:
e =
SendCharCOM(ASC("A")) 'Convert 'A' to an
integer and pass it.
The .dll has its own
buffer (or it might be using the system buffer) so even
though you can only pass one character a time you can
quickly loop thru a string passing each character as you
itterate thru the string. (See the example file below
for one possible way of doing it.)
ReadCharCOM(n)
This function is used to
get (or read) a character from the receive buffer. You
pass in the name of the integer variable (byRef) in
which you want it to put the character and it returns
TRUE if successfull and FALSE if not (if there's no data
to read for example).
Example:
Dim RxChar as Integer
Dim RxString as String
RxString = ""
'Clear the string.
While
ReadCharCOM(RxChar)
'Keep getting characters as long as there are any
RxString =
RxString & Chr(RxChar) 'Convert the numeric
value to a char and append.
Wend
CloseCOM()
This function closes the
port. The .dll can only handle one port so it closes
whatever port is open and it always seems to return 0.
Example:
e = CloseCOM()
Apart from the above calls
there are also calls to set and reset the control line
outputs (RTS and DTR) and to get the status of the
contol line inputs (CTS, DCD, DSR & RI). The .dll
doesn't provide any flowcontrol so that has to be coded
"manually" by using the above calls or XON/XOFF
characters. This however isn't
something that I've personally done as of this writing.
A couple of notes.
Each macro called in Mach3
is run as a separate instance. That means that one macro
doesn't know what another macro does or have done. This
means that you can not open the port with one macro and
expect to send or receive data with another macro.
Likewise if you open the port with a macro but forget to
close it before the macro ends that port will be left "hanging"
and the only way to regain access to it is to restart
Mach3. For that reason I highly recommend implementing
an error handler that at least closes the port if
there's an unexpected error while the macro executes.
For the same reason as
above this also means that this way of handling serial
input and output doesn't provide a way to get data from
a device which you don't have control over. You have to
have the port opened in order to receive data from the
from the device and you can't leave the port open
when you're not executing the macro. So if you have a device which "randomly"
spits out data and you can't afford to miss anything
then this aproach won't work. The "best" devices to use
with this aproach is "polled" devices, ie. devices that
only sends data when you tell it to.
The .dll provides a way of
passing Windows messages from the Windows API to the
calling application when the status of the port changes
(like events in any event driven language). Unfortunately there's no way,
at least as far as I know, to receive
these messages in Cypress Enable and for that reason
I've left the declarations of this function out of this
example. If someone knows how to do this please let me
know.
You
can download an
example script that I used when working this out. I
used a serial loopback plug so that anything going out
the port was going right back in. The loopback plug is
simply a female DB9 wired as shown to the left. The
macro is pretty straight forward, it checks COM1-COM5
and builds up a string that is then shown as an InputBox
asking the user to input a COM port to open. It opens
the port, sends a lenghty string out and then immedately
reads the receive buffer and displays whatever comes in.
Henrik Olsson 2011-04-16
henrik [at] henriksplace [dot]
se |