infrared

URL:

    http://wagner.pp.ru/~akovalenko/irdasock-latest.tar.gz
    http://wagner.pp.ru/~akovalenko/irdasock-win32-stubs.zip

This extension enables Tcl to connect to IrDA (infrared) services on Win2K and Linux (with limitations for Win98).

Introduction

Communication with devices through an infrared link is usually performed with sockets. The special address family for infrared links (AF_IRDA) is supported on Linux and Windows.

Irdasock extension opens such sockets as Tcl channels. Besides that, it can list all connected devices with their numeric IDs, names and hints.

    % package require irdasock 1.0
    % irda::discover
    {1176300784 R520 {PnP Modem Comm OBEX}}
    % 

You can see here an example of [irda::discover] result, which gave us the device id 1176300784, with name "R520" (it is my mobile phone) and hints {Pnp Modem Comm OBEX} describing which services it claims to support.

When the discovery is done and the target device id is known you can connect to the specific service:

    % set id [lindex [irda::discover] 0 0]
    1176300784
    % set fh [irda::connect $id IrDA:IrCOMM]
    sock212
    % puts $fh ATZ
    % flush $fh
    % gets $fh
    ATZ
    % gets $fh
    % gets $fh
    OK

Now you can do everything you need with the socket $fh. In case of a mobile phone, you can use my gsm library to read and send SMS...

The service which you connect to must use TinyTP protocol. OBEX and IrCOMM both work this way.

Commands

    irda::discover

List connected devices.

The command returns a list of the following structure:

  {Dev1-Hw-Addr Dev1-Name {Hint1 Hint2 ... } }
  {Dev2-Hw-Addr Dev2-Name {Hint1 Hint2 ... } }

Each device has a hardware address that you can pass to [irda::connect]. This address is not permanent, however: if a device will leave the scope of IR receiver for a significant time interval (30 seconds or so), it will be given a new address when it returns.

The device name is more permanent. It's an ideal choice to show up in a message like "Your $gadget is now connected!"

Hints describe a nature of the device and what services it can be expected to provide. All possible hints are the following:

    PnP, PDA, Computer, Printer, Modem, Fax, LAN,
    Telephony, Server, Comm, Message, HTTP, OBEX.

As an example, my Ericsson R520 cell phone gives {PnP Modem Comm OBEX} in the list of hints.

    irda::connect ?-mode tinytp|ircomm? device-addr service-name

Connect to a device and return a channel handle.

Here, device-addr is one of the addresses returned by [irda::discover]. Service-name is something like IrDA:IrCOMM or IrDA:OBEX.

The -mode option sets the special handling of the IrCOMM control data. As these data are transmitted in regular TinyTP frames, you will receive them in a random places of a byte stream unless some countermeasures are taken.

There's no need to specify -mode option in common cases, as it defaults to "ircomm" when the service name is IrDA:IrCOMM and to "tinytp" otherwise. The option must be specified only if you're connecting to some serial port emulating service with the name different from IrDA:IrCOMM (it's rare), or to some non serial port emulating service which is named IrDA:IrCOMM (it's almost impossible).

    irda::server ?-mode tinytp|ircomm? service-name script

Create a server socket and bind it to the specified service name. When an incoming connection is detected on this socket the specified script is run. The script is invoked with two additional arguments: first is the name of a newly-created channel to communicate with the client, second is the device identifier, as returned by irda::discover.

The -mode option works as in irda::connect, but it's almost unusable for now: IrCOMM services must be advertised in the IAS database to be recognized as such. Access to the IAS database will be implemented soon.

Implementation details

As Tcl supports TCP/IP, it contains a lot of code to manage sockets, and only a small subset of this code is TCP/IP-specific. So I decided to write an extension for Tcl which creates AF_IRDA sockets and then simply passes them to Tcl channel infrastructure.

The first drawback of this solution was that fconfigure was confused when trying to getpeername() on my sockets. So I decided to use stacked channels. The next version stacks my channel over Tcl's socket and overrides the functions that handle channel-specific fconfigure options.

The IrCOMM service was (and still is) another headache. As IrCOMM control data are received in regular TinyTP packets, we need to know packet boundaries to strip these data off. There is a fine setsockopt() on Windows'2000 that lets the irda stack do the job. There is no such option on Linux - but it's easy to know the next packet size without upsetting the Tcl socket infrastructure. So I use the platform-dependent solution to achieve the cross-platform result. The stacked channel procedures handles IrCOMM control data on Linux, but not on Windows'2000, where setsockopt() is enough.

Porting the Linux solution to Windows would be useful too, as Windows'98 cannot do the necessary setsockopt().

-- Anton Kovalenko


DG -- Excellent work Anton! I have borrowed a little bit from your code and was wondering what the proper way is to display the irdaDeviceId member of the SOCKADDR struct? I'm using MAC address style, but is there a prefered manner?

    SOCKADDR_IRDA *irdaaddr;
    char formatedId[12];
    ....
    /* Device ID. */
    sprintf(formatedId, "%02x-%02x-%02x-%02x",
            irdaaddr->irdaDeviceID[0], irdaaddr->irdaDeviceID[1],
            irdaaddr->irdaDeviceID[2], irdaaddr->irdaDeviceID[3]);

Results look as:

    % load iocpsock30g
    (Debug) 2 % ::irda::discovery
    {83-18-00-00 THINKPAD-600E {Computer Telephony Comm OBEX}}
    (Debug) 3 %

Mike Collins -- Any possibility of adding Consumer IR to the IrDA library ?

    In doing some research, I found that (ONE) of the differences
 in using the IR port is that it tends to be a connectionless setup
 i.e. don't have a destination address, or its defined as Rdm (I'm assuming random)
 and then you also have to do some delays to achieve the
 40kHz timing for Consumer IR, and apparently 38.7kHz is close enough.