Serial port (COM interface) programming

Previous topic - Next topic

Kitty Hello

In order to communicate with the serial port, you can use this solution. It works on Win32 so far.

Create a new file, paste this contents into it and add it to your project:
Code (glbasic) Select


// --------------------------------- //
// Project: SerialPort
// Start: Wednesday, March 12, 2008
// IDE Version: 5.199




TYPE TComTimeouts
    ReadIntervalTimeout%
    ReadTotalTimeoutMultiplier%
    ReadTotalTimeoutConstant%
    WriteTotalTimeoutMultiplier%
    WriteTotalTimeoutConstant%
ENDTYPE




INLINE


} // namespace

#ifdef WIN32 // ### WINDOWS ###

#define DWORD unsigned long
#define WINAPI __stdcall
#define __in
#define __inout
#define __in_opt
#define BOOL int

typedef HANDLE HLOCAL;
#define INVALID_HANDLE ((HANDLE)-1)
HANDLE g_hSerialFile[8]={INVALID_HANDLE,INVALID_HANDLE,INVALID_HANDLE,INVALID_HANDLE,   INVALID_HANDLE,INVALID_HANDLE,INVALID_HANDLE,INVALID_HANDLE};

#define GENERIC_READ                     (0x80000000L)
#define GENERIC_WRITE                    (0x40000000L)
#define CREATE_NEW          1
#define CREATE_ALWAYS       2
#define OPEN_EXISTING       3
#define OPEN_ALWAYS         4



typedef struct _COMMTIMEOUTS {
  DWORD ReadIntervalTimeout;
  DWORD ReadTotalTimeoutMultiplier;
  DWORD ReadTotalTimeoutConstant;
  DWORD WriteTotalTimeoutMultiplier;
  DWORD WriteTotalTimeoutConstant;
} COMMTIMEOUTS,
*LPCOMMTIMEOUTS;

typedef struct _DCB {
  DWORD DCBlength;
  DWORD BaudRate;
  DWORD fBinary;
  DWORD fParity;
  DWORD fOutxCtsFlow;
  DWORD fOutxDsrFlow;
  DWORD fDtrControl;
  DWORD fDsrSensitivity;
  DWORD fTXContinueOnXoff;
  DWORD fOutX;
  DWORD fInX;
  DWORD fErrorChar;
  DWORD fNull;
  DWORD fRtsControl;
  DWORD fAbortOnError;
  DWORD fDummy2;
  WORD wReserved;
  WORD XonLim;
  WORD XoffLim;
  BYTE ByteSize;
  BYTE Parity;
  BYTE StopBits;
  char XonChar;
  char XoffChar;
  char ErrorChar;
  char EofChar;
  char EvtChar;
  WORD wReserved1;
} DCB,
*LPDCB;



extern "C"
{
#ifdef UNICODE
HANDLE __stdcall CreateFileW (const wchar_t*,DWORD,DWORD,void*,DWORD,DWORD,HANDLE);
#else
HANDLE __stdcall CreateFileA (const char*,DWORD,DWORD,void*,DWORD,DWORD,HANDLE);
#endif
BOOL __stdcall GetCommState(HANDLE, LPDCB);
BOOL __stdcall SetCommState(HANDLE, LPDCB);
BOOL __stdcall GetCommTimeouts(HANDLE, COMMTIMEOUTS*);
BOOL __stdcall SetCommTimeouts(HANDLE, COMMTIMEOUTS*);

BOOL __stdcall WriteFile(HANDLE, const void*, DWORD numwrite, DWORD* nwritten, const void* overlapped);
BOOL __stdcall ReadFile(HANDLE, void*, DWORD numread,  DWORD* nread,    const void* overlapped);

BOOL __stdcall CloseHandle(HANDLE);
DWORD __stdcall GetLastError(void);


#ifdef UNICODE
DWORD __stdcall FormatMessageW(DWORD dwFlags, void* lpSource, DWORD dwMessageId, DWORD dwLanguageId, wchar_t* lpBuffer, DWORD nSize, void *Arguments);
#else
DWORD __stdcall FormatMessageA(DWORD dwFlags, void* lpSource, DWORD dwMessageId, DWORD dwLanguageId, char* lpBuffer, DWORD nSize, void *Arguments);
#endif

int __stdcall MessageBoxW(HWND hWnd, const wchar_t* lpText, const wchar_t* lpCaption, unsigned int uType);

}


