/********************************************************************/
/*                                                                  */
/*  Uarttst.cpp: Sample UART Loopback Test Program                  */
/*                                                                  */
/*  This program sends a stream of data to COM1 at regular time     */
/*  intervals specified by the value of the constant TIME_INTERVAL. */
/*  Refer to the comments throughout the program for more details.  */
/*                                                                  */
/*  (c)Copyright Sealevel Systems, Inc., 2001                       */
/*                                                                  */
/*  SEALEVEL SYSTEMS INCORPORATED.                                  */
/*  155 Technology Place                                            */
/*  P.O. Box 830                                                    */
/*  Liberty, SC 29657  USA                                          */
/*  (864) 843-4343                                                  */
/*  (864) 843-3067 FAX                                              */
/*                                                                  */
/********************************************************************/

#include <stdio.h>
#include <conio.h>
#include <windows.h>

#define STR_TO_DISP      6   // Number of test strings to use
#define TIMEOUT_CONSTANT 5
#define ESC              27
#define STR_LENGTH       49
#define TIME_INTERVAL    50  // This is the counter that determines
                             // the time interval between the outputs
                             // of the test string. If you are 
                             // receiving hacked up data, you will 
                             // either need to increase this value or
                             // the data rate (or both). See below for
                             // more information. 

HANDLE  WinStdOut = NULL;    // Console Handle


////////////////////////////////////cursor()////////////////////////////////////

void cursor(int x_pos, int y_pos)
{           
    // Moves the cursor to the position specified by _curpos;
    
    COORD Newcurpos;
    Newcurpos.X = x_pos;
    Newcurpos.Y = y_pos;
    SetConsoleCursorPosition(WinStdOut, Newcurpos);

}// cursor()

////////////////////////////////Erase_Screen()////////////////////////////////////

void Erase_Screen(void)
{
    // Fills the section of the screen that prints received data with spaces.
    
    int  c;
    char erasestr[81];

    for (c = 0; c < 81; c++)
        erasestr[c] = ' ';  // space

    erasestr[80] = '\0';

    cursor(0, 8);
    
    for (c = 8; c < 24; c++)
        printf(erasestr);

}// Erase_Screen()


////////////////////////////////////main()/////////////////////////////////////

int main(void)
{
    int  cur_str      = 0;             // Current test string to send
    int  timer        = TIME_INTERVAL;
    int  key_pressed  = 0;
    char teststring[6][STR_LENGTH]; 
    char rec_str[STR_LENGTH];
	bool bStatus;
    
    int x_pos = 0, y_pos = 8;
    DWORD        bytes_written = 0;         // Number of bytes written to port
    DWORD        bytes_read    = 0;         // Number of bytes read from port
    HANDLE       comport       = NULL;      // Handle for COM port
    DCB          comSettings;               // Contains various port settings
    COMMTIMEOUTS tempComTimeouts;           // Our temporary time-outs for COM1   
    COMMTIMEOUTS savedComTimeouts;          // Stores the original time-outs
    
    CONSOLE_SCREEN_BUFFER_INFO consoleInfo; // Used to get cursor position.

    strcpy(teststring[0], "The quick brown fox jumped over the lazy dog. \n\r\0");
    strcpy(teststring[1], "(c)Copyright Sealevel Systems Inc., 1999.     \n\r\0");
    strcpy(teststring[2], "abcdefghijklmnopqrstuvwxyz1234567890()+;.,-/< \n\r\0");
    strcpy(teststring[3], "This program created with Microsoft Visual C++\n\r\0");
    strcpy(teststring[4], "On an Intel Pentium with 32 megs of RAM.      \n\r\0");
    strcpy(teststring[5], "rig rIg riG rIG Rig RIg RIG RiG               \n\r\0");    
    
    printf("Sample UART Loopback Test Program.\n");
    printf("(c)Copyright Sealevel Systems, Inc., 1999.\n\n");
        
    // Open COM port
    if ((comport = 
         CreateFile("\\\\.\\COM1",
                    GENERIC_READ | GENERIC_WRITE, // for reading and writing
                    0,                            // exclusive access
                    NULL,                         // no security attributes
                    OPEN_EXISTING,              
                    FILE_ATTRIBUTE_NORMAL,
                    NULL)) == INVALID_HANDLE_VALUE)
    {
        printf("Unable to open COM1.\n\n");
        printf("Press any key to exit.");
        getch();
        return(1);
    }

    printf("COM1 opened.\n\n");

    WinStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

    // Save time-out parameters for COM1 
    bStatus = GetCommTimeouts(comport,&savedComTimeouts);
	if (bStatus == 0)
    {
        printf("GetCommTimeouts failure.\n\n");
        printf("Press any key to exit.");
        getch();
        return(1);
    }
    

    // Set our time-outs. 
    // When ReadIntervalTimeout and ReadTotalTimeoutMultiplier are
    // set to MAXDWORD and ReadTotalTimeoutConstant is a value 
    // greater than 0 and less than MAXWORD, the following occurs
    // when ReadFile() is called:
    //
    // - If there are any characters in the input buffer, ReadFile()
    // returns immediately with the characters in the buffer, instead
    // of waiting until the end of the transmission. 
    //
    // - If there are no characters in the input buffer, ReadFile() 
    // waits until a character arrives and then returns immediately.
    // (Again without waiting until the end of the transmission.)
    //
    // - If no character arrives within the time specified by 
    // ReadTotalTimeoutConstant, ReadFile times out.
    //
    // Information obtained from the "COMMTIMEOUTS" section of the 
    // MSDN Library.

    tempComTimeouts.ReadIntervalTimeout         = MAXDWORD; 
    tempComTimeouts.ReadTotalTimeoutMultiplier  = MAXDWORD; 
    tempComTimeouts.ReadTotalTimeoutConstant    = TIMEOUT_CONSTANT;
    tempComTimeouts.WriteTotalTimeoutMultiplier = TIMEOUT_CONSTANT;
    tempComTimeouts.WriteTotalTimeoutConstant   = TIMEOUT_CONSTANT;
    bStatus = SetCommTimeouts(comport,&tempComTimeouts);
	if (bStatus == 0)
    {
        printf("SetCommTimeouts failure.\n\n");
        printf("Press any key to exit.");
        getch();
        return(1);
    }


    // Set Port parameters.
    // We make a call to GetCommState() first in order to fill
    // the comSettings structure with all the necessary values.
    // Then we change the ones we want and call SetCommState().

    bStatus = GetCommState(comport, &comSettings);
	if (bStatus == 0)
    {
        printf("GetCommState failure.\n\n");
        printf("Press any key to exit.");
        getch();
        return(1);
    }
    comSettings.BaudRate = CBR_9600;
    comSettings.StopBits = ONESTOPBIT;
    comSettings.ByteSize = 8;
    comSettings.Parity   = NOPARITY;
    comSettings.fParity  = FALSE;
    bStatus = SetCommState(comport, &comSettings);
	if (bStatus == 0)
    {
        printf("GetCommTimeouts failure.\n\n");
        printf("Press any key to exit.");
        getch();
        return(1);
    }
    
    printf("Sending string: ");
    printf("\n\nReceived: ");

    while(key_pressed != ESC)
    {
        if (kbhit())
            key_pressed = getch();
        
        //Send data. if succesful, WriteFile() returns non-zero 
        if ((timer == TIME_INTERVAL))
        {
            cursor(16,5);
            printf(teststring[cur_str]);
            
            WriteFile(comport,              // Handle
                      &teststring[cur_str], // Outgoing data
                      STR_LENGTH,           // Number of bytes to write
                      &bytes_written,       // Number of bytes written
                      NULL);
            
            if (cur_str++ == (STR_TO_DISP - 1))
                cur_str = 0;

            timer = 0;
        
        }

        // Read data.
        // Because of the way the time-outs are set (see above), 
        // Readfile() will actually never read the whole string in
        // one call. Rather, it receives the data in chunks which 
        // is then printed to the console. If TIME_INTERVAL is too
        // small or the data rate too slow, Readfile() will not have
        // time to read all the data before more is sent.
        
        // If succesful, ReadFile() returns non-zero
        ReadFile(comport,       // Handle
                 &rec_str,      // Incoming data
                 STR_LENGTH,    // Number of bytes to read
                 &bytes_read,   // Number of bytes read
                 NULL);
     
        if (bytes_read >= 1)
        {
            rec_str[bytes_read] = 0;
            if (y_pos == 24)
            {
                y_pos = 8;
                Erase_Screen();
            }
            cursor(x_pos, y_pos);   
            printf("%s", rec_str);
            
            
            GetConsoleScreenBufferInfo(WinStdOut, &consoleInfo);
            x_pos = consoleInfo.dwCursorPosition.X;
            y_pos = consoleInfo.dwCursorPosition.Y;

        }
        
        timer++;
    }

    // Restore time-out parameters 
    SetCommTimeouts(comport,&savedComTimeouts);
    CloseHandle(comport);

    cursor(0, y_pos + 2);
        
    return(0);

}// main()