Symbian
Symbian OS Library

SYMBIAN OS V9.3

[Index] [Spacer] [Previous] [Next]



GlassTerm: glass teletype terminal


Example code

The source code for this example application can be found in the directory:

examples\SerialComms\ServerClientSide\GlassTerm

These are the main files contained in the examples. Some extra files may be needed to run the examples, and these will be found in the appropriate examples directory.

// GlassTerm.CPP
//
// Copyright (c) 2000-2005 Symbian Software Ltd.  All rights reserved.
//

// Purpose: Glass Term : read/Write from keyboard/serial port : example code for SDK

// This uses the first serial port on the system, and offers a choice between different
// handshaking modes.  All control characters apart from carriage returns and line feeds are
// displayed on the screen with ^ carets in from of the the ASCII equivalent (so tab = ^I)

// The ESC key is used to end the program

// Note :

// In order for this program to do anything, the serial port should be connected to something.
// A modem is of course quite suitable.  In the absence of a modem, a loopback plug with the
// receive and transmit lines connected will echo all serial port output to the screen.  In
// the absence of a loopback plug, a bent paper clip or somethings similar connecting pins 2 and 3
// on any 25 or 9 way serial connector will do exactly the same thing.  This last suggestion
// is something that is carried out entirely at your own risk, and you should be very careful
// neither to connect any other pins by mistake nor to push the paper clip in too far !

// If using the bent paper clip, you'll find that in order for the hardware handshaking option
// to work, a second bent paper clip connecting pins 4 and 5 on a 25-way connector or pin 7 and 8
// on a 9-way connector will also be needed - this second paper clip is needed to connect RTS to CTS

// Note: this sample program now shows how to support infra-red as well
// as RS232 and also systems with multiple serial ports and multiple
// possible CSYs.

#include <e32base.h>
#include <e32test.h>
#include <e32svr.h>
#include <c32comm.h>
#include <f32file.h>

#include "CommonFiles.h"

// first define our device driver names

_LIT(LDD_NAME,"ECOMM");

#if defined (__WINS__)
_LIT(PDD_NAME,"ECDRV");
#else
_LIT(PDD_NAME,"EUART1");
#endif

// next define an arbitrary buffer size and Hundred seconds in microseconds

const TInt KBufSize (512);
const TInt KOneHundredSecond (100000000);

// short literals for use in doExampleL() declared at file scope
_LIT(KMessage2,"%c\n");
_LIT(KMessage14,"^%c");
_LIT(KMessage15,"%c");
_LIT(KColons,"::");

// utility function to print received text
void printReceivedText(TDes8& localInputBuffer,TInt numberRead);

LOCAL_C void doExampleL ()
    {
    _LIT(KMessage0,"Select S for RS232 Serial or R for InfraRed port : ");
    _LIT(KMessage1,"Select 0 for no handshaking, 1 for CTS/RTS and 2 for XON/XOFF :");
    _LIT(KMessage4,"Loading device drivers\n");
    _LIT(KMessage5,"Starting comms server\n");
    _LIT(KMessage6,"Connecting to comms server\n");
    _LIT(KMessage7,"Loading %S.CSY module\n");
    _LIT(KMessage8,"%S has %S available as %S::%u to %S::%u\n");
    _LIT(KMessage9,"Opened %S\n");
    _LIT(KMessage10,"Configuring Serial port for 115200 bps 8 bits no parity 1 stop\n");
    _LIT(KMessage11,"Powering up port\n");
    _LIT(KMessage12,"\nDisconnecting\n");
    _LIT(KMessage13,"\nWrite Failed %d\n");
    _LIT(KMessage16,"\nRead failed %d\n");
    _LIT(KMessage17,"Closed %S\n");
    _LIT(KMessage18,"Closing server connection\n");
    _LIT(KMessage19,"Comms server reports we have %u comms modules loaded\n");
    _LIT(KMessage20,"Using the lowest %S out of %S::%u to %S::%u\n");
    
    _LIT(KPanic,"StraySignal");
    _LIT(RS232,"ECUART1");
    _LIT(IRCOMM,"IRCOMM");

    TBuf16 < 6 > csyName;

    TUint8 csyMode;
    const TUint8 mask=0xdf; // this mask 0xdf turns lower to upper case

    console->Printf (KMessage0);
    do
        csyMode = STATIC_CAST(TUint8,console->Getch () & mask); 
    while ((csyMode < 'R') || (csyMode > 'S'));
    console->Printf (KMessage2, csyMode);

    if (csyMode=='S')
        csyName.Copy(RS232);
    else
        csyName.Copy(IRCOMM);



    TKeyCode handshakingMode;
    console->Printf (KMessage1);
    do
        handshakingMode = console->Getch ();
    while ((handshakingMode < '0') || (handshakingMode > '2'));
    console->Printf (KMessage2, handshakingMode);


    // Under WINS we must force a link to the file server
    // so that we're sure we'll be able to load the device drivers.
    // On a MARM implementation, this code would not
    // be required because higher level components
    // will automatically have started the services.

#if defined (__WINS__)
    _LIT(KMessage3,"Connect to file server\n");
    console->Printf (KMessage3);
    RFs fileServer;
    User::LeaveIfError (fileServer.Connect ());
    fileServer.Close ();
#endif


    // Load the physical and logical device drivers
    // Symbian OS will automatically append .PDD and .LDD and
    // search /System/Libs on all drives starting from C:
    // If EIKON has done this, they'll already exist -
    // no harm will have been done

    console->Printf (KMessage4);
    TInt r = User::LoadPhysicalDevice (PDD_NAME);
    if (r != KErrNone && r != KErrAlreadyExists)
        User::Leave (r);
    r = User::LoadLogicalDevice (LDD_NAME);
    if (r != KErrNone && r != KErrAlreadyExists)
        User::Leave (r);

    // Both WINS and EIKON will have started the comms server process.
    // (this is only really needed for ARM hardware development racks)

#if !defined (__WINS__)
    console->Printf (KMessage5);
    r = StartC32 ();
    if (r != KErrNone && r != KErrAlreadyExists)
        User::Leave (r);
#endif

    // Now (at last) we can actually connect to the comm server

    console->Printf (KMessage6);
    RCommServ server;
    User::LeaveIfError (server.Connect ());

    // Load the CSY module
    // Symbian OS will automatically search \System\Libs
    // on all drives starting from C:

    console->Printf (KMessage7,&csyName);
    r = server.LoadCommModule (csyName);
    User::LeaveIfError (r);

    // if we know our machine architecture we can just go ahead and open (say) COMM::0
    // however, for machine independence we are better off looking up that information

    // the oddly-named NumPorts function actually tells us how many CSYs are loaded
    // this isn't 0 since we've just loaded one ...

    TInt numPorts;
    r = server.NumPorts (numPorts);
    User::LeaveIfError (r);
    console->Printf (KMessage19,numPorts);

    // we can get port information for each loaded CSY in turn (note we
    // index them from 0) - we can find out the number of ports supported
    // together with their names, and their description. The information is
    // returned in a TSerialInfo structure together with the name of the
    // CSY that we've indexed

    TSerialInfo portInfo;
    TBuf16 < 12 > moduleName;

    for (TInt index=0 ; index < numPorts ; index++)
        {
        r = server.GetPortInfo (index, moduleName, portInfo);
        User::LeaveIfError (r);
        console->Printf (KMessage8,
                              &moduleName,
                              &portInfo.iDescription,
                              &portInfo.iName,
                              portInfo.iLowUnit,
                              &portInfo.iName,
                              portInfo.iHighUnit);
        }

    // However, we are really only interested in using the CSY that we've
    // just loaded up ourselves.  We could find out its portInfo by
    // comparing the moduleName returned by the version of GetPortInfo we
    // just used to the name of the CSY we loaded, but there's a better
    // version of GetPortInfo we can use, which just takes the name of a CSY
    // as a parameter. We'd expect to find this informtion is an exact 
    // duplicate of the indexed portInfo for the last loaded CSY
    // Our example code will use the lowest possible port (why not?)

    r = server.GetPortInfo (csyName, portInfo);
    console->Printf (KMessage20,
                          &portInfo.iDescription,
                          &portInfo.iName,
                          portInfo.iLowUnit,
                          &portInfo.iName,
                          portInfo.iHighUnit);

    // Now let's use a few Symbian OS functions to construct a descriptor for the
    // name of the lowest port our CSY supports -
    // The name can  be as long as a TSerialInfo.iName plus a
    // couple of colons and digits

    TBuf16 < KMaxPortName + 4 > portName; // declare an empty descriptor buffer
    portName.Num (portInfo.iLowUnit);   // put in the port number in ASCII
    portName.Insert (0, KColons);   // stick in a couple of colons
    portName.Insert (0, portInfo.iName); // and lead off with the iName

    // and at last we can open the first serial port,which we do here in exclusive mode

    RComm commPort;
    console->Printf (KMessage9, &portName);
    r = commPort.Open (server, portName, ECommExclusive);
    User::LeaveIfError (r);

    // Now we can configure our serial port
    // we want to run it at 115200 bps 8 bits no parity (why not?)
    // so maybe we ought to get of its capabilities and check it can
    // do what we want before going ahead

    TCommCaps ourCapabilities;
    commPort.Caps (ourCapabilities);

    if (((ourCapabilities ().iRate & KCapsBps115200) == 0) ||
         ((ourCapabilities ().iDataBits & KCapsData8) == 0) ||
         ((ourCapabilities ().iStopBits & KCapsStop1) == 0) ||
         ((ourCapabilities ().iParity & KCapsParityNone) == 0))
        User::Leave (KErrNotSupported);

    console->Printf (KMessage10);

    TCommConfig portSettings;
    commPort.Config (portSettings);
    portSettings ().iRate = EBps115200;
    portSettings ().iParity = EParityNone;
    portSettings ().iDataBits = EData8;
    portSettings ().iStopBits = EStop1;

    // as well as the physical characteristics, we need to set various logical ones
    // to do with handshaking, behaviour of reads and writes and so so

    portSettings ().iFifo = EFifoEnable;
    if (handshakingMode == '2')
        portSettings ().iHandshake = (KConfigObeyXoff | KConfigSendXoff); // for xon/xoff
    else if (handshakingMode == '1')
        portSettings ().iHandshake = (KConfigObeyCTS | KConfigFreeRTS);    // for cts/rts
    else
        portSettings ().iHandshake = KConfigFailDSR; // for no handshaking

    portSettings ().iTerminator[0] = 10;
    portSettings ().iTerminatorCount = 1;          // so that we terminate a read on each line feed arrives

    r = commPort.SetConfig (portSettings);
    User::LeaveIfError (r);

    // now turn on DTR and RTS, and set our buffer size

    commPort.SetSignals (KSignalDTR, 0);
    commPort.SetSignals (KSignalRTS, 0);
    TInt curlenth = commPort.ReceiveBufferLength ();
    commPort.SetReceiveBufferLength (4096);
    curlenth = commPort.ReceiveBufferLength ();

    // now we can start using the port

    TKeyCode key;
    TPtrC8 outputByte ((TUint8 *) & key, 1);
    TBuf8 < KBufSize > localInputBuffer;
    TRequestStatus readStat, keyStat;

    // a null read or write powers up the port

    console->Printf (KMessage11);
    commPort.Read (readStat, localInputBuffer, 0);
    User::WaitForRequest(readStat);
    r = readStat.Int ();
    User::LeaveIfError (r);

    // now the main glass terminal
    // this could be either an active object
    // or, as in this case, an asynchronous loop

    // note that we use Read() with a timeout - we have configured the port so that
    // line feeds trigger early completion of reads, which optimizes text based reception.

    // if we'd used the request commPort.ReadOneOrMore (readStat, localInputBuffer) we
    // could well have ended up calling the server once per character (up to 2000 times
    // per second!) so a regular re-issuing of the read request once in every 100 second is no
    // big deal (to retain echoing of keyboard characters)


    console->Read (keyStat);
    commPort.Read (readStat, KOneHundredSecond, localInputBuffer);
    for (;;)
        {
        User::WaitForRequest (readStat, keyStat);

        // From keyboard

        if (keyStat != KRequestPending)
            {
            key = console->KeyCode ();

            if (key == 0x1b)      // ESCAPE - Disconnect
                {
                console->Printf (KMessage12);
                commPort.ReadCancel ();      // Cancel Read
                User::WaitForRequest (readStat);
                break;
                }

            if (key < 256)            // ASCII - Write to serial port
                {
                TRequestStatus stat;
                commPort.Write (stat, outputByte);
                User::WaitForRequest (stat);
                r = stat.Int ();
                if (r != KErrNone)  // Write has failed for some reason
                    console->Printf (KMessage13, r);
                }

            console->Read (keyStat);         // When complete, read again
            }

        // From serial port - we display printable characters, line feeds and carriage returns
        // but control characters are displayed as a caret ^ followed by the printable equivalent

        // timeout errors are OK here, but we do need to check that there really is data in the
        // buffer before printing it to the screen as we might have timed out with no data

        else if (readStat != KRequestPending)
            {
            if (readStat == KErrNone || readStat == KErrTimedOut)
                {
                // check descriptor and print any characters
                TInt numberRead = localInputBuffer.Length ();
                if (numberRead != 0) 
                    printReceivedText(localInputBuffer,numberRead);
                else
                // else check the input buffer and print any characters
                    {
                    numberRead = commPort.QueryReceiveBuffer();
                    if (numberRead != 0)
                        {
                        commPort.ReadOneOrMore(readStat, localInputBuffer);
                        User::WaitForRequest (readStat);
                        if (readStat == KErrNone) printReceivedText(localInputBuffer,numberRead);
                        }
                    }
                }
            else     // An error occured on reading
                console->Printf (KMessage16, readStat.Int ());
            commPort.Read (readStat, KOneHundredSecond, localInputBuffer);
            }

        // help !! a request we can't cater for

        else
            {
            User::Panic (KPanic, 0);
            }

        }

    // Close port

    commPort.Close ();
    console->Printf (KMessage17, &portName);
    console->Printf (KMessage18);
    server.Close ();
    }

void printReceivedText(TDes8& localInputBuffer,TInt numberRead)
    {
    TUint8 *nextByte = &localInputBuffer[0];
    for (int i = 0; i < numberRead; i++, nextByte++)
        {
        if ((*nextByte < 32) && (*nextByte != 10) && (*nextByte != 13))
            console->Printf (KMessage14, (*nextByte) + 64);
        else
            console->Printf (KMessage15, *nextByte);
        }
    }

// BLD.INF
// Component description file
//
// Copyright (c) 2000 Symbian Ltd.  All rights reserved.

PRJ_MMPFILES

GlassTerm.mmp

// GlassTerm.MMP
//
// Copyright (c) 2000-2005 Symbian Software Ltd.  All rights reserved.

// using relative paths for sourcepath and user includes

TARGET        GlassTerm.exe
TARGETTYPE    exe
UID           0
VENDORID 0x70000001
CAPABILITY   All -TCB

SOURCEPATH    .
SOURCE        GlassTerm.cpp

USERINCLUDE   .
USERINCLUDE   ..\CommonFiles
SYSTEMINCLUDE \Epoc32\include

LIBRARY       euser.lib efsrv.lib c32.lib

// CommonFiles.h
//
// Copyright (c) 2000 Symbian Ltd.  All rights reserved.


#ifndef __CommonFiles_H
#define __CommonFiles_H

#include <e32cons.h>

// public
LOCAL_D CConsoleBase* console; // write all your messages to this
LOCAL_C void doExampleL(); // code this function for the real example

// private
LOCAL_C void callExampleL(); // initialize with cleanup stack, then do example

GLDEF_C TInt E32Main() // main function called by E32
    {
    _LIT(KSymbianEx,"SymbianEx");
    CTrapCleanup* cleanup=CTrapCleanup::New(); // get clean-up stack
    TRAPD(error,callExampleL()); // more initialization, then do example
    __ASSERT_ALWAYS(!error,User::Panic(KSymbianEx,error));
    delete cleanup; // destroy clean-up stack
    return 0; // and return
    }

LOCAL_C void callExampleL() // initialize and call example code under cleanup stack
    {
    _LIT(KStatus1,"Symbian OS Example Code (comms)");
    _LIT(KStatus2,"failed: leave code=%d");
    _LIT(KStatus3,"ok");
    _LIT(KStatus4," [press any key]");

    console=Console::NewL(KStatus1,TSize(KConsFullScreen,KConsFullScreen));
    //console=Console::NewL(KStatus1, TSize(KDefaultConsWidth,KDefaultConsHeight));
    CleanupStack::PushL(console);
    TRAPD(error,doExampleL()); // perform example function
    if (error) console->Printf(KStatus2, error);
    else console->Printf(KStatus3);
    console->Printf(KStatus4);
    console->Getch(); // get and ignore character
    CleanupStack::Pop(); // close console
    }

#endif

[Top]


Description

GlassTerm is a terminal application with configurable handshaking that illustrates the use of the Serial Communications API.

[Top]


Build

The source code for this example application can be found in the directory:

examples\SerialComms\ServerClientSide\GlassTerm

It may be in the directory in which you installed Symbian OS, or it may be in src\common\developerlibrary\. It includes the two project files needed for building: bld.inf and the .mmp file.

The Symbian OS build process describes how to build this application, which results in an executable called \epoc32\release\<target>\<urel or udeb>\GLASSTERM.EXE.

[Top]


Usage

  1. Run the executable GLASSTERM.EXE.

    Executables for the emulator targets wins and winscw can be run on your PC. Executables for ARM targets must be copied to your target platform before being run.

  2. Once running, the glass teletype application performs two simple functions:

    • Read any key presses and send the characters to the serial port.

    • Receive any incoming characters from the serial port and display them on screen.

    It sends and receives at 19200 baud, 8 data bits, no parity, 1 stop bit.

In order to see the application working, you must make a suitable serial port connection. A simple way is to connect a PC and a Symbian OS phone, and run the application on both machines simultaneously. (Before doing this, make sure to close down any other applications using the serial port on the PC, and set the phone's Link To Desktop option to Off). Characters typed on one machine are then echoed to the application on the other.

[Top]


Classes used