therealhanuta

CommAPI - Serial and parallel communication with Windows API

100 posts in this topic

In the moment there are two UDF's for serial communication:

The first UDF is very popular, but it has the disadvantage, that an unknown DLL needs to be used.

The second UDF use the Windows API, but it has much less features.

So, I decided to create an another UDF for my requirements.

You can find the result here: http://www.autoitscript.com/wiki/CommAPI

It is a further development of the second UDF and has (almost) all functions of the first UDF.

Feel free to fix any existing bugs and to extend it with additional functions.

3 people like this

Share this post


Link to post
Share on other sites



Hi therealhanuta

Could you supply a simple example on how to use your routines, I mean, to open a serial port, set baudrate, read, write and close?

I´m asking that as most of the time these are the really needed functions. The good point is to not use any dll....

Thanks

Jose

1 person likes this

Share this post


Link to post
Share on other sites

#3 ·  Posted (edited)

Hi therealhanuta

FOr many and many years I have being using   ObjCreate ("NETCommOCX.NETComm")

   ->

It has the basic functions and all my programs work with those functions without problems, besides to xoff when receive 0X00 byte and the dll to install that is a nightmare in some places.

While I had evaluated martin´s Udf, they are really great, but would be very complex to update in my programs, and maybe has so many options. And also,still  has a dll to make things complex to install.

So, my question/suggestion ( I believe ist´s a subset of your udfs): :ermm:  :shifty:

It´s possible to you to create a separate and simple UDF with just the basic functions (without dll, of course), as the above link , where the functions really needed are as follows(I represented them with that dll interface):

==> one to declare / activate serial port. 

  $comRS232 = ObjCreate("NETCommOCX.NETComm")

==> one to set parameters:  (it also must be able to be called later to change parameters, like baud rate)

With $comRS232 
  .CommPort = $serialPort
  .PortOpen = True
  .Settings = "600,N,8,1"

 

==> manipulation routines

  $comRS232.InBufferCount = 0  and $numChars=  $comRS232.InBufferCount

  $buffer= $comRS232.InputData

    $comRS232.Output = $msg 

  $comRS232.PortOpen=false or true  

  -> one routine to list (in an array, for example) the avaiable serial ports (new function)

==> desired extra functions/issues (they must be optional):

  - have a way to declare an event when something arrives in buffer

        (best if just when "n" bytes arrives, but one event for each open port)

  - have a way to declare event when transmition finishes

  - be able to receive/treat x00 byte normally as any other character (without create a xoff)

    Thanks

Jose

Edited by joseLB

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

Hi joseLB,

here is an example, which do not ignore 0x00:

#include "CommInterface.au3"

Global Const $iPort = 3
Global Const $iBaud = 9600
Global Const $iParity = 0
Global Const $iByteSize = 8
Global Const $iStopBits = 0
Global Const $sCommand = "Command" & @CRLF

Main()

Func Main()
    Local $iErrorLine = COMHandling()
    If @extended Then
        MsgBox(32, "Error", _WinAPI_GetLastErrorMessage())
    ElseIf @error Then
        MsgBox(32, "Error", "Error in line " & $iErrorLine)
    EndIf
EndFunc

Func COMHandling()
    Local $hFile = 0
    Local $sResult = ""

    ToolTip(_CommAPI_GetCOMPorts(), Default, Default, "COM Ports", 1)
    $hFile = _CommAPI_OpenCOMPort($iPort, $iBaud, $iParity, $iByteSize, $iStopBits)
    If @error Then Return SetError(@error, @extended, @ScriptLineNumber)
    _CommAPI_ClearCommError($hFile)
    If @error Then Return SetError(@error, @extended, @ScriptLineNumber)

    For $i = 1 To 10
        _CommAPI_TransmitData($hFile, $sCommand)
        If @error Then Return SetError(@error, @extended, @ScriptLineNumber)
        Sleep(5000)
        $sResult = _CommAPI_ReceiveData($hFile)
        If @error Then Return SetError(@error, @extended, @ScriptLineNumber)
        If $sResult Then
            ToolTip($sResult, Default, Default, "Received data", 1)
        Else
            ToolTip("Loop " & $i, Default, Default, "No received data", 3)
        EndIf
    Next

    _CommAPI_CLosePort($hFile)
    If @error Then Return SetError(@error, @extended, @ScriptLineNumber)
    Sleep(5000)
EndFunc

 

BufferCount is not implemented in the moment. Maybe I can add this next week.

Edited by therealhanuta

Share this post


Link to post
Share on other sites

#5 ·  Posted (edited)

Hi joseLB,......

BufferCount is not implemented in the moment. Maybe I can add this next week.

THANKS A LOT for sharing and for all your support !!!!!   I will try them and report results here!!!!!

Sorry, but 2 more questions: o:)

1- Can I use many times $hFile = _CommAPI_OpenCOMPort($iPort, $iBaud, $iParity, $iByteSize, $iStopBits) on the same serial port, for example, to change baud, or you recomend close and open it again?

2- Can I declare $oMyError = ObjEvent("AutoIt.Error", "MyErrFunc"), so if an error like "port does not exists", or whatever other error could be traped, like at this exampe:

Func 
MyErrFunc($oError)
.....
 $msgError = "error at module "& $oError.source &@cr 
       &  "  - description: " & StringStripWS($oMyError.description, 3) &@cr _
       &  "  - error: " & Hex($oMyError.number, 8) &" : "&  $oError.retcode &@cr _
       &  "  - script line: " & $oMyError.scriptline &@cr _
       &  "  - descr.Windows: " & $oError.windescription
....
msgBox (4096, "error detected during execution", $msgError)

Jose

Edited by joseLB

Share this post


Link to post
Share on other sites

#6 ·  Posted (edited)

Hi therealhanuta

Up to now the routines are working great! Not all tests yet...

I will post here some tips in receiving binary data thru a serial port. I hope it can help someone..

First, what is a binary stream? As an example, a microcontroler (PIC) getting data thru ADC and sending the 2 adquired bytes straight thru serial, without any treatment.

Now supose your message has: 1 letter(A-Z)  + 4 ADC values + 3 counters of anything (each one byte). Your message will arrive with 1 + 4 x 2 + 3 = 12 bytes (or as AU3 sees it,  string with 12 "chars").

  1. $sResult = _CommAPI_ReceiveData($hFile) will have at $sResult a "string" with 12 "chars".
  2. besides the first one, each of these chars are byte values. On them can exist binary value 0. So, it´s vital that 0 be not interpreted as XOFF by receiver (PC)
  3. now let´s separate the 1 + 4 + 3 = 8 fields
  4. field1= StringMid($sResult,1,1)            ;this is just a characterbetwen A-Z
    
    field2= ASC(StringMid($sResult,2,1))               ;LSB of the ADC value. First byte = LSBb, 2nd. byte = MSB
    field2= field2 + ASC(StringMid($sResult,3,1))*256  ;MSB of the ADC value - now field2 is a number, betwen 0-65535
    
    ;field3..5 are similar to field 2, just increase by one the start position in stringMid
    
    field6= ASC(StringMid($sResult,10,1))              ;now field6 a "number" betwen 0-255
    ;filed7-8 are similar to field 6, just increase by one the start position in stringMid
Edited by joseLB

Share this post


Link to post
Share on other sites

Hi therealhanuta

Using an usb-to-serial converter (prolific), if, after a sucessful open and loop reading data ok, if I remove the usb converter, no error is reported (@error  and @extended  = 0).

That´s the intended behavior?

Jose

Share this post


Link to post
Share on other sites

#8 ·  Posted (edited)

This very very simple and TESTED example to show basic usage of CommAPI or to test your serial connection

(CommAPI works like a charm)
It needs a terminal emulator, like putty, on your pc (with a loopback cable= pins 2<->3)
   or a emulator in another PC with cross cable ( pins 2 <->3 and 3<->2)
It asks at terminal emulator to type something and shows what was typed 3 seconds later.
Typing ... (3 dots) terminate the program.
Tested with a USB - serial converter (prolific) at 9600 cross cabled to another PC with putty terminal emulator

Jose

#include "CommInterface.au3"

Global Const $iPort = 6             ;============== > CHANGE HERE  TO THE PORT YOU ARE USING ==================
Global Const $iBaud = 9600
Global Const $iParity = 0
Global Const $iByteSize = 8
Global Const $iStopBits = 0


$x=_CommAPI_GetCOMPorts()                   ;get a string with all your serial ports separated with @CRLF
MsgBox(4096,"list of Com Ports",$x)             ;show avaiable ports
$loop=1
$hFile = _CommAPI_OpenCOMPort($iPort, $iBaud, $iParity, $iByteSize, $iStopBits)
$x= _CommAPI_ClearCommError($hFile)

Do
     _CommAPI_TransmitData($hFile, @crlf&"type anything (... = end):")  ;send prompt
     sleep (3000)                                                           ;user have  up to 3 seconds to type something
     $digitado= _CommAPI_ReceiveData($hFile)            ;get typed string
    _CommAPI_TransmitData($hFile, @crlf&"-->" & $digitado)  ;echoes to serial what was typed above
    ToolTip($loop & $digitado)              ;shows in tooltip typed chars, preceded by number of iterations
    $loop=$loop+1
Until $digitado= "..."                      ;if user types ... (3 dots) program finishes
_CommAPI_CLosePort($hFile)
Exit
Edited by joseLB

Share this post


Link to post
Share on other sites

1- Can I use many times $hFile = _CommAPI_OpenCOMPort($iPort, $iBaud, $iParity, $iByteSize, $iStopBits) on the same serial port, for example, to change baud, or you recomend close and open it again?

No, you have to close port before you can use _CommAPI_OpenCOMPort again. Instead of this you can use function _CommAPI_ChangeCommStateElement. Or you can use _CommAPI_BuildCommDCB in combination with _CommAPI_SetCommState.

2- Can I declare $oMyError = ObjEvent("AutoIt.Error", "MyErrFunc"), so if an error like "port does not exists", or whatever other error could be traped, like at this exampe:

No, because I use only DLL calls and no COM object. You habe to check @error after every function.

Using an usb-to-serial converter (prolific), if, after a sucessful open and loop reading data ok, if I remove the usb converter, no error is reported (@error  and @extended  = 0).

That´s the intended behavior?

I would expect, that @error is set when you try to transmit or receive data. Unfortunately I habe no USB-to-serial converter to test this.

Share this post


Link to post
Share on other sites

Hi therenauta

Thanks for your answers

I hope i´m wrong, but here follows one more observation / question.

Seems to me that only when you have an active _CommAPI_ReceiveData command, PC is able to receive data thru serial, discarding all messages that arrive during the period you do not have an active _CommAPI_ReceiveData  command.

In other words: you give an _CommAPI_ReceiveData. I suppose that it "waits" for a message to arrive during "timeout"(default=100ms). After that program continues it´s execution doing other stuff. If new messages arrives before another _CommAPI_ReceiveData all that data is ignored?

My suspection is due to one part of program where:

- give _CommAPI_TransmitData to send a command to an external device

- this message can take up to 10 seconds to be executed, and nothing else will be done by the device

- so, during this time PC is doing other stuff (or in sleep)

- after > 10 secs, PC issues _CommAPI_ReceiveData

- but nothing comes, and I know that the message arrived.

Thanks

Jose

Share this post


Link to post
Share on other sites

wow, thanks for sharing,

i have been using COMMGvv2 [by Martin Gibson] for years.

And,  the only bug for COMMMGvv2 is it seems not handling error package very well.

i will try urs in my appliaction!~

Share this post


Link to post
Share on other sites

Thanks for this UDF.

I used it now for a while and it works great.

Share this post


Link to post
Share on other sites

   There was a third UDF using kernel32 I downloaded maybe a year and a half ago. I saw it two days ago but can't find it again. It was 90% complete and one of the last threads was to the effect would anyone have an interest in developing it further.

   Anyway.... it was very, very similar to this one therealhanuta but yours seems a whole lot more complete and polished. The problem, as mentioned above, is that there aren't any examples. Just the basics would be a huge help. Open, send, receive and close would be a huge, huge help. Any chance of getting some examples?  

Share this post


Link to post
Share on other sites

Anyway.... it was very, very similar to this one therealhanuta but yours seems a whole lot more complete and polished. The problem, as mentioned above, is that there aren't any examples. Just the basics would be a huge help. Open, send, receive and close would be a huge, huge help. Any chance of getting some examples?

 

Here you can find some examples, now.  :)

Share this post


Link to post
Share on other sites

I've got a question if someone might be able to help.

I've built a small app to read an MSR for a cash register. It works with track2 of a gift card and the end result is to read the number, query a website and get a balance.

Anyway, the MSR is an idtech idmb-3321 rs232 reader. It comes with a utility to modify settings within the MSR.

The issue is that if I start the utility and connect to the MSR, my code gathers the output of a card swipe without issue. Depending on the settings of the MSR that might include STX/ETX or not, or whatever I want to format it as pre/post fix or terminations, etc. It works fine though, as long as I parse the text to my likings.

However, if I don't start the config util, my app does not work. It is also interesting in that if I start hyperterminal, read the swipe, then shut hyperterminal down, my app will read it just fine.

Its like there is a driver needed or a service, but I did not think rs232 needed a driver. This is on win7 ult x32, and the hyperterminal is missing from win7, so I used hyperterm .exe and .dll from winxp.

I've looked at the idtech website, and they say there is no driver for rs232. Of course for usb2rs232 there are.

I get no errors in the code. The handle is good. As a matter of fact, what is returned is DC3 (BE. After running hyperterm or the config utiity, it works perfectly. I tried raw mode in the MSR, but there is some decoding there that I don't understand, although the output is readable without running hyperterm or the config utility.

The settings are:

port 1

baud 9600

parity 0

stop bits 1

byte size 8

handshake XonXoff

Any ideas?

Share this post


Link to post
Share on other sites

I tried it on a couple xpsp3 machines, it does the exact same thing. Run HT once, and it is fine. Otherwise, it does not get the data sent.

Interestingly here is a post that describes what is happening, with a reply that just figures lol

http://answers.microsoft.com/en-us/windows/forum/windows_7-hardware/initialize-serial-port/a9fa325b-fafc-44b1-b7c1-9501c1dca09b

I've searched init/initializing com/serial ports, but found nothing. I have no idea what the issue might be really.

Anyone?

Share this post


Link to post
Share on other sites

Hi sulfurious,

there are 3 useful tools.that I use:

  1. command MODE in command line (it shows you the current settings of COM ports)
  2. HTerm as a better HyperTerminal (German developer site, but English GUI)
  3. Null-modem emulator com0com (virtual serial port driver for test purposes)

First, try following steps:

  1. run MODE
  2. run HyperTerminal
  3. run MODE again
  4. compare both results
  5. if there is a different, this is the solution

If there is no different, try application HTerm. Play with options "CTS Flow control", DTR and RTS.

Share this post


Link to post
Share on other sites

#18 ·  Posted (edited)

Hello,

I am just working on understanding of CommAPI.

Can anybody explain to me which meaning has the parameter 15:
_CommAPI_PurgeComm($hFile, 15)

And

What is the meaning of "5000" ?

Local $sResult = _CommAPI_ReceiveData($hFile, IniRead($sFileINI, "Timeout", "IDSR", 5000))

I found this in commInterface.au3.

In my view Timeout and Buffer is mismatched.

; #FUNCTION# ====================================================================================================================
; Name ..........: _CommAPI_ReceiveData
; Description ...: Receives data (RxD/RX/RD) to a specified communications device.
; Syntax ........: _CommAPI_ReceiveData(Const $hFile[, $iMinBufferSize = 1[, $iMaxWaitTime = 100]])

Thanks and greetings

Andrew

Edited by adom

Thanks! :bye:

Greetings

Andrew

 

Share this post


Link to post
Share on other sites

Hello,

I suppose I have found some not-logical in sample code:

Call string:

Local $sResult = _CommAPI_ReceiveData($hFile, 500)

 

error: _CommAPI_ReceiveData() called with wrong number of args.

 

; #FUNCTION# ====================================================================================================================
; Name ..........: _CommAPI_ReceiveData
; Description ...: Receives data (RxD/RX/RD) to a specified communications device.
; Syntax ........: _CommAPI_ReceiveData(Const $hFile[, $iMinBufferSize = 1[, $iMaxWaitTime = 100]])
; Parameters ....: $hFile               - [in] A handle to the communications device.
; Return values .: Success - Received string
;                  Failure - Empty string
; Author ........:
; Modified ......:
; Remarks .......: If the result contains Chr(0), you should convert the result with function Binary().
; Related .......: _CommAPI_TransmitData
; Link ..........:
; Example .......: No
; ===============================================================================================================================
Func _CommAPI_ReceiveData(Const $hFile) ; only one Parameter!!!!!!!!!!!!!
    Local $tBuffer = DllStructCreate("char")
    Local $iWritten = 0
    Local $sResult = ""
    Do
        _WinAPI_ReadFile($hFile, DllStructGetPtr($tBuffer), 1, $iWritten)
        If @error Then Return SetError(@error, 0, $sResult)
        If $iWritten Then $sResult &= DllStructGetData($tBuffer, 1)
    Until Not $iWritten
    Return $sResult
EndFunc   ;==>_CommAPI_ReceiveData

 

Has anybody knowledge to clarify this?

THX

Greetings

Andrew


Thanks! :bye:

Greetings

Andrew

 

Share this post


Link to post
Share on other sites

Hello,

I have a work to do with Serial communication:

If the receiver wants to send to my PC more than one telegram lets say 10 telegrams with each a <CR> at the end of telegram.

The <CR> should tell me that after it a new telegram starts.

Now the problem:

How can I recognize these <CR> with Local $sResult = _CommAPI_ReceiveData($hFile) ?

Any idea is welcome.

Greetings

Andrew


Thanks! :bye:

Greetings

Andrew

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now

  • Similar Content

    • cheeroke
      By cheeroke
      Hi all,
      I got this code and would like to be able to change Baud Rate and instead of sending character by character i would like to be able (if possible) to send whole string. But i don't know how to change it.
      I am taking input from file and processing whole line (this is done in FilesHandling.au3).
      To execute this i am just calling SendData("FileName", int) in "main" script.
      Any help very appreciated.
      #include <WinAPI.au3> #include <Array.au3> #include "FilesHandling.au3" ;init DLL function, we need handle to call the function $h = DllCall("Kernel32.dll", "hwnd", "CreateFile", "str", "\\.\COM19", "int", BitOR($GENERIC_READ,$GENERIC_WRITE), "int", 0, "ptr", 0, "int", $OPEN_EXISTING, "int", $FILE_ATTRIBUTE_NORMAL, "int", 0) $handle=$h[0] Func SendData($FileName, $LineNumber) ;string to be send $c = readFile($FileName, $LineNumber) $cLenght = StringLen($c) $aArray = StringSplit($c, "") ;_ArrayDisplay($aArray, "", Default, 64) For $i = 1 To $cLenght writeChar($handle, $aArray[$i], $cLenght) Next ;move to next line writeChar($handle, @CR,1) EndFunc ;write a single char func writeChar($handle,$c,) $stString = DLLStructCreate("char str") $lpNumberOfBytesWritten = 0 DllStructSetData($stString, 1, $c) $res = _WinAPI_WriteFile($handle, DllStructGetPtr($stString, "str"), 1,$lpNumberOfBytesWritten) if ($res<>true) then ConsoleWrite ( _WinAPI_GetLastErrorMessage() & @LF) EndIf EndFunc  
    • MazeM
      By MazeM
      Hi
      here's another UDF for the serial port. It is very similar to CommAPI using kernel32.dll, but all code is packed into a single file without any dependencies, not even using WinAPI.au3. It differs from existing UDF that it doesn't allow a timeout when reading, instead it always returns immediately, either with the requested amount ob bytes read or with a failure status. And of course there is a function provided to query the amount of available bytes in the receive buffer. The reason behind this design decision: You can do 1000 other things in the main loop while checking from time to time if enough data bytes arrived. There's no point to block the program waiting for the serial port.
      It is currently a work-in-progress, as I didn't test all functions yet. The code was developed and tested on Windows 7 64 bit.  The ComUDF-Tests.au3 shows some tests and basic usage of the UDF. Maybe there's no reason to use this UDF, given the existence of the others UDFs, but I did it to get to know DllCall better - I use structs no only to pass but also to get data back (I don't use the array returned by DllCall to read that data, unless required). You're welcome to test it on older and newer Windows versions.
      Here's a list of the implemented functions:
      ; _ComListPorts ; _ComOpenPort ; _ComSetTimeouts ; _ComClosePort ; ; _ComSetBreak ; _ComClearBreak ; _ComGetInputcount ; _ComGetOutputcount ; _ComClearOutputBuffer ; _ComClearInputBuffer ; ; _ComSendByte ; _ComReadByte ; _ComSendBinary ; _ComReadBinary ; ; _ComSendChar ; _ComReadChar ; _ComSendCharArray ; _ComReadCharArray ; _ComSendString ; _ComReadString ; ; __ComClearCommError ; __PurgeComm Maze
       
      ComUDF.au3
      ComUDF-Tests.au3
    • OldNoob
      By OldNoob
      I'm attempting to call the winapi function EnumSystemFirmwareTables using DllCall "Kernel32.dll" without success. I am a total noob when it comes to this and could use some direction. Based on the documentation "Dealing with Dlls in AutoIt" by Andreas Karlsson, I have tried using the following code to obtain the buffersize of the Firmware Table Buffer.
      Thanks in advance for any help
      #include <WinAPI.au3> MsgBox(0, "ESFT BufferSize", "BufferSize = " & _EnumSystemFirmwareTables()) Func _EnumSystemFirmwareTables() $aRet = DllCall ("Kernel32.dll", "UINT", "EnumSystemFirmwareTables", "DWORD", "ACPI", "PVOID" ,Null , "DWORD" ,Null) if @error Then MsgBox (0,"Error","An error ocurred with the DLLCALL, error returned = " & @error &@CRLF & "GetLastError = " & _WinAPI_GetLastError ( ),0) Exit else Return $aRet endif EndFunc