Page 1 of 1

ascii serial COM comunication for arduino and I2C

Posted: 03 Apr 2016, 08:03
by bettj
This is a script I put together so that I could use AHK to talk to my Arduino Uno over coms, to run an I2C LCD. There is already a project out there for comunicating over coms, but it only worked for a preset value in HEX. I updated it with a ascii to hex converter and added all the necessary bits to make it work over COMs. links to resources below. I will post the Arduino Code here as well if you need it. It will be the 2nd one. It is based off of a example linked below.

It took me forever to get this to work but I have combined a few existing projects to get this to work properly. I understand that It could use a lot of cleanup work, but It work for now. If you have any suggestions pls leave them below.

**Added new arduino code on the bottom to keep words on same line. If you have any issues pls post. (NOTE: Cuts off words that are bigger than 16 characters long)

---------------------------| AHK Code |--------------------------

Code: Select all

AscToHex(str) {
  Return str="" ? "":Chr((Asc(str)>>4)+48) Chr((x:=Asc(str)&15)+(x>9 ? 55:48)) AscToHex(SubStr(str,2))
}

F4::
InputBox, asciiMessage, "what do you want", What...?
rawHex= % AscToHex(asciiMessage)
hexLngth:= % StrLen(rawHex)/2
hexLngth:=Floor(hexLngth)
bMessage=""
cMessage=""
MsgBox, Begin COM Test

;########################################################################
;###### User Variables (Change these to match you needs) ################
;########################################################################
COM_Port     = COM3
COM_Baud     = 9600
COM_Parity   = N
COM_Data     = 8
COM_Stop     = 1

;########################################################################
;###### Script Variables (Don't touch these) ############################
;########################################################################
COM_Settings = %COM_Port%:baud=%COM_Baud% parity=%COM_Parity% data=%COM_Data% stop=%COM_Stop% dtr=Off

;########################################################################
;###### Main Routine ####################################################
;########################################################################
Initialize_COM(COM_Settings) 

;NL = New Line

while, hexLngth>A_Index-1{
	iniwrite, % SubStr(rawHex,A_Index*2-1,2), temp.ini,simple,%A_Index%
	iniread, tMessage,temp.ini,simple,%A_Index%
	iniwrite, `, 0x%tMessage%, temp.ini,character,%A_Index%
	iniread, jMessage, temp.ini,character,%A_Index%
	fileAppend, %jMessage%, temp.txt
}
FileRead, Message, temp.txt
Message= % SubStr(message,3)
filedelete, temp.txt
;            &   NL   NL    O    8    (    )   NL   NL    %
;Message =0x26,0x0A,0x0A,0x4F,0x38,0x28,0x29,0x0A,0x0A,0x25
Write_to_COM(Message)


Close_COM(COM_FileHandle) 
Return


;########################################################################
;###### Subroutines (You don't really need to look below this line) #####
;########################################################################

;########################################################################
;###### Initialize COM Subroutine (Don't touch this routine) ############
;########################################################################
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 (Don't touch this routine) #################
;########################################################################
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 (Don't touch this routine) #############
;########################################################################
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 (Don't touch this routine) ############
;########################################################################
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
  }

  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

}

;-----Serial-com-----
;https://autohotkey.com/board/topic/26231-serial-com-port-console-script/page-2
;----Hex Converter----
;https://autohotkey.com/board/topic/29293-closed-collection-of-beautiful-one-liner-codes/page-2#entry187995


---------------------------| Arduino Code |--------------------------

Code: Select all




/*
** Example Arduino sketch for SainSmart I2C LCD Screen 16x2
** based on https://bitbucket.org/celem/sainsmart-i2c-lcd/src/3adf8e0d2443/sainlcdtest.ino
** by
** Edward Comer
** LICENSE: GNU General Public License, version 3 (GPL-3.0)

** This example uses F Malpartida's NewLiquidCrystal library. Obtain from:
** https://bitbucket.org/fmalpartida/new-liquidcrystal

** Modified - Ian Brennan ianbren at hotmail.com 23-10-2012 to support Tutorial posted to Arduino.cc

** Written for and tested with Arduino 1.0
**
** NOTE: Tested on Arduino Uno whose I2C pins are A4==SDA, A5==SCL

*/
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

#define I2C_ADDR    0x3F // <<----- Add your address here.  Find it from I2C Scanner
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7

char sMem[15]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int n = 1;
int chl = 0;
char ch = 49;
String readString;

LiquidCrystal_I2C	lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);

void setup()
{
 lcd.begin (16,2); //  <<----- My LCD was 16x2

 
// Switch on the backlight
lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
lcd.setBacklight(HIGH);
lcd.home (); // go home
Serial.begin(9600);

// lcd.print("SainSmartI2C16x2");  
}

void loop()
{
 // Backlight on/off every 3 seconds
 lcd.setCursor (0,1);        // go to start of 2nd line
 ///while (Serial.available() == 0);
  while (Serial.available()) {
       delay(10);  //small delay to allow input buffer to fill
         
       char c = Serial.read();  //gets one byte from serial buffer
       if (c == ',') {break;}  //breaks out of capture loop to print readstring
       readString += c; } //makes the string readString  
       if(readString!="")lcd.clear();    
        
     if (readString.length() >0) {
       //You will need to change the substring settings in this section to fit your screen. default (16x2)
       Serial.println(readString.substring(0,16)); //prints string to serial port out;
       Serial.println(readString.substring(16,32));
       lcd.print(readString.substring(0,16));
         lcd.setCursor (0,1);
       lcd.print(readString.substring(16,32));
       readString=""; //clears variable for new input
 ///if(chl>16)chl=0;
 ///char sMem[chl] = Serial.read();
 ///chl=chl+
 ///Serial.println(sMem);
 ///lcd.print(sMem);
 //lcd.print(n++,DEC);
 //lcd.setBacklight(LOW);      // Backlight off
 //delay(3000);
 //lcd.setBacklight(HIGH);     // Backlight on
 //delay(3000);
     }}
     
**edit: Apr/3/2016
Fixed arduino code for 16x2 text scrolling.


---------------------------| Arduino Code (Word Detection) |--------------------------

Code: Select all


/*
** Example Arduino sketch for SainSmart I2C LCD Screen 16x2
** based on https://bitbucket.org/celem/sainsmart-i2c-lcd/src/3adf8e0d2443/sainlcdtest.ino
** by
** Edward Comer
** LICENSE: GNU General Public License, version 3 (GPL-3.0)

** This example uses F Malpartida's NewLiquidCrystal library. Obtain from:
** https://bitbucket.org/fmalpartida/new-liquidcrystal

** Modified - Ian Brennan ianbren at hotmail.com 23-10-2012 to support Tutorial posted to Arduino.cc
** Modified again by: Bettj AHK (Auto hotkey) forums.
** https://autohotkey.com/boards/viewtopic.php?f=6&t=15550

** NOTE: Tested on Arduino Uno whose I2C pins are A4==SDA, A5==SCL

*/
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

#define I2C_ADDR    0x3F // <<----- Add your address here.  Find it from I2C Scanner
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7

char sMem[15]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int n = 1;
int chl = 0;
char ch = 49;
String readString;
String first;
String second;
LiquidCrystal_I2C	lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);

void setup()
{
 lcd.begin (16,2); //  <<----- My LCD was 16x2

 
// Switch on the backlight
lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
lcd.setBacklight(HIGH);
lcd.home (); // go home
Serial.begin(9600);

// lcd.print("SainSmartI2C16x2");  
}

void loop()
{
  while (Serial.available()) {
       delay(10);  //small delay to allow input buffer to fill
         
       char c = Serial.read();  //gets one byte from serial buffer
       if (c == ',') {break;}  //breaks out of capture loop to print readstring
       readString += c; } //makes the string readString  
       if(readString!="")lcd.clear();    
        
     if (readString.length() >0) {
       //You will need to change the substring settings in this section to fit your screen. default (16x2)
       /*if (readString.substring(16,16) == " ") {
          
         Serial.println(readString.substring(0,16)); //prints string to serial port out;
         Serial.println(readString.substring(16,32));
         lcd.print(readString.substring(0,16));
         lcd.setCursor (0,1);
         lcd.print(readString.substring(16,32));
         readString="";
         
       }
       else{
         */
        if (readString.length() >16){
        
        first = readString.substring(0,16);
        first = first.substring(0,first.lastIndexOf(' '));
        Serial.println(first);
        second = readString.substring(first.lastIndexOf(' ')+1,32);
        second = second.substring(second.indexOf(' ')+1,32);
        Serial.println(first);
        Serial.println(second);
        lcd.print(first);
          lcd.setCursor (0,1);
        lcd.print(second);
        readString="";
       }
       else{
         Serial.println(readString);
         lcd.print(readString);
         readString="";
       }
     }}

-----Serial-com-----
https://autohotkey.com/board/topic/2623 ... ipt/page-2
----Hex Converter----
https://autohotkey.com/board/topic/2929 ... ntry187995
----Arduino Serial COM to I2c LCD----
https://bitbucket.org/celem/sainsmart-i ... cdtest.ino

Re: ascii serial COM comunication for arduino and I2C

Posted: 05 Apr 2016, 14:10
by Peterm
Interesting. As a new starter with Arduino and LCD, I can see why it took you an age.

Re: ascii serial COM comunication for arduino and I2C

Posted: 11 Feb 2018, 09:23
by New user
Hi bettj

Could you please share .dll file . I can find it on web :headwall: :(