About ACCESS to RS232.

ACCESS to RS232 is a DLL that passes data to your Access basic code and 
sends data from Access basic over your communication line.
Up to 4 com-ports are supported in the full version.
The demo is restricted to one port and speeds up to 2400 baud.
The code is written in C, using Visual C++.
Please read 'pitfalls/tips/tricks' before using!

Samples are provided for Access. The Access basic-code is included in ASCII format.
It should not be that difficult (although not tested) to rewrite the Access 
samples to DBASE/DELPHI/FOXPRO or any other development environment that can 
execute a piece of code via a DDE-command and can pass/receive data from a DLL.
You can use Paradox but it will be sending only because off it's limited DDE support.

In the full version you get:
- No starting 'demo dialogs'.
- Support up to 4 comm ports
- Speeds higher than 2400 baud
  (remember that the windows comm.drv can cause problems with 
							baudrates > 9600bps!)
- Error-checking.
- Low-level UART access
- Free support via Compuserve for one month.
- information & discounts on upgrades.

HOW DOES IT WORK?
Access can access DLL's. So It's no big deal to call a DLL and get some work done.
Sending is easy but how do you know if there's some data waiting for you. Poll?
In an environment like windows you can't poll constantly, that will freeze the system.
It's better to use notifications that some kind off event happened.
The system then notifies the window there's data, this is called a 'callback'.
Unfortunately Access does not know how to handle callback's in Access basic. 
And that's where a DLL is handy. When a timer of 10ms expires all open ports are checked.
when data has arrived on a port, the DLL tells Access, via DDE (Dynamic Data Exchange),
to run a macro that runs a piece of code that enters the DLL and passes the data into
Access-basic, where you can do your own things with it.
The windows comm.drv can handle notifications but it doesn't handle them very well.
That's the reason to use a timer instead.

ADVANTAGES:
- low overhead
- works in background
- sits tight on Windows API
- implemented as a small DLL (< 20K)
- some in-line assembly for smooth multitasking (nothing undocumented though).
- work-arounds on limits in the Windows comm.drv.
- cheap
- requires minimum of Access Basic code. Get your communications up and running fast!

'DISADVANTAGES:'
- processes one port at the time 
  (except when dialing, then sending/receiving continues on other ports).
- Windows comm.drv could be unstable (especially with high baudrates).
- Windows 3.1x tested only.

SETUP
Create a directory.
Copy *.?DB to it.
Copy the DLL to WINDOWS\SYSTEM or to your own directory (preferred). 
If you use your own directory it is best to create a path to it.
Start via Access the SERIAL.MDB database.
On the form press the 'Start' button and one or both 'Open Port' buttons.
The port is opened on 2400bps, 8 databits, 1 stopbit, no parity, handshaking Xon/Xoff.
You can communicate now.
In the sample databasereceived data will be saved in a sequential file with a random -numeric- name.
The MDB is just a sample of Access calling the DLL. You Access wizards can do much better!!

ORDER INFO.
Contact us via our Compuserve account (100527,422) on how to get the full version.
Pricing $60, $35 for every other system you install the software on.
Help us to keep the prices low and order additional licences! 
In the end you benefit from it (faster & cheaper upgrades, more build-in functions).
Sources are available for registered users only. Contact us for pricing.
Allow 2-4 weeks for delivery.


If you have any questions feel free to drop us an e-mail (100527,422).


Run on a PC with 8MB RAM (keep smartdrv cache <= 1024K).

We think we can do the same thing for Access and WinSockets and 
maybe Access and NetBIOS, like we did with Access and RS232.
Interested? Let us know!


MS-ACCESS, FoxPro, Visual C++ and MS-Windows(95) are trademarks of Microsoft corp.
Delphi, DBase and Paradox are trademarks of Borland Int.

The code is provided here 'as is' without any warranty. Use at your own risk!
In no event shall EMd Projects be liable for any damages whatsoever;
regardless of the form of the claim.You shall defend and hold harmless EMde 
Projects from and against any lawsuits or claims whatsoever including lawyer's costs.
If you use the software, or any part of it, you agree with these terms.

You are NOT allowed to: 
 * Modify the software (except the sample database SERIAL.MDB).
 * Distribute/copy the software -or any part of it-, other then for backup purposes, 
   unless agreed otherwise.
 * Sell the software -or any part of it- for profit, or non-profit.
 * Install this software on more than one machine without an additional license, 
   unless agreed otherwise.
   (This does not imply the demo version. 
    This you may copy freely but not sell or use for commercial purposes or use on live systems.
    If you do copy the demo-software you must include all parts.)

EMd Projects is a young, small and independent software company, based in the Netherlands.
We specialize in Windows development focusing on the communication part and databases.

We do C with:
RS232
Access
ODBC
MAPI
WinSockets
NetBIOS

Training:
MS Office
WPWin

Copyright EMd Projects '95.
Compuserve account: 100527,422.

Return policy: NO RETURNS

=============================================================================
PITFALLS/TIPS/TRICKS

We tested with WFW 3.11 and a baudrate up to 9600 bps with a 8250 UART.

Handshaking is essential! If you don't set proper handshaking chances are big
that you lose data.

The debug-window is overhead!
If you startup with an open debug-window (showing all in- outgoing characters)
remember that with no handshaking you can lose data because the system spends
to much time updating the window and the port does not get serviced soon enough.
Click right mouse-button in the client-area of the debug window to clear
the listbox (don't click in the listbox).
A listbox in windows is limited to 64K of data be aware of this while using the 'debug-window'.
It's best to reset often.

Hex value's the debug-window expands:
<soh>   0x01 
<stx>   0x02
<etx>   0x03
<eot>   0x04
<enq>   0x05
<ack>   0x06
<bel>   0x07
<tab>   0x09
<lf>    0x0A
<ff>    0x0C
<cr>    0x0D
<dle>   0x10
<nak>   0x15
<syn>   0x16
<etb>   0x17
<can>   0x18
<esc>   0x1B
 
You can send and receive data on one port at the time.
**You can however send/receive to ports while dialing numbers via a modem**.
If you process a port, data on other ports won't get serviced until you 
finished processing the port. Be aware of this in your application design!
(We realize this a drawback. Our intention is to solve this problem in a future release).

If you send, the data must fit in the transmit queue you assigned via 
SerialOpenPort ()! Data could be lost if you don't check on this.
This is what the Visual C++ documentation says about sending:
'Applications should set the size of the transmission queue to an amount 
 no smaller than the size of the largest expected output string'.
Be aware of this!!!

We decided not to depend on the notification functions of the windows(3.1) comm.drv
because it's not that reliable. We use a timer of 10ms instead and poll all 
open-ports. Because timer message's have a low priority in windows you have some
overhead but not that much it will affect overall performance a great deal.

Be sure to set the 'Ignore DDE Requests' to NO in Access
(can be found under menu-item View/Options/Generel).

Sending and receiving is limited to max 32k-1. This is caused by how the
windows-api is defined (length parameter is integer=1024*32).

DLL uses DDE to communicate with the server app (Access).
If one app crashes the whole system does not work anymore.

The Open-port and Close-port functions belong together.
The 'SerialShutDown' function closes all open ports.

Start only one instance off MS-Access. We added code to prevent starting twice.

If you use the runtime version of Access replace the sApp parameter of
the 'SerialStartUp ()' function from "MSAccess" to the name of the .MDB file.

Be VERY carefull in using the low-level I/O functions. You must know excaclty
what you're doing if you want to use this function.
Consult your hardware manual for the IO-addresses you want to access or get a
book on serial-communications.
If your unfamiliar with these kind of functions, don't use them!!
(for the insiders: the functions are the same as the C runtime functions 
 _inp () and _outp ()).

If you use hardware that share's IRQ's you might want to add options
in the [386Enh] section of your system.ini that look like this:
ComIrqSharing=On
COM1Irq=4 (set value as apropriate)
COM2Irq=3
COM3Irq=4
COM4Irq=3
It all depends on your hardware consult the manuals of your PC and the add-in card.

We found out that the windows comm.drv does not trap the absolute values for 
the errors that occur.
We added the error-count functions to get an idea if errors do occur. 
If 80 frame-errors occur (trapped by an external RS232-analyzer) you may see 
only 40 of them in the error dialogbox (it's both way to much!).
If you do need absolute figures use an RS232-analyzer.

The Access database (SERIAL.MDB) is a sample only.
Do your own housekeeping on your com-port array and on your error-trapping

If you obtain the sources and you make modifications remember that Access loads
a DLL only once. You can build a new version but it's not linked to Access.
Restart Access to 're-attach' the new build DLL.

Get spec! If you're new to RS232 grab all the information you can get.
(There must be some on Compuserve).

Future releases??
Send us an e-mail with your wishes. It's up to you.
(Our plans are to convert to Windows 95 when it arrives, 
 add XMODEM, maybe ZMODEM and/or Kermit file transfer support and sending/
 receiving on multiple ports at the same time (Windows 95, one port one t(h)read??).
Maybe convert to an OLE custom-control?? (BIG job!!).

=============================================================================
THE FUNCTIONS

Declare Function SerialStartUp Lib "ACSERIAL.DLL" (ByVal iStart%, ByVal sApp As String, ByVal sTopic As String, ByVal sCode As String) As Integer
Can only be executed once.
Initializes the DLL, sets notification to one byte for all ports
parms: istart 0 = operate silently.
	      1 = Opens 'debug' window and show all incoming/outgoing characters.
       sApp   Application name that responds to our DDE request ("MSAccess", "dbasewin")
       sTopic DDE topic of the responding applications ("System").
       sCode  Macro or module/code to run by DDE server (Access).
	      this must be an exsisting macro/code name!
returns: 0 = DLL already started.
	 1 = DLL started ok.

Declare Function SerialShutDown Lib "ACSERIAL.DLL" () As Integer
shuts down the DLL, closes all open ports and the 'debug' window (if open).
parms : none
returns: 1

Declare Function SerialOpenPort Lib "ACSERIAL.DLL" (ByVal iPort%, ByVal iInque%, ByVal iOutQue%, ByVal lBaudrate&, ByVal iDatabits%, ByVal iStopbits%, ByVal sParity As String, ByVal sHandshake As String) As Integer
Opens requested port.
parms: port-number: 1 for com1, 2 for com2, 3 for com3, 4 for com4.
       In-queue:  number of bytes in the receive queue of the port, max 32k, if <= 0 set to 4k.
       Out-queue: number of bytes in the transmission queue of the port, max 32k, if <=0  set to 4k.
		  the data you send must fit in the queue!!
       Baudrate:  the baudrate the hardware and windows comm.drv supports.
       remember   the comm.drv could be buggy. don't go in to high rates without testing!!
       databits:  number of databits, (7 or 8, defaults to 7).
       stopbits:  number of stopbits, (1 = one stopbit, 2 = 1.5 stopbit, 3 = 2 stopbits).
       parity:    N = no parity, E = even parity, O = odd parity, M = mark parity, S = space parity, defaults to N.
       Handshake: H = hardware (CTS/RTS), X = Xon/Xoff,  " " = No handshaking.

returns: number of the open device (this is the value you work with!!)
	< 0 if an error occurs

Declare Function SerialClosePort Lib "ACSERIAL.DLL" (ByVal iClosePort%) As Integer
Closes an open port.
Characters in the output queue are sent before the port is realy closed.
parms: port number (returned by  SerialOpenPort () NOT the port number (1 for com1)).
returns: 0 if successfull
	-1 if an error occurs

Declare Function SerialReceive Lib "ACSERIAL.DLL" (ByVal iReadPort As String, ByVal sSerialData As String, ByVal iDataLen%) As Integer
Receives data from a device.
Function is called by macro 'MReadPort' wich is called by the DLL through DDE.
parms: port number: (returned by  SerialOpenPort () NOT the port number (1 for com1)).
       the port numbers is returned OUT of the DLL to you. Check the value after the function has returned
       See sample 'SerReadData ()' in SERIAL.MDB.
       Data: string that 'carries' the data. Assign string lenght in Access basic (see sample function 'SerReadData ()')
       Lenght: lenght off string you'll pass to the DLL (max 32k, limited by Win API).
returns: number of bytes received.
While receiving other port's can't receive.

Declare Function SerialSend Lib "ACSERIAL.DLL" (ByVal iSendPort%, ByVal sSendData As String, ByVal iSendLen%) As Integer
Sends data to a device.
Data must fit in the transmit queue you assigned via SerialOpenPort ()!
DLL check's comm.drv's out-queue if all data is really transmited before returning to Access basic.
parms: port number: (returned by  SerialOpenPort () NOT the port number (1 for com1)).
       Data: data to send to the device (max 32k limited by Win API)
       Length: length off string you'll pass to the DLL (max 32k limited by Win API)
returns: number of bytes actually send.

Declare Function SerialNotification Lib "ACSERIAL.DLL" (ByVal iNotifPort%, ByVal iNotifLevel%) As Integer
Sets number of bytes that have actually arrived on the specified port before it notifies Access it can start reading.
DLL waits max 30 sec for all data.
The level for the notification must fit in the in-queue (see SerialOpenPort()).
parms: port number: (returned by  SerialOpenPort () NOT the port number (1 for com1)).
       level: minimum of bytes you want in port before processing. (defaults to one byte if level <=  0)
returns: 1 (always).

Declare Function SerialDial Lib "ACSERIAL.DLL" (ByVal iDialPort%, ByVal sDialNumber As String) As Integer
Dials a number on the port and waits for the modem to connect
parms: port number: (returned by  SerialOpenPort () NOT the port number (1 for com1)).
       number to dial
returns: 1 if successfully connected.
	 0 if error occurred or user pressed cancel.
	-1 if already dialing or dial-dialogbox could not be created.
While dialing, other ports can continue to send/receive.

Declare Function SerialModemHangup Lib "ACSERIAL.DLL" (ByVal iPort%) As Integer
Hangs up modem that was connected to a remote system
It executes a hardware hang-up by clearing and setting the DTR line
parms: port number: (returned by  SerialOpenPort () NOT the com-port number (1 for com1)).
returns: 1 (always).

Declare Function SerialErrorOn Lib "ACSERIAL.DLL" () As Integer
Turns on error checking for all ports and sets counters to zero.
We found out that the windows comm.drv does not trap the absolute values for the errors that occur.
We added this function so you can get an idea if errors do occur.
Errors are counted on all ports.
Execute this funtion at any time.
returns: 1 if successful
	-1 if DLL not started

Declare Function SerialDisplayErrors Lib "ACSERIAL.DLL" () As Integer
Shows errors and events hat might have occurred.
returns: 1 if successful.
	-1 if DLL not started or dialogbox could not be created.

Declare Function SerialErrorOff Lib "ACSERIAL.DLL" () As Integer
Turns off error checking.
Execute this funtion at any time.
returns: 1 if successful.
	-1 if DLL not started.

Declare Function SerialGetModemRespone Lib "ACSERIAL.DLL" (ByVal sModemResponse As String) As Integer
Gets the last response from the connected modems on all open ports.
returns: string length of response, if successful
	 0 if no response yet (startup initializes response to hex-null).
	-1 if DLL not started

Declare Function SerialLowLevelRead Lib "ACSERIAL.DLL" (ByVal IOAddress%, ByVal sValue$) As Integer
Reads byte directly from the UART. Can be used for reading char's from parallel-port.
Be VERY carefull when using this function. Entering the UART resets values in registers.1
Causing error-trapping to be very inacurate (amongst other things).
Parms: IOAdress. Not the open port-number but a hex value representing the port.
       Consult the manual of the hardware for the port-value 
       Value: value of byte read in Ascii format
return: value of byte read in integer format.
Same as C runtime function _inp ().
Not in demo version.

Declare Function SerialLowLevelWrite Lib "ACSERIAL.DLL" (ByVal IOAddress%, ByVal sValue$) As Integer
Writes byte directly to the UART. Can be used for sending char's to parallel-port.
Be VERY carefull when using this function.
Parms: IOAdress. Not the open port-number but a hex value representing the port.
       Consult the manual of the hardware for the port-value 
       Value: value of byte to send.
return: always 1.
Same as C runtime function _outp ().
Not in demo version.