#if 0



#ifdef WINCE
#define SERDLL "corelib.dll"
#else
#define SERDLL "kernel32.dll"
#endif

#ifdef UNICODE
DECLARE(CreateFileW,  SERDLL, (const wchar_t*,DWORD,DWORD,void*,DWORD,DWORD,HANDLE),HANDLE);
#else
DECLARE(CreateFileA,  SERDLL, (const char*,DWORD,DWORD,void*,DWORD,DWORD,HANDLE),HANDLE);
#endif
DECLARE(GetCommState, SERDLL, (HANDLE, LPDCB), BOOL);
DECLARE(SetCommState, SERDLL, (HANDLE, LPDCB), BOOL);


DECLARE(GetCommTimeouts, SERDLL, (HANDLE, COMMTIMEOUTS*), BOOL);
DECLARE(SetCommTimeouts, SERDLL, (HANDLE, COMMTIMEOUTS*), BOOL);

DECLARE(WriteFile, SERDLL, (HANDLE, const void*, DWORD numwrite, DWORD* nwritten, const void* overlapped), BOOL);
DECLARE(ReadFile,  SERDLL, (HANDLE, void*, DWORD numread,  DWORD* nread,    const void* overlapped), BOOL);
DECLARE(CloseHandle, SERDLL, (HANDLE), BOOL);

// error handling
DECLARE(GetLastError, SERDLL, (void), DWORD);
#ifdef UNICODE
DECLARE(FormatMessageW, SERDLL, (DWORD dwFlags, void* lpSource, DWORD dwMessageId, DWORD dwLanguageId, wchar_t* lpBuffer, DWORD nSize, void *Arguments), DWORD);
#else
DECLARE(FormatMessageA, SERDLL, (DWORD dwFlags, void* lpSource, DWORD dwMessageId, DWORD dwLanguageId, char* lpBuffer, DWORD nSize, void *Arguments), DWORD);
#endif



#endif





#else // ### NOT WINDOWS ###




// Prototypes for Linux
extern "C"
{
typedef long off_t;
typedef int speed_t;
#define ssize_t int
typedef unsigned int size_t;
typedef int mode_t;
#define O_NOCTTY   0x8000
#define O_RDWR   2
#define O_NONBLOCK 0x0004
#define O_NDELAY O_NONBLOCK

#define TCIFLUSH 0
#define TCSANOW 0

#define EAGAIN          11

/* c_cflag bit meaning */
#define CBAUD   0000017
#define  B0     0000000         /* hang up */
#define  B50    0000001
#define  B75    0000002
#define  B110   0000003
#define  B134   0000004
#define  B150   0000005
#define  B200   0000006
#define  B300   0000007
#define  B600   0000010
#define  B1200  0000011
#define  B1800  0000012
#define  B2400  0000013
#define  B4800  0000014
#define  B9600  0000015
#define  B19200 0000016
#define  B38400 0000017
#define EXTA B19200
#define EXTB B38400
#define CSIZE   0000060
#define   CS5   0000000
#define   CS6   0000020
#define   CS7   0000040
#define   CS8   0000060
#define CSTOPB  0000100
#define CREAD   0000200
#define CPARENB 0000400
#define CPARODD 0001000
#define HUPCL   0002000
#define CLOCAL  0004000
#define CIBAUD  03600000                /* input baud rate (not used) */
#define CRTSCTS 020000000000            /* flow control */


#define NCCS 17
struct termios {
        unsigned long c_iflag;          /* input mode flags */
        unsigned long c_oflag;          /* output mode flags */
        unsigned long c_cflag;          /* control mode flags */
        unsigned long c_lflag;          /* local mode flags */
        unsigned char c_line;           /* line discipline */
        unsigned char c_cc[NCCS];       /* control characters */
};


int open(const char *pathname, mode_t mode);
int errno;
char *strerror(int errnum);

int tcgetattr(int fildes, struct termios *termios_p);
int tcsetattr(int fildes, int optional_actions, struct termios *termios_p);

int tcflush(int fildes, int queue_selector);
int cfsetospeed(struct termios *termios_p, speed_t speed);



int ioctl(int fildes, int request, ... /* arg */);
int open(const char *pathname, int flags);
ssize_t read(int fd, void *buf, size_t count);
int write(int fildes, const char * buf, off_t count);
int close(int fildes);
int printf(char*, ...);
}

#define INVALID_HANDLE 0
static int g_hSerialFile[8] = {INVALID_HANDLE,INVALID_HANDLE,INVALID_HANDLE,INVALID_HANDLE,  INVALID_HANDLE,INVALID_HANDLE,INVALID_HANDLE,INVALID_HANDLE};



// closes the serial port
void CloseAdrPort(int iSlot)
{
// you may want to restore the saved port attributes
    if (g_hSerialFile[iSlot] != INVALID_HANDLE)
    {
        close(g_hSerialFile[iSlot]);
    } // end if
    g_hSerialFile[iSlot] = INVALID_HANDLE;
} // end CloseAdrPort


// opens the serial port
// return code:
//   > 0 = fd for the port
//   -1 = open failed

int OpenAdrPort(int iSlot, int iRS232Port)
{
DGStr sPortName = "/dev/ttyS"+iRS232Port;

    // make sure port is closed
    CloseAdrPort();

    g_hSerialFile[iSlot] = open(sPortName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd < 0)
    {
        STDERR (CGStr("open error ")+DGNat(errno)+CGStr(" ")+CGStr(strerror(errno))+CGStr("\n"));
    }
    return g_hSerialFile[iSlot];
}

// 0 OK
// <0 failed
int SetAdrPortMode(int iSlot, int baud)
{
if(g_hSerialFile[iSlot] != INVALID_HANDLE)
    {
        struct termios my_termios;
        tcgetattr(g_hSerialFile[iSlot], &my_termios);
        /*
        printf("fd is %d\n", g_hSerialFile[iSlot]);
        // NOTE: you may want to save the port attributes
        //       here so that you can restore them later
        printf("old cflag=%08x\n", my_termios.c_cflag);
        printf("old oflag=%08x\n", my_termios.c_oflag);
        printf("old iflag=%08x\n", my_termios.c_iflag);
        printf("old lflag=%08x\n", my_termios.c_lflag);
        printf("old line=%02x\n", my_termios.c_line);
*/

        tcflush(g_hSerialFile[iSlot], TCIFLUSH);


        my_termios.c_cflag = 0;
switch(baud)
{
#define MKB(a) case a: my_termios.c_cflag |= B ## a; break;
MKB(0);
MKB(50);
MKB(75);
MKB(110);
MKB(150);
MKB(200);
MKB(300);
MKB(600);
MKB(1200);
MKB(1800);
MKB(2400);
MKB(4800);
MKB(9600);
MKB(19200);
MKB(38400);
default:
return -1;
}

        my_termios.c_cflag |=  (CS8 |CREAD | CLOCAL | HUPCL);

        cfsetospeed(&my_termios, B9600);
        tcsetattr(g_hSerialFile[iSlot], TCSANOW, &my_termios);

        printf("new cflag=%08x\n", my_termios.c_cflag);
        printf("new oflag=%08x\n", my_termios.c_oflag);
        printf("new iflag=%08x\n", my_termios.c_iflag);
        printf("new lflag=%08x\n", my_termios.c_lflag);
        printf("new line=%02x\n", my_termios.c_line);
    } // end if
    return 0;
}





// writes zero terminated string to the serial port
// return code:
//   >= 0 = number of characters written
//   -1 = write failed
int WriteAdrPort(int iSlot, const char* psOutput, int outlen)
{
    return write(g_hSerialFile[iSlot], psOutput, outlen);
}

// read string from the serial port
// return code:
//   >= 0 = number of characters read
//   -1 = read failed
int ReadAdrPort(int iSlot, char* psResponse, int iMax)
{
    int iIn;
    iIn = read(g_hSerialFile[iSlot], psResponse, iMax-1);
    if (iIn < 0)
    {
    if (errno == EAGAIN)
    {
return 0; // assume that command generated no response
}
    }
    return iIn;
}



#endif // ### WIN / LINUX ###







namespace __GLBASIC__ {

ENDINLINE

FUNCTION DebugLastError:
INLINE
#ifdef WIN32
DWORD err = ::GetLastError();

// DGStr serr = DGStr("Error number: ") + (DGNat)err;
// MessageBoxW((HWND)GLBASIC_HWND(), serr.w_str(), L"Debug:", 1);

#ifdef UNICODE
wchar_t lpMsgBuf[1024];
FormatMessageW(0x00001000 | 0x00000200, NULL, err, 1<<10, lpMsgBuf, 1024, NULL);
// MessageBoxW((HWND)GLBASIC_HWND(), lpMsgBuf, L"Debug:", 1);
#else
char lpMsgBuf[1024];
FormatMessageA(0x00001000 | 0x00000200, NULL, err, 1<<10, lpMsgBuf, 1024, NULL);
#endif

DEBUG(DGStr(lpMsgBuf));
DEBUG(DGStr("\n"));
#else
DEBUG(CGStr(strerror(errno)) + CGStr("\n"));
#endif
ENDINLINE
ENDFUNCTION


FUNCTION COM_Open: iSlot%, port%, bRead, bWrite
INLINE
#ifdef WIN32
DGStr port_Str = CGStr("COM") + port + CGStr(":");

#ifdef UNICODE
    g_hSerialFile[iSlot] = ::CreateFileW(port_Str.w_str(), (bRead?GENERIC_READ:0)|(bWrite?GENERIC_WRITE:0),0,0,OPEN_EXISTING, 0,0);
    #else
    g_hSerialFile[iSlot] = ::CreateFileA(port_Str.c_str(), (bRead?GENERIC_READ:0)|(bWrite?GENERIC_WRITE:0),0,0,OPEN_EXISTING, 0,0);
    #endif

    if(g_hSerialFile[iSlot] == INVALID_HANDLE)
    {
    port_Str = CGStr("Error opening port: ")+port_Str;
    DebugLastError();
    }
    return (g_hSerialFile[iSlot]!=INVALID_HANDLE) ? TRUE:FALSE;
#else
return (OpenAdrPort(iSlot, port) < 0) ? FALSE:TRUE;
#endif
ENDINLINE
ENDFUNCTION

FUNCTION COM_Close: iSlot%
INLINE
#ifdef WIN32
if(g_hSerialFile[iSlot] != INVALID_HANDLE)
{
    ::CloseHandle(g_hSerialFile[iSlot]);
    g_hSerialFile[iSlot]=INVALID_HANDLE;
}
#else
CloseAdrPort(iSlot);
#endif
ENDINLINE
ENDFUNCTION

// parity: 0 = none, 1=even, 2=mark, 3=space
FUNCTION COM_SetComState: iSlot%, baud, parity, databits, stopbits
INLINE
#ifdef WIN32
    DCB MyDCB;
    for(int i=0; i<sizeof(DCB); ++i)
    {
        ((char*)&MyDCB)[i] = 0;
    }
    MyDCB.DCBlength = sizeof(DCB);
    if( GetCommState(g_hSerialFile[iSlot], &MyDCB))
    {
        MyDCB.BaudRate=(int)baud;
        MyDCB.Parity = (int)parity;
        MyDCB.ByteSize = (int)databits;
        MyDCB.StopBits = (int)stopbits;
        return SetCommState(g_hSerialFile[iSlot], &MyDCB);
    }
    return FALSE;
    #else
    SetAdrPortMode(iSlot, baud);
    // #NEED parity and shit
    #endif
ENDINLINE
ENDFUNCTION


FUNCTION COM_SetCommTimeouts%: iSlot%, tout AS TComTimeouts
INLINE
#ifdef WIN32
COMMTIMEOUTS co;
    co.ReadIntervalTimeout        =tout.ReadIntervalTimeout;
    co.ReadTotalTimeoutMultiplier =tout.ReadTotalTimeoutMultiplier;
    co.ReadTotalTimeoutConstant   =tout.ReadTotalTimeoutConstant;
    co.WriteTotalTimeoutMultiplier=tout.WriteTotalTimeoutMultiplier;
    co.WriteTotalTimeoutConstant  =tout.WriteTotalTimeoutConstant;
    ::SetCommTimeouts(g_hSerialFile[iSlot], &co);
#else
// ???
#endif
ENDINLINE
ENDFUNCTION

FUNCTION COM_Read$: iSlot%, nbytes% = 1024
INLINE
DGStr buf;
#ifdef WIN32
    DWORD nread =0;
    ReadFile(g_hSerialFile[iSlot], buf.getbuffer(nbytes+1), nbytes, &nread, NULL);
    #else
    int nread = ReadAdrPort(iSlot, buf.getbuffer(nbytes+1), nbytes);
    #endif

    if(nread > 0)
    {
        buf[(int)nread] = '\0';
    buf.setlength(nread);
        return buf;
    }
    return DGStr("");
ENDINLINE
ENDFUNCTION


FUNCTION COM_Write%: iSlot%, text$
INLINE
#ifdef WIN32
    DWORD nwrite =0;
    if( WriteFile(g_hSerialFile[iSlot], text_Str.c_str(), (DWORD)LEN(text_Str), &nwrite, NULL))
    return nwrite;
    return 0;
#else
return WriteAdrPort(iSlot, text_Str.c_str(), LEN(text_Str));
#endif
ENDINLINE
ENDFUNCTION





Here's a test program for the library:

Code (glbasic) Select

// --------------------------------- //
// Project: SerialPort
// Start: Wednesday, March 12, 2008
// IDE Version: 5.199


port%  = 1 // COM1 / /dev/ttyS1
baud   = 19200 // baud rate
parity = 0     // 0=none, 1=odd, 2=even, 3=mark, 4=space
datab  = 8     // data bits
stop   = 0     // stop bits: 0=1 (ONE) stop bit, 1=1.5 bits, 2=2 bits

// Open COM port for writing
IF COM_Open(0, port, FALSE, TRUE) = FALSE THEN Error("open COM2 failed")

// Set Baud rate and handshake
IF COM_SetComState(0, baud, parity, datab, stop) = FALSE THEN Error("Failed to set com state")





// optionally set com timeouts
//    LOCAL tout AS TComTimeouts
//    tout.ReadIntervalTimeout        = 0xffffffff
//    tout.ReadTotalTimeoutMultiplier = 0
//    tout.ReadTotalTimeoutConstant   = 0
//    tout.WriteTotalTimeoutMultiplier= 0
//    tout.WriteTotalTimeoutConstant  = 0
//    COM_SetCommTimeouts(0, tout)

// Write something
    IF COM_Write(0, "ABC\n")=0 THEN Error("Failed to write data")

// Read something - make sure you COM_Open(..., TRUE, ...) before
// read$ = COM_Read$()

// close COM port
    COM_Close(0)

MOUSEWAIT
END


FUNCTION Error: txt$
    PRINT txt$, 0,0
    SHOWSCREEN
    MOUSEWAIT
    END
ENDFUNCTION


Neurox

Hi Gernot,
I've found a two errors in code :

Code (glbasic) Select

FUNCTION COM_SetComState: baud, parity, databits, stopbits
INLINE
    DCB MyDCB;
    for(int i=0; i
    {
        ((char*)&MyDCB)[i] = 0;
    }

First "For cycle" not complete

Code (glbasic) Select

FUNCTION COM_Read: buffer[], nbytes
INLINE
    char buf[1024];
    DWORD nread =0;
    if( ReadFile(g_hSerialFile, buf, nbytes, &nread, 0))
    {
        DIM(buffer, nread);
        for(int i=0; i
            buffer(i) = buf[i];

Second "For cycle" not complete

Bye,
Neurox
Paolo Borzini | paolo@borzini.it
The WhiteFly Software | www.thewhitefly.it
Service on line for screen printers | www.4pellicole.it

Kitty Hello

This forum is starting to make me angry. It just swallowed that text. Maybe FireFox was the problem.

Neurox

Paolo Borzini | paolo@borzini.it
The WhiteFly Software | www.thewhitefly.it
Service on line for screen printers | www.4pellicole.it

javiero

Hi Gernot

This will work in WINCE ?

My idea is get GPS data from a serial port.

Please excuseme my very bad english.

Kind Regards.

Kitty Hello

Very good question. It seems that the functions are there. Though Windows CE builds unicode so there might be some tweaks required.
If you could build it and try, please? There's surely a way to fix it then.

If you make a project to read GPS from a serial device, please let us know about your progress. I think many will like the idea to get GPS data from any device, so you should seperate GPS handling from the input source. Reading NMEA strings seems the easiest and lowest common protocol.

javiero


I'm waiting for a wince gps device.

When I have one, i will try it.

Thanks. ;-)

Moru

This topic looks interesting, is there any updated version of it or is this it? :-)

trucidare

yep yesterday kittie solved the last problems with reading com port on wince devices.
MacBook Pro 2,2 GHz Core 2 Duo, 4 GB RAM, 160 GB HDD, 8600M GT
Core i3 - 3,07 GHz, 8 GB Ram, 2.5 TB HDD, Geforce GTX 260+ OC

Kitty Hello

OK, updated in the first topic.

MrTAToad

Ocean will be interested in this :)

Julian

Sorry for pushing this old thread but i can't get this to work with my bluetooth GPS receiver. Normally it takes some time (for example with putty) to open the comport (bluetooth needs some time to connect), but with this code I instantly get the error "Failed to set com state". The Comport Settings are correct. Here is my Code:
Code (glbasic) Select
Running = TRUE
error$ = ""

port%  = 10 // COM10
baud   = 9600 // baud rate
parity = 0     // 0=none, 1=odd, 2=even, 3=mark, 4=space
datab  = 8     // data bits
stop   = 0     // stop bits: 0=1 (ONE) stop bit, 1=1.5 bits, 2=2 bits

// Open COM port for reading
IF COM_Open(0, port, TRUE, FALSE) = FALSE THEN error$="open COM10 failed"

// Set Baud rate and handshake
IF COM_SetComState(0, baud, parity, datab, stop) = FALSE THEN error$="Failed to set com state"

WHILE Running
IF KEY(01) = 1
Running = FALSE
ENDIF
IF error$ <> ""
PRINT error$,0,0
ELSE
read$ = COM_Read$(0,1)
PRINT read$, 0,0
ENDIF
SHOWSCREEN
WEND
COM_Close(0)
END
I am using Windows 7 X64 (don't know if thats relevant)

Kitty Hello

Try this function for a more detailed error, maybe?
FUNCTION DebugLastError:


Julian

#13
Ok with the debug mode and "DebugLastError()" between "IF COM_SetComState [...]" and the main Loop I get
QuoteDie Syntax für den Dateinamen, Verzeichnisnamen oder die Datenträgerbezeichnung ist falsch.

Das Handle ist ungültig.
Which is strange because I know, that the settings are correct (as I said it works in other programs like putty or hterm). The first error accrues even before DebugLastError() is called.
//Edit: OK this is very strange. I Changed the Devices Bluetooth Comport to COM3 and now its working.

Turbo TURTLE

Hi everyone,

I see the topic is very old, but i'm in great need to communicate with an Arduino via USB.
I tried to apply the code in post #1, but it gives an error. The output of the compiler:
_______________________________________
*** Configuration: WIN32 ***
precompiling:
GPC - GLBasic Precompiler V.10.053 SN:61acac25 - 3D, NET
Wordcount:25 commands
compiling:
C:\Users\OLUSUM~1\AppData\Local\Temp\glbasic\gpc_temp1.cpp: In function `__GLBASIC__::DGStr __GLBASIC__::COM_Read_Str(DGNat, DGNat)':
C:\Users\OLUSUM~1\AppData\Local\Temp\glbasic\gpc_temp1.cpp:1057: error: invalid token
C:\Users\OLUSUM~1\AppData\Local\Temp\glbasic\gpc_temp1.cpp:1057: error: non-lvalue in unary `&'
C:\Users\OLUSUM~1\AppData\Local\Temp\glbasic\gpc_temp1.cpp:1057: error: stray '\' in program
C:\Users\OLUSUM~1\AppData\Local\Temp\glbasic\gpc_temp1.cpp:1057: error: invalid token
*** FATAL ERROR - Please post this output in the forum
_______________________________________
*** Finished ***
Elapsed: 1.1 sec. Time: 15:11
Build: 0 succeeded.
*** 1 FAILED ***

I also downloaded an older version of glbasic (12.308) but it didn't work.