AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Serial ( COM ) Port Console Script
Goto page 1, 2  Next
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
aobrien



Joined: 14 Feb 2008
Posts: 31

PostPosted: Sat Feb 16, 2008 8:55 am    Post subject: Serial ( COM ) Port Console Script Reply with quote

[UPDATE]
The code posted here uses the port.dll file that can be found on the web for free. The problem with port.dll is that it will only send one byte of data every 15.8ms (translates to 633 baud rate) regardless of the intended baud rate. This is a limitation of the port.dll file.

I am leaving this port.dll code here as a reference and because it works, however, I don't recommend using port.dll.

The recommended method to send/receive on the COM port is in the next post
[/UPDATE]


Well, at the risk of embarasing myself, here is a serial port console script that I pieced together using code that I found here on the forum.

I took me quite some time to put it together. So, I am placing it here so others can benefit.

The real power of this script is the ease with which it can be modified to send data instead of ASCII characters - something HyperTerminal (and other terminal emulators) can't do.

Code:
;=================== Serial_Port_Console.ahk ===========================
; 2/15/08
; Assembled, tested (on WinXP), and debugged by aobrien with help from 
;   other AHK forum members and threads.
;
; Instructions:
; 1) Copy port.dll from the link below and put into windows
;    system32 directory
;    http://www.the-starbearer.de/Praxis/ElektronikamPC/RS232/portdll.htm
;
; 2) Modify the COM port settings (under the User Variables heading)
;    to your needs and save the file.
;
; 3) Launch this script to connect to the COM Port
;
; 4) CTRL-F1 to close the COM port and exit the receive loop
;
; Script Behavior/Notes:
; * The script is designed to use a text editor (Notepad) to place the
;     received COM port characters.
; * When you attempt to type into the designated text editor the script
;     will capture the character and send it out the COM port. This is
;     accomplished with the Hotkey Assignments section.
; * Currently the script is written to only send/receive ASCII characters,
;     however, it would be REALLY EASY to modify the script so that it
;     will output/input data - something that HyperTerminal can't do.
; * When you first launch the script it will open Notepad and save it
;     using the Console_Path variable and a predetermined file name.
;
; !!!The Notepad text file MUST be saved so that the words
;      "COM1_Console_(timestamp).txt - Notepad" appear as the window
;      title, because the script will want to change to the window with
;      that name when it receives a character on the COM port.
;
;========================================================================

#SingleInstance Force
SetTitleMatchMode, 2

;========================================================================
;====== User Variables ==================================================
;========================================================================
COM_Port     = COM1
COM_Baud     = 115200
COM_Parity   = N
COM_Data     = 8
COM_Stop     = 1
Console_Path = C:\aobrien\_AutoHotKey_Scripts\COM_Port

;========================================================================
;====== Script Variables ================================================
;========================================================================
COM_Settings     = %COM_Port%:%COM_Baud%,%COM_Parity%,%COM_Data%,%COM_Stop%
Console_File_Name= %COM_Port%_Console.txt
Console_Title    = %Console_File_Name% - Notepad

;========================================================================
;====== Notepad Console Check ===========================================
;========================================================================
;Check for console, if there isn't already one, then open it.
IfWinNotExist, %Console_Title%
{
  Run, Notepad
  WinWait, Untitled - Notepad
  Send, !fa  ;file save as
  WinWait, Save As
  Clipboard = %Console_Path%\%Console_File_Name%
  Send, ^v{ENTER}
}

;========================================================================
;====== Hotkey Assignments - Used for Serial Port Transmit ==============
;========================================================================
;If the Console window is the focus then typing any character on the
;  keyboard will cause the script to send the character out the COM port.
Hotkey, IfWinActive, %Console_Title%,

;Direct Key Presses for a-z
Loop, 26
  HotKey, % "$" chr(96+A_Index), HotkeySub

;Direct Key Presses for 0-9
Loop, 10
  HotKey, % "$" chr(47+A_Index), HotkeySub

;Direct Key Presses for Other Keys
HotKey, -, HotkeySub
HotKey, =, HotkeySub
HotKey, [, HotkeySub
HotKey, ], HotkeySub
HotKey, `;, HotkeySub
HotKey, `', HotkeySub
HotKey, `,, HotkeySub
HotKey, `., HotkeySub
HotKey, `/, HotkeySub
HotKey, `\, HotkeySub
Hotkey, Space, HotkeySub
Hotkey, ENTER, HotkeySub
Hotkey, BS, HotkeySub

;Shift Modified Character Key Presses for a-z.
Loop, 26
  HotKey, % "$+" chr(96+A_Index), HotkeySub_Char_Shift

;Shift Modified Character Key Presses for 0-9
Loop, 10
  HotKey, % "$+" chr(47+A_Index), HotkeySub_Other_Shift

;Shift Modified Character Key Presses for Other Keys
HotKey, +-, HotkeySub_Other_Shift
HotKey, +=, HotkeySub_Other_Shift
HotKey, +[, HotkeySub_Other_Shift
HotKey, +], HotkeySub_Other_Shift
HotKey, +`;, HotkeySub_Other_Shift
HotKey, +`', HotkeySub_Other_Shift
HotKey, +`,, HotkeySub_Other_Shift
HotKey, +`., HotkeySub_Other_Shift
HotKey, +`/, HotkeySub_Other_Shift
HotKey, +`\, HotkeySub_Other_Shift

;========================================================================
;====== Serial Port Receive =============================================
;========================================================================

;Quit_var is used to exit the COM port receive loop
Quit_var = 0

hModule := DllCall("LoadLibrary", "str", "Port.dll")
result := DllCall("Port.dll\OPENCOM", "str", COM_Settings)
if result = 0
{
  MsgBox, AHK could not open %COM_Port%
  return
}

MsgBox, AHK is now connected to %COM_Port%

SetFormat, integer, data

;COM port receive loop
data = 0
Loop
{
  data := DllCall("Port.dll\READBYTE")

  ;This will send every byte that is received on
  ;  the COM port to the Notepad Console
  If data <> -1
  {
    Critical, On
    WinWait, %Console_Title%,
    IfWinNotActive, %Console_Title%, , WinActivate, %Console_Title%,
    WinWaitActive, %Console_Title%,   
   
    ;If you want to see the decimal data (as received by the serial port,
    ;  then uncomment the line below.
    ;Send, ^{END}data=%data%{ENTER}
    ASCII := chr(data)
    Send, ^{END}%ASCII%
    Critical, Off
  }

  if Quit_var = 1
    Break

}

result := DllCall("c:\temp\Port.dll\CLOSECOM")
DllCall("FreeLibrary", "UInt", hModule)
MsgBox, AHK is now disconnected from %COM_Port%

return

;========================================================================
;====== Serial Port Transmit ============================================
;========================================================================
HotkeySub:
var := Asc(SubStr(A_ThisHotkey,0)) ;Get the key that was pressed and convert it to its ASCII code
If A_ThisHotkey = Space
  var=32
If A_ThisHotkey = BS
  var=8
If A_ThisHotkey = ENTER
  {
    var=10 ;New Line
    d := DllCall("Port\SENDBYTE", "UInt", var)
    var=13 ;Carriage Return
  }
d := DllCall("Port\SENDBYTE", "UInt", var)
return

HotkeySub_Char_Shift:
var := SubStr(A_ThisHotkey,0) ;Get the key that was pressed.
StringUpper, var, var         ;Convert it to uppercase
var := Asc(var)               ;Get the ASCII equivalent
d := DllCall("Port\SENDBYTE", "UInt", var) ;Send it out the COM port
return

HotkeySub_Other_Shift:
var := SubStr(A_ThisHotkey,0) ; Get the key that was pressed.
;Convert it to the shift version
;  StringUpper won't work on the following.
If var = 1
  var = !
If var = 2
  var = @
If var = 3
  var = #
If var = 4
  var = $
If var = 5
  var = `%
If var = 6
  var = ^
If var = 7
  var = &
If var = 8
  var = *
If var = 9
  var = (
If var = 0
  var = )
If var = -
  var = _
If var = =
  var = +
If var = [
  var = {
If var = ]
  var = }
If var = \
  var = |
If var = `;
  var = :
If var = `'
  var = "
If var = `,
  var = <
If var = `.
  var = >
If var = `/
  var = ?

var := Asc(var)  ;Get the ASCII equivalent
d := DllCall("Port\SENDBYTE", "UInt", var) ;Send it out the COM port
return

;========================================================================
;====== Save & Reload and Minimize AHK Script ===========================
;========================================================================
^!w::
Send, ^s
Sleep, 200

Reload
Sleep, 1000

return

;========================================================================
;====== Exit Console Receive Loop =======================================
;========================================================================
^F1::
Quit_var = 1
return


[EDIT] I was trying to be slick by putting a timestamp on the notepad file name - without testing it. Doing this broke the script. The code shown above works as advertised. Sorry for any confusion this may have caused.[/EDIT]
Back to top
View user's profile Send private message
aobrien



Joined: 14 Feb 2008
Posts: 31

PostPosted: Fri Mar 28, 2008 7:41 pm    Post subject: Correct method for sending/receiving data on the Serial Port Reply with quote

The following code uses built in Microsoft Windows dll/functions. This is the method that I recommend for reading/writing the serial ports.

[EDIT] 4/2/08 - Made corrections to "Create the SetCommTimeouts Structure" section as suggested by Lexikos[/EDIT]

[EDIT] 5/22/08 - Made corrrection to Read_from_COM() subroutine. Removed redundant line from COM port receive loop. Thanks Ron1 for reporting the bug. [/EDIT]

[EDIT] 7/23/08 - Fixed the following problems:
1. Reading NULL (0x00). Previously, if NULL (0x00) data was received then everything following the NULL was omitted. As a result Read_from_COM() now returns DATA instead of ASCII. This is how the function should have been written from the beginning.
Example: Read_Data := Read_from_COM("0xFF") ;if the RX buffer contained 0x11, 0x22, 0x00, 0x33, 0x44 then Read_Data will contain 1122003344. The conversion to ASCII routine can be found in the "Serial Port Receive" section of the script.

2. Using COM Ports larger than 9. The script can now handle COM ports > 9. Thanks krisky68 for reporting and finding the fix for this bug. I was able to reproduce this bug when I changed the internal COM port on my laptop (Dell 620) from COM1 to COM11. The fix supplied by krisky68 allows my internal COM port to work at COM11. However, I often use the "Prolific USB-to-Serial Comm Port" adaptors and when I set it to COM11 then I get a failure at the "BuildCommDCB" dll call -- I have no clue how to fix this and I suspect that it is a limitation of the adaptor.

The procedure on how to change the COM# for your device can be found on the first post in the second page of this topic.[/EDIT]

Code:
;################### Serial_Port_Console_ReadFile.ahk ###################
; 3/27/08
; Assembled, tested (on WinXP), and debugged by aobrien with help from
;   other AHK forum members (especially Lexikos) and threads.
;
; This script is based upon dll structures that are built into the
; Microsoft Windows environment. This script is ugly (not very readable),
; because, it was written for demonstration purposes and I didn't want
; to complicate things by using #Include files.
;
; The most useful subroutines are listed below. Take them and write your
; application around them.
;     Initialize_COM(COM_Settings)
;     Close_COM(COM_FileHandle)
;     Read_from_COM("0xFF") -- Size of receive buffer. This returns ASCII
;                                representation of the received HEX data.
;     Example: Read_Data := Read_from_COM("0xFF") ;if the RX buffer contained
;              0x11, 0x22, 0x00, 0x33, 0x44 then Read_Data will contain 1122003344
;     Write_to_COM(Hex_Data) -- Comma delimited hex data. If I wanted to
;                               send "Hello World" I would do the following:
;     ;ASCII DATA=  H    e    l    l    o   SP    W    o    r    l    d
;     Hex_Data = 0x48,0x65,0x6C,0x6C,0x6F,0x20,0x57,0x6F,0x72,0x6C,0x64
;     Write_to_COM(Hex_Data)
;
; Instructions:
; 1) Modify the COM port settings (under the User Variables heading)
;    to your needs and save the file.
;
; 2) Launch this script to connect to the COM Port.
;
; 3) CTRL-F1 to close the COM port and exit the receive loop.
;
; Script Behavior/Notes:
; * The script is designed to use a text editor (Notepad) to place the
;     received COM port characters.
; * When you attempt to type into the designated text editor the script
;     will capture the character and send it out the COM port. This is
;     accomplished with the Hotkey Assignments section.
; * Currently the script is written to only send/receive ASCII characters,
;     however, it would be REALLY EASY to modify the script so that it
;     will output/input data - something that HyperTerminal can't do.
; * When you first launch the script it will open Notepad and save it
;     using the Console_Path variable and a predetermined file name.
;
; !!!The Notepad text file MUST be saved so that the words
;      "COM1_Console.txt - Notepad" appear as the window
;      title, because the script will want to change to the window with
;      that name when it receives a character on the COM port.
;
;########################################################################
MsgBox, Begin COM Test

#SingleInstance Force
SetTitleMatchMode, 2

;########################################################################
;###### User Variables ##################################################
;########################################################################
COM_Port     = COM4
COM_Baud     = 115200
COM_Parity   = N
COM_Data     = 8
COM_Stop     = 1
Console_Path =

;########################################################################
;###### Script Variables ################################################
;########################################################################
COM_Settings = %COM_Port%:baud=%COM_Baud% parity=%COM_Parity% data=%COM_Data% stop=%COM_Stop% dtr=Off
Console_File_Name= %COM_Port%_Console.txt
Console_Title    = %Console_File_Name% - Notepad

;########################################################################
;###### Notepad Console Check ###########################################
;########################################################################
;Check for console, if there isn't already one, then open it.
IfWinNotExist, %Console_Title%
{
  Run, Notepad
  WinWait, Untitled - Notepad
  Send, !fa  ;file save as
  WinWait, Save As
  Clipboard = %Console_Path%\%Console_File_Name%
  Send, ^v{ENTER}
}

;########################################################################
;###### Hotkey Assignments - Used for Serial Port Transmit ##############
;########################################################################
;If the Console window is the focus then typing any character on the
;  keyboard will cause the script to send the character out the COM port.
Hotkey, IfWinActive, %Console_Title%,

;###### Direct Key Presses for a-z ######
Loop, 26
  HotKey, % "$" chr(96+A_Index), HotkeySub

;###### Direct Key Presses for 0-9 ######
Loop, 10
  HotKey, % "$" chr(47+A_Index), HotkeySub

;###### Direct Key Presses for Other Keys ######
HotKey, -, HotkeySub
HotKey, =, HotkeySub
HotKey, [, HotkeySub
HotKey, ], HotkeySub
HotKey, `;, HotkeySub
HotKey, `', HotkeySub
HotKey, `,, HotkeySub
HotKey, `., HotkeySub
HotKey, `/, HotkeySub
HotKey, `\, HotkeySub
Hotkey, Space, HotkeySub
Hotkey, ENTER, HotkeySub
Hotkey, BS, HotkeySub

;###### Shift Modified Character Key Presses for a-z. ######
Loop, 26
  HotKey, % "$+" chr(96+A_Index), HotkeySub_Char_Shift

;###### Shift Modified Character Key Presses for 0-9 ######
Loop, 10
  HotKey, % "$+" chr(47+A_Index), HotkeySub_Other_Shift

;###### Shift Modified Character Key Presses for Other Keys ######
HotKey, +-, HotkeySub_Other_Shift
HotKey, +=, HotkeySub_Other_Shift
HotKey, +[, HotkeySub_Other_Shift
HotKey, +], HotkeySub_Other_Shift
HotKey, +`;, HotkeySub_Other_Shift
HotKey, +`', HotkeySub_Other_Shift
HotKey, +`,, HotkeySub_Other_Shift
HotKey, +`., HotkeySub_Other_Shift
HotKey, +`/, HotkeySub_Other_Shift
HotKey, +`\, HotkeySub_Other_Shift

;########################################################################
;###### Serial Port Receive #############################################
;########################################################################
;Quit_var is used to exit the COM port receive loop
;  0=Don't Exit; 1=Exit; CTRL-F1 to set to 1 and exit script.
Quit_var = 0
Initialize_COM(COM_Settings)

;COM port receive loop
Loop
{
  Read_Data := Read_from_COM("0xFF")
  ;msgbox, Bytes_Received=%Bytes_Received% ;Global variable that is set by Read_from_COM()

  ;0xFF in the above line basically sets the size of the read buffer.
  ;  Use StringLeft to collect only the data received by the COM port.
  ;  Bytes_Received is a global variable that is set during the Read_from_COM() routine.
  ;StringLeft, Read_Data, Read_Data, Bytes_Received ;5/22/08 This line was moved to Read_from_COM() subroutine.

  ;Process the data, if there is any.
  If (Bytes_Received > 0)
  {
    ;msgbox, Read_Data=%Read_Data%

    ;Prevent interruption during execution of this loop.
    Critical, On

    ;If care is taken, you can comment out these WinActive lines for performance gains.
    IfWinNotActive, %Console_Title%, , WinActivate, %Console_Title%,
      WinWaitActive, %Console_Title%,   

    ;7/23/08 Modified this IF statement because Read_from_COM() now returns data instead of ASCII
    ;Set to 0 if you want to see the decimal data as received by the serial port.
    IF (1)
    {
      ;Begin Data to ASCII conversion
      ASCII =
      Read_Data_Num_Bytes := StrLen(Read_Data) / 2 ;Read_from_COM() returns 2 characters for each byte

      Loop %Read_Data_Num_Bytes%
      {
        StringLeft, Byte, Read_Data, 2
        StringTrimLeft, Read_Data, Read_Data, 2
        Byte = 0x%Byte%
        Byte := Byte + 0 ;Convert to Decimal       
        ASCII_Chr := Chr(Byte)
        ASCII = %ASCII%%ASCII_Chr%
      }
      Send, ^{END}%ASCII%
      ;End Data to ASCII conversion
    }
    Else ;Send the data that was received by the COM port-ASCII format
      Send, ^{END}%Read_Data%

    Critical, Off
  }

  ;CTRL-F1 sets Quit_var=1
  if Quit_var = 1
    Break
}

Close_COM(COM_FileHandle)
MsgBox, AHK is now disconnected from %COM_Port%
;ExitApp ;Exit Script
Return

;########################################################################
;###### Serial Port Transmit ############################################
;########################################################################

;###### Normal Key Presses ######
HotkeySub:
var := Asc(SubStr(A_ThisHotkey,0)) ;Get the key that was pressed and convert it to its ASCII code
If A_ThisHotkey = Space
  var=0x20
If A_ThisHotkey = BS
  var=0x08
If A_ThisHotkey = ENTER
    var=0x0D,0x0A ;New Line, Carriage Return ; 7/23/08 Changed 0x0A,0x0D to 0x0D,0x0A

Write_to_COM(var) ;Send it out the COM port
return

;###### Shift Key Presses ######
HotkeySub_Char_Shift:
var := SubStr(A_ThisHotkey,0) ;Get the key that was pressed.
StringUpper, var, var         ;Convert it to uppercase
var := Asc(var)               ;Get the ASCII equivalent
Write_to_COM(var)             ;Send it out the COM port
return

;###### Other Shift Key Presses ######
HotkeySub_Other_Shift:
var := SubStr(A_ThisHotkey,0) ; Get the key that was pressed.
;Convert it to the shift version
;  StringUpper won't work on the following.
If var = 1
  var = !
If var = 2
  var = @
If var = 3
  var = #
If var = 4
  var = $
If var = 5
  var = `%
If var = 6
  var = ^
If var = 7
  var = &
If var = 8
  var = *
If var = 9
  var = (
If var = 0
  var = )
If var = -
  var = _
If var = =
  var = +
If var = [
  var = {
If var = ]
  var = }
If var = \
  var = |
If var = `;
  var = :
If var = `'
  var = "
If var = `,
  var = <
If var = `.
  var = >
If var = `/
  var = ?

var := Asc(var)   ;Get the ASCII equivalent
Write_to_COM(var) ;Send it out the COM port
return

;########################################################################
;###### Initialize COM Subroutine #######################################
;########################################################################
Initialize_COM(COM_Settings)
{
  Global COM_FileHandle

  ;###### Build COM DCB ######
  ;Creates the structure that contains the COM Port number, baud rate,...
  VarSetCapacity(DCB, 28)
  BCD_Result := DllCall("BuildCommDCB"
       ,"str" , COM_Settings ;lpDef
       ,"UInt", &DCB)        ;lpDCB
  If (BCD_Result <> 1)
  {
    MsgBox, There is a problem with Serial Port communication. `nFailed Dll BuildCommDCB, BCD_Result=%BCD_Result% `nThe Script Will Now Exit.
    Exit
  }

  ;###### Extract/Format the COM Port Number ######
  ;7/23/08 Thanks krisky68 for finding/solving the bug in which COM Ports greater than 9 didn't work.
  StringSplit, COM_Port_Temp, COM_Settings, `: 
  COM_Port_Temp1_Len := StrLen(COM_Port_Temp1)  ;For COM Ports > 9 \\.\ needs to prepended to the COM Port name.
  If (COM_Port_Temp1_Len > 4)                   ;So the valid names are
    COM_Port = \\.\%COM_Port_Temp1%             ; ... COM8  COM9   \\.\COM10  \\.\COM11  \\.\COM12 and so on...
  Else                                          ;
    COM_Port = %COM_Port_Temp1%
  ;MsgBox, COM_Port=%COM_Port% 

  ;###### Create COM File ######
  ;Creates the COM Port File Handle
  ;StringLeft, COM_Port, COM_Settings, 4  ; 7/23/08 This line is replaced by the "Extract/Format the COM Port Number" section above.
  COM_FileHandle := DllCall("CreateFile"
       ,"Str" , COM_Port     ;File Name         
       ,"UInt", 0xC0000000   ;Desired Access
       ,"UInt", 3            ;Safe Mode
       ,"UInt", 0            ;Security Attributes
       ,"UInt", 3            ;Creation Disposition
       ,"UInt", 0            ;Flags And Attributes
       ,"UInt", 0            ;Template File
       ,"Cdecl Int")
  If (COM_FileHandle < 1)
  {
    MsgBox, There is a problem with Serial Port communication. `nFailed Dll CreateFile, COM_FileHandle=%COM_FileHandle% `nThe Script Will Now Exit.
    Exit
  }

  ;###### Set COM State ######
  ;Sets the COM Port number, baud rate,...
  SCS_Result := DllCall("SetCommState"
       ,"UInt", COM_FileHandle ;File Handle
       ,"UInt", &DCB)          ;Pointer to DCB structure
  If (SCS_Result <> 1)
  {
    MsgBox, There is a problem with Serial Port communication. `nFailed Dll SetCommState, SCS_Result=%SCS_Result% `nThe Script Will Now Exit.
    Close_COM(COM_FileHandle)
    Exit
  }

  ;###### Create the SetCommTimeouts Structure ######
  ReadIntervalTimeout        = 0xffffffff
  ReadTotalTimeoutMultiplier = 0x00000000
  ReadTotalTimeoutConstant   = 0x00000000
  WriteTotalTimeoutMultiplier= 0x00000000
  WriteTotalTimeoutConstant  = 0x00000000

  VarSetCapacity(Data, 20, 0) ; 5 * sizeof(DWORD)
  NumPut(ReadIntervalTimeout,         Data,  0, "UInt")
  NumPut(ReadTotalTimeoutMultiplier,  Data,  4, "UInt")
  NumPut(ReadTotalTimeoutConstant,    Data,  8, "UInt")
  NumPut(WriteTotalTimeoutMultiplier, Data, 12, "UInt")
  NumPut(WriteTotalTimeoutConstant,   Data, 16, "UInt")

  ;###### Set the COM Timeouts ######
  SCT_result := DllCall("SetCommTimeouts"
     ,"UInt", COM_FileHandle ;File Handle
     ,"UInt", &Data)         ;Pointer to the data structure
  If (SCT_result <> 1)
  {
    MsgBox, There is a problem with Serial Port communication. `nFailed Dll SetCommState, SCT_result=%SCT_result% `nThe Script Will Now Exit.
    Close_COM(COM_FileHandle)
    Exit
  }

  Return %COM_FileHandle%
}

;########################################################################
;###### Close COM Subroutine ############################################
;########################################################################
Close_COM(COM_FileHandle)
{
  ;###### Close the COM File ######
  CH_result := DllCall("CloseHandle", "UInt", COM_FileHandle)
  If (CH_result <> 1)
    MsgBox, Failed Dll CloseHandle CH_result=%CH_result%

  Return
}

;########################################################################
;###### Write to COM Subroutines ########################################
;########################################################################
Write_to_COM(Message)
{
  Global COM_FileHandle
  Global COM_Port

  SetFormat, Integer, DEC

  ;Parse the Message. Byte0 is the number of bytes in the array.
  StringSplit, Byte, Message, `,
  Data_Length := Byte0
  ;msgbox, Data_Length=%Data_Length% b1=%Byte1% b2=%Byte2% b3=%Byte3% b4=%Byte4%

  ;Set the Data buffer size, prefill with 0xFF.
  VarSetCapacity(Data, Byte0, 0xFF)

  ;Write the Message into the Data buffer
  i=1
  Loop %Byte0%
  {
    NumPut(Byte%i%, Data, (i-1) , "UChar")
    ;msgbox, %i%
    i++
  }
  ;msgbox, Data string=%Data%

  ;###### Write the data to the COM Port ######
  WF_Result := DllCall("WriteFile"
       ,"UInt" , COM_FileHandle ;File Handle
       ,"UInt" , &Data          ;Pointer to string to send
       ,"UInt" , Data_Length    ;Data Length
       ,"UInt*", Bytes_Sent     ;Returns pointer to num bytes sent
       ,"Int"  , "NULL")
  If (WF_Result <> 1 or Bytes_Sent <> Data_Length)
    MsgBox, Failed Dll WriteFile to %COM_Port%, result=%WF_Result% `nData Length=%Data_Length% `nBytes_Sent=%Bytes_Sent%
}

;########################################################################
;###### Read from COM Subroutines #######################################
;########################################################################
Read_from_COM(Num_Bytes)
{
  Global COM_FileHandle
  Global COM_Port
  Global Bytes_Received
  SetFormat, Integer, HEX

  ;Set the Data buffer size, prefill with 0x55 = ASCII character "U"
  ;VarSetCapacity won't assign anything less than 3 bytes. Meaning: If you
  ;  tell it you want 1 or 2 byte size variable it will give you 3.
  Data_Length  := VarSetCapacity(Data, Num_Bytes, 0x55)
  ;msgbox, Data_Length=%Data_Length%

  ;###### Read the data from the COM Port ######
  ;msgbox, COM_FileHandle=%COM_FileHandle% `nNum_Bytes=%Num_Bytes%
  Read_Result := DllCall("ReadFile"
       ,"UInt" , COM_FileHandle   ; hFile
       ,"Str"  , Data             ; lpBuffer
       ,"Int"  , Num_Bytes        ; nNumberOfBytesToRead
       ,"UInt*", Bytes_Received   ; lpNumberOfBytesReceived
       ,"Int"  , 0)               ; lpOverlapped
  ;MsgBox, Read_Result=%Read_Result% `nBR=%Bytes_Received% ,`nData=%Data%
  If (Read_Result <> 1)
  {
    MsgBox, There is a problem with Serial Port communication. `nFailed Dll ReadFile on %COM_Port%, result=%Read_Result% - The Script Will Now Exit.
    Close_COM(COM_FileHandle)
    Exit
  }

;;;;;;;; 7/23/08 This section is replaced by the code below ;;;;;;;;;;;;;
;  SetFormat, Integer, DEC
;
;  ;###### Format the received data ######
;  ;Return only the bytes that were received.
;  If (Bytes_Received = 0)
;    Return ""
;  Else
;    ;StringTrimRight, Data, Data, %Bytes_Received% ;5/22/08 This line is essentially useless and could potentially show up as a bug.
;    StringLeft, Data, Data, Bytes_Received ;5/22/08 This is what the above line should have been
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  ;###### Format the received data ######
  ;This loop is necessary because AHK doesn't handle NULL (0x00) characters very nicely.
  ;Quote from AHK documentation under DllCall:
  ;     "Any binary zero stored in a variable by a function will hide all data to the right
  ;     of the zero; that is, such data cannot be accessed or changed by most commands and
  ;     functions. However, such data can be manipulated by the address and dereference operators
  ;     (& and *), as well as DllCall itself."
  i = 0
  Data_HEX =
  Loop %Bytes_Received%
  {
    ;First byte into the Rx FIFO ends up at position 0

    Data_HEX_Temp := NumGet(Data, i, "UChar") ;Convert to HEX byte-by-byte
    StringTrimLeft, Data_HEX_Temp, Data_HEX_Temp, 2 ;Remove the 0x (added by the above line) from the front

    ;If there is only 1 character then add the leading "0'
    Length := StrLen(Data_HEX_Temp)
    If (Length =1)
      Data_HEX_Temp = 0%Data_HEX_Temp%

    i++

    ;Put it all together
    Data_HEX := Data_HEX . Data_HEX_Temp
  }
  ;MsgBox, Read_Result=%Read_Result% `nBR=%Bytes_Received% ,`nData_HEX=%Data_HEX%

  SetFormat, Integer, DEC
  Data := Data_HEX

  Return Data

}

;########################################################################
;###### Exit Console Receive Loop #######################################
;########################################################################
^F1::
Quit_var = 1
return


Last edited by aobrien on Sat Jul 26, 2008 5:53 am; edited 3 times in total
Back to top
View user's profile Send private message
bradchuck



Joined: 01 Apr 2008
Posts: 3

PostPosted: Tue Apr 01, 2008 12:50 pm    Post subject: Thanks Reply with quote

Thanks! This was usefill to me.

I am making it into an include file and using it to replace the Port.dll file. I also found that port.dll it did not work correctly. Your method does.
Back to top
View user's profile Send private message
aobrien



Joined: 14 Feb 2008
Posts: 31

PostPosted: Wed Apr 02, 2008 5:33 pm    Post subject: Reply with quote

Hi bradchuck,

I'm glad that you are finding it useful.

FYI, I made a correction just now - the only changs are in the "Initialize COM Subroutine" section of the code. Just update your library with that section and you should be good to go.

aobrien
Back to top
View user's profile Send private message
Guest






PostPosted: Thu May 15, 2008 4:55 pm    Post subject: Thank you for Serial (COM) Port Console Script Reply with quote

Thank you for this very useful script. I marvel at the level of knowledge that it takes to create such a script.

Until now I have not been able to get any terminal program that I have evaluated to behave exactly as needed in my application. But, with the building blocks that you have provided, I have realized a proper configuration at last.

The result will be incorporated in a computer-based system that provides magnification services to individuals with low vision. The script controls the the parameters of the display through the display's serial port.

Again, my appreciation for this most helpful contribution.

Ron1
Back to top
aobrien



Joined: 14 Feb 2008
Posts: 31

PostPosted: Sun May 18, 2008 7:36 am    Post subject: Reply with quote

Hi Ron1,

Thanks for the compliment Very Happy. I'm glad that you have found it useful and are putting it to such a noble cause.

Well, necesity is the mother of invention. It's funny that I was only about 26.4ms away from not developing this WinDLL version.

For my application, I am using AHK to communicate with my companies ICs. I first started using Port.dll and after several days of development I was finally at the point of where I should have been able to communicate with the ICs, however, I kept getting NACK (Not Acknowlege). I probed the Serial Port Tx line on the oscilloscope and noticed that there was only one byte of data being transmitted every 15.8ms - I was sending 8 bytes (8*15.8ms=126.4ms). It turned out that if our ICs didn't get all 8 bytes of data in 100ms then they timed-out and sent NACK.

It took me about another 2 weeks to develop the WinDLL version. But to be honest, I couldn't have done it without the help of Lexikos.

If I do say so myself, I've done some pretty amazing things with AHK. In one case, I wrote a test automation script (in one week), which would have taken a team of software developers several months with conventional methods, saving my companies ass in the process... and they don't even know it.

I particularly enjoy automating Windows based test equipment. I automated a (Big Name) pattern generator (took me about a month) they have a team of people working more than 6 months now, trying to accomplish the same thing and they come to me for help.

This is the first time that I have made any contribution to an Open Source community and I'm honored for the opportunity.

aobrien.
Back to top
View user's profile Send private message
Guest






PostPosted: Fri Jun 20, 2008 10:20 am    Post subject: Reply with quote

nice

could this be used to send hex commands to a plasma? I gave up on a script ~ a year ago that used port.dll, maybe this is the answer?
Back to top
aobrien



Joined: 14 Feb 2008
Posts: 31

PostPosted: Wed Jun 25, 2008 7:48 am    Post subject: Reply with quote

Yes, I use it to send binary data at 115200 baud rate every day. The instructions (at the top of the script) are pretty clear, I hope. Please let me know if it needs clarification.


Note-to-all: I have recently found a bug on receiving 0x00 (NULL) - it is a pretty nasty bug. I haven't had the time to post the fix yet. Just beware and if you need the fix urgently, then let me know and I'll do my best to put it up sooner.
Back to top
View user's profile Send private message
evandevon



Joined: 22 Apr 2008
Posts: 18

PostPosted: Sun Jun 29, 2008 7:18 pm    Post subject: HELP PLEASE Reply with quote

Please see

http://www.autohotkey.com/forum/viewtopic.php?p=204820#204820

for my post.
Can't seem to get your second script to work (first works fine) I think it may be because the first data value is null?

Thanks for these awesome scripts aobrien

Evs
_________________
Inventing problems that need solutions...
Back to top
View user's profile Send private message
Raccoon



Joined: 02 Jan 2008
Posts: 70
Location: Freenode IRC

PostPosted: Sun Jul 13, 2008 4:46 pm    Post subject: Reply with quote

I slashed out over a hundred lines of code by removing all your hotkeys and using a Timer plus Input instead.

Code:
;########################################################################
;###### Serial Port Transmit ############################################
;########################################################################

GetUserInput:
{
   WinGetActiveTitle, Active_Title
   if (Active_Title != Console_Title)
      Return

   Input, var, IVL1T1, {Space}{Backspace}{Enter}{Escape}

   if ErrorLevel = Timeout
      Return
   if (var := Asc(var))
      Write_to_COM(var)
   if ErrorLevel = EndKey:Space
      Write_to_COM(0x20)
   if ErrorLevel = EndKey:Backspace
      Write_to_COM(0x08)
   if ErrorLevel = EndKey:Enter
      Write_to_COM(0x0D)
   if ErrorLevel = EndKey:Escape
   {
      Quit_var = 1
      SetTimer, GetUserInput, Off
   }
} Return


and put this line somewhere above the Serial Port Receive loop, and after Notepad has been initialized.

Code:
SetTimer, GetUserInput, 50


Note that for my purposes, I changed the value of Enter to 0x0D. Yours was 0x0A0D (which is pretty non-standard, I think you meant 0x0D0A, bug?). In any event, you should probably change this to a User Variable at the top section of the script.

Code:
COM_Data     = 8
COM_Stop     = 1
COM_Return   = 0x0D0A
Console_Path =


Thank you so very much for this code. I've been trying to port it from SerialTerm with no clue how to bottle the functions for Read/Write. Hope my contribution appears in your next update! Very Happy

PS. A side effect of this code is the Read Loop will pause for 1 second intervals while capturing input, causing any output from the COM Port to stutter. This wont be a problem with most uses that are usually "turn-based" communication, but it could be an issue for asynchronous data transmissions such as "chat". I think the trade-off is worth it, personally. There may be a similarly better solution as-well.
_________________
Need help right away? Get live support on IRC.
Already have an IRC client installed? /join #autohotkey
Back to top
View user's profile Send private message
Raccoon



Joined: 02 Jan 2008
Posts: 70
Location: Freenode IRC

PostPosted: Sun Jul 13, 2008 5:07 pm    Post subject: Reply with quote

Oh, and you should also change this:

Code:
    ;Send the data that was received by the COM port-ASCII format
    Send, ^{END}%Read_Data%

to
Code:
    ;Send the data that was received by the COM port-ASCII format
    Send, ^{END}
    SendRaw, %Read_Data%


I noticed that any output that contained "^V" was pasting the contents of my clipboard in its place.
_________________
Need help right away? Get live support on IRC.
Already have an IRC client installed? /join #autohotkey
Back to top
View user's profile Send private message
krisky68
Guest





PostPosted: Sat Jul 19, 2008 10:23 am    Post subject: Does this work for all com port numbers? Reply with quote

Hi, trying to use this to talk to a modem on COM17. I noticed that the code assumes that the com port number is single digit (ie. four chars for the handle):

Code:
  ;Creates the COM Port File Handle
  StringLeft, COM_Port, COM_Settings, 4


But even after I changed this, the script fails when attempting to create the file handle. Nothing else is connected to the port. Is there some limitation I don't know about?
Back to top
Raccoon



Joined: 02 Jan 2008
Posts: 70
Location: Freenode IRC

PostPosted: Sun Jul 20, 2008 10:15 pm    Post subject: Reply with quote

Assuming you changed StringLeft value to 5 making %COM_Port% == COM17, it should be right, but then again it may be bumping the predicted string length in some other portion of the script. I agree, this is poor coding.

Before going any further with AHK, I would suggest trying to connect using the command line tool called SerialTerm (you can find a link on these forums) or download the newest Putty and connect through it.

Once you've established a connection, having all your other variables in order (baud, parity, etc), then try again with AHK. Of course, make sure these are closed first (only one may connect at a time).
_________________
Need help right away? Get live support on IRC.
Already have an IRC client installed? /join #autohotkey
Back to top
View user's profile Send private message
krisky68
Guest





PostPosted: Mon Jul 21, 2008 7:21 am    Post subject: Reply with quote

OK thanks, will try. Actually all I want for the time being is to send AT commands to the modem to dial phone numbers from the clipboard, I am already doing it via ahk and a hidden hyperterminal window, so I know the settings are fine. (and yes, I did make sure HT wasn't running when i tried the script Smile ) But the HT approach becomes cumbersome if I ever want to read from the modem (eg. to get the ID of incoming calls).
Back to top
aobrien



Joined: 14 Feb 2008
Posts: 31

PostPosted: Tue Jul 22, 2008 7:50 am    Post subject: Reply with quote

Hi Raccoon,

Yes, I agree that it isn't the best coding in the world. I'm actually not happy with the whole flow of the script, but I wanted an easy way to demonstrate the script and I didn't know how to do it any other way.

Your way is much better. Thanks for the suggestions. I'll put them in soon.

Krisky, I'll also look into the single digit console problem, but when I test this script I actually test it in loop back mode -- 2 com ports on the same pc running 2 different copies of the same script. So I don't have any problem using different com port numbers.

I'll try to get the changes in by the end of the week.

aobrien
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group