This section of the document describes how an application interfaces to the µH Router using the Unix FIFO interface. If you are only interested in using the router, please go directly to the Set Up page.
FIFO Interface
The µH Router FIFO interface uses pairs of single direction Unix FIFOs (also known as Named Pipes) to communicate.
When using the router, an application typically goes through the following steps:
- launch the router
- obtain a connection to a keyer (e.g., a microKEYER)
- obtain connections to the keyer's functions (e.g., a microKEYER's PTT or CAT port)
The application then uses these ports to communicate with each of the keyer's function.
When the client has finished using the router, it can terminate the router, or just leave µH Router running.
When the application has finished using the router, it
Launching the µH Router
There are multiple ways to launch µH Router. With Cocoa, you can send a -launchApplication: message to the AppKit's NSWorkspace class. With Carbon, you can launch an application by calling LSOpenApplication in Launch Services. Finally, you can use AppleScript to launch the router from an AppleScript Studio program or a Carbon or Cocoa application.
Master FIFO Ports on the µH Router
The µH Router opens two statically named ports when it is launched. These two ports are called "/tmp/microHamRouterWrite" and "/tmp/microHamRouterRead". The former is used by an application to send commands and requests to the router and the latter is used by the application to read responses from the router. These two are the only static ports that an application uses to talk with the router, and they are the only ports that are shared among applications.
All the other ports that are used by an application are dynamically created and are unique to each application.
Under both Cocoa and Carbon, the open() call is used to get file descriptors to these two ports. I.e.,
routerRd = open( "/tmp/microHamRouterRead", O_RDONLY ) ;
routerWr = open( "/tmp/microHamRouterWrite", O_WRONLY ) ;
The following are command bytes that can be sent to the Master port (these can be found in the header file RouterCommands.h):
#define ROUTERFUNCTION 0x80
#define OPENMICROKEYER ( ROUTERFUNCTION + 0x01 ) // get a port to the microKEYER router
#define OPENCWKEYER ( ROUTERFUNCTION + 0x02 ) // get a port to the CW KEYER router
#define OPENDIGIKEYER ( ROUTERFUNCTION + 0x03 ) // get a port to the DIGI KEYER router
#define OPENKEYER ( ROUTERFUNCTION + 0x08 ) // get a port to keyer from the following KeyerID (char*)
#define KEYERID ( ROUTERFUNCTION + 0x09 ) // get n-th keyer's KeyerID (null terminated string)
#define QUITIFNOKEYER ( ROUTERFUNCTION + 0x1f ) // quit if there are no keyers
#define QUITIFNOTINUSE ( ROUTERFUNCTION + 0x1e ) // quit if not connected
#define QUITALWAYS ( ROUTERFUNCTION + 0x1d ) // quit
Obtaining Ports to a Keyer
To open a pair of ports for communicating with a keyer, you send the OPENMICROKEYER or the OPENDIGIKEYER or the OPENCWKEYER command to the master port. The µH Router will send back a name that you can use to open as ports to the specific keyer.
The following is an example of how to get a connection (the write and read file descriptors) to a microKEYER.
char path[26], baseName[20], request[1] = { OPENMICROKEYER } ;
write( routerWrite, request, 1 ) ;
if ( read( routerRd, baseName, 20 ) > 0 ) {
strcpy( path, baseName ) ;
strcat( path, "Write" ) ;
microKeyerWrite = open( path, O_WRONLY ) ;
strcpy( path, baseName ) ;
strcat( path, "Read" ) ;
microKeyerRead = open( path, O_RDONLY ) ;
}
else {
// router did not find a microKEYER
}
If the µH Router could not find a microKEYER, it returns a string of length zero. Otherwise it returns a string that contains the base path name of a named pipe. When the string "Write" is concatenated to this base name, it forms the path of the named pipe which the application uses to send a command to the microKEYER component of the Router. When the string "Read" is concatenated to this base name, it forms the path of the named pipe which the application uses to receive replies from the microKEYER component of the Router.
Connections to the DIGI KEYER and CW KEYER are similarly opened.
Opening Multiple Keyers
The OPENMICROKEYER, OPENDIGIKEYER and OPENCWKEYER commands access the first microKeyer, digiKeyer or CW Keyer that is found by the router. When there are multiple digiKeyers, etc, you will need to use a different mechanism to access all the keyers.
By calling KEYERID with an 8-bit integer argument, the router will return a null terminated "keyerID" string for the n-th keyer that the router finds. The "keyerID" is typically a unique string of the form "D2TBJGWG" or "DKOUKJ9L," where the first two characters identify the type of keyer (DK=digiKeyer, D2=digiKeyer II, CK=CW keyer, MK=microKeyer and M2=microKeyer II) and the following 6 characters are unique strings that are assigned to the keyer by microHAM.
The argument n is a 0-based integer (one byte) and when n reaches the number of keyers that are found, a zero length string will be returned.
When a KEYERID is returned, the null terminated string can be sent back to the router with the KEYERID command to get the port numbers similar to OPENMICROKEYER, OPENDIGIKEYER and OPENCWKEYER commands.
The following gets the second keyer,
char keyerID[16], baseName[20], request[2] = { KEYERID, 1 }, openKeyer[1] = { OPENKEYER } ;
write( routerWrite, request, 2 ) ;
read( routerRd, keyerID, 16 ) ;
write( routerWrite, openKeyer, 1 ) ;
write( routerWrite, keyerID, strlen( keyerID )+1 ) ;
read( routerRd, baseName, 20 ) ; ...
Obtaining Ports to a Keyer Function
As shown above, a Keyer Port is obtained by sending an OPEN request to the Master Port. In a similar manner, a port for a specific keyer function is obtained by sending an OPEN request to the Keyer Port.
Each microHAM keyer has multiple functions, and each of these functions can be accessed through its own unique port (the following definitions can be found in the header file RouterCommands.h).
#define OPENRADIO ( KEYERFUNCTION + 0x02 ) // get a RADIO port
#define OPENCONTROL ( KEYERFUNCTION + 0x03 ) // get a CONTROL port
#define OPENPTT ( KEYERFUNCTION + 0x04 ) // get a port to the PTT flag bit
#define OPENCW ( KEYERFUNCTION + 0x05 ) // get a port to the serial CW flag bit
#define OPENRTS ( KEYERFUNCTION + 0x06 ) // get a port to the RTS flag bit
#define OPENFSK ( KEYERFUNCTION + 0x07 ) // get an FSK port
#define OPENWINKEY ( KEYERFUNCTION + 0x08 ) // get the WinKey port
#define OPENFLAGS ( KEYERFUNCTION + 0x09 ) // get the FLAGS port
For example, assuming that the microKeyerWrite and microKeyerRead file descriptors are already created as shown in the above code example, the file descriptors for the PTT read and write ports for the microKEYER can be obtained by:
char path[26], baseName[20], request[1] = { OPENPTT } ;
write( microKeyerWrite, request, 1 ) ;
if ( read( microKeyerRead, baseName, 20 ) > 0 ) {
strcpy( path, baseName ) ;
strcat( path, "Write" ) ;
microKeyerPTTWrite = open( path, O_WRONLY ) ;
strcpy( path, baseName ) ;
strcat( path, "Read" ) ;
microKeyerPTTRead = open( path, O_RDONLY ) ;
}
As before, you append the string "Write" or "Read" to the returned base name to get the two pathnames to use to send commands and receive status.
If the keyer does not have a certain function (specifically, a DIGI KEYER does not have a WinKey port, and a CW KEYER does not have an FSK port) the OPEN request will return a zero length base name.
RADIO Port
Once a pair of ports are opened to the radio channel with the OPENRADIO request, that pair of ports will behave exactly as if you have opened a port to a serial device that is connected to the radio's CAT port. You send byte stream data to the radio using the Write port and receive data back from the radio using the Read port.
Please note that you do not use ioctl to set the serial port parameters (bits, parity, baud rate) of the radio port. Instead, you set the radio's serial parameter by using SET RADIO CHANNEL command the Control Port. Refer to the microHAM Keyer Protocol document for the details.
CONTROL Port
The Control port of a microHAM keyer is used to set parameters in the keyer. This includes setting the serial port parameters (number of data and stop bits, parity and baud rate) of the RADIO and FSK channels, settings various parameters such as FSK polarity, setting up the WinKey memory, obtaining version number of the keyer, setting up the bootloader for downloading new firmware, etc.
In addition to the 8 bit data bytes in a CONTROL stream, the microHAM keyers require that the first and the last byte of a control command string be specially tagged. The way the µH Router handles this is to require that you send each complete command string using a single write() call. The router will then tag the first and the last byte of that string properly. These tagged bytes are the ones that are shown in bold in the microHAM Keyer Protocol document.
The first and last byte of a CONTROL channel reply from the keyer that comes back from the keyer are similarly tagged. Since the data bytes themselves already occupy the full 8 bits of a byte, the way the µH Router handles it is to always send a 16-bit word to the application for each byte that it receives. Each data byte that is sent to the application through the CONTROL Port is encoded into two bytes. The first byte of the pair contains the tag information and the second byte contains the actual data. The tag is zero for the first and last byte of a CONTROL reply sequence and the interior bytes each come with a tag that has the value 1.
Please note that you may get CONTROL status/replies from the keyer from commands that your application did not initiate. You have to be prepared to reject unneeded replies from the keyer.
The µH Router periodically sends a "heartbeat" to the keyer so that a PTT assertion is not timed out by the onboard watchdog timer in the keyer. The µH Router uses the "ARE YOU THERE" control sequence as the heartbeat signal, and the keyer will echo these commands back when it receives them. In addition, other applications might also be using the control port to set parameters on or to obtain status from the keyer.
The tag bytes help identify the start and end of a command string, so you will know when a CONTROL string begins and when it ends - the MSB of the actual data byte of the first byte in a CONTROL string is always off and the MSB of the last data byte in a CONTROL string is always on.
PTT Port
If you send a non-zero byte to this port, the keyer will engage the PTT of the attached receiver (as long as the keyer is set up to connect this command to the physical PTT line - see STORE SETTINGS command in the microHAM Keyer Protocol document).
Whenever the PTT state changes (the PTT changes are reported back as a bit within the FLAGS channel of the keyer), the µH Router will send the state (0 or non-zero) of the PTT back to the application (on the application's PTT read port).
Again, please make sure that you ignore any PTT state changes that you did not initiate since they can be caused by other applications or even other physical sources (e.g., from a foot switch).
CW Port
Like the PTT port, this port asserts or deasserts a "serial CW" line. If the keyer is set up to obey the signal, writing a non-zero character to this port will assert the keying line from the keyer to the radio.
RTS Port
This is yet another port that controls a flag bit on the Keyer and appears on the RTS pin of the serial port from the keyer to the radio.
WinKey Port
This is the port that is connected to the WinKey chip on a microKEYER and a CW KEYER (note: the DIGI KEYER lacks this chip).
The data that you send to this port is documented in K1EL's WinKey document. You can command the WinKey chip to send Morse characters, change dash/dot weighting, CW speed, join two characters into a Prosign, etc.
FSK Port
This port allows you to send Baudot data to an FSK buffer in the keyer, to be sent out as an FSK keying line. The baud rate and other parameters are set up by sending the SET FSK CHANNEL command to the CONTROL port. Characters sent to the FSK Port will key the FSK line of the radio.
(NOTE: the FSK ports in the microHAM devices do not work at the nominal Amateur RTTY data rate of 45.45 baud -- you will have to configure it to use 45.0 baud. While this is not ideal with demodulators that are tuned for 22 msec bit periods, there should be no noticeable difference in performance except when the SNR is very poor.)
Write-only Ports
(Added in v1.30)
The keyer ports (PTT, WinKey, FSK, etc) can be opened in Write-only mode if you are not interested in reading back any responses from the keyer. To open a port as write-only, merge the WRITEONLY flag to the open request (e.g., OPENPTT|WRITEONLY). The WRITEONLY flag is defined as:
#define WRITEONLY 0x80
Closing Connection to a Keyer
When the application is done with using a Keyer, it is highly recommended that it sends a command to close all connections to the keyer. This will allow you (and other apps) to use the quitIfNotInUse command that is described in the following section.
char request[1] = { CLOSEKEYER } ;
write( microKeyerWrite, request, 1 ) ;
Closing the Keyer will also cause the Router to close all of its function ports that is associated with the application's keyer port; you do not need to individually close the PTT or CAT ports. But make sure that you no longer write to those ports, since that could result in a "broken pipe" error.
Terminating the µH Router
There are three different ways that you can use to terminate the µH Router and they can be invoked either from the named pipe interface or from AppleScript.
The first way is the unconditional quit message that you can send to the router's master port. It is not advisable to use this command if the router can potentially be shared and be in use by another application. When the µH Router receives this command, it waits one second and then quits. E.g.,
char request[1] = { QUITALWAYS } ;
write( routerWrite, request, 1 ) ;
The µH Router also supports a quitIfNoKeyer command. When the µH Router wakes up, it looks to find any serial port that is associated with a microHAM device. It it does not find any keyer and if it receives a subsequent quitIfNoKeyer command, the µH Router will terminate itself. E.g.,
char request[1] = { QUITIFNOKEYER } ;
write( routerWrite, request, 1 ) ;
Instead of launching the µH Router and then asking it to quit, the application can also check to see if any microHAM devices exist before launching the router. microHAM devices have serial ports with names that start with "usbserial-MK"(microKEYER), "usbserial-DK"(DIGI KEYER) or "usbserial-CK"(CW KEYER).
Another conditional termination script is the quitIfNotInUse command. This command will cause the µH Router to terminate if there are no connections made to it from an application. You can safely send the quitIfNotInUse command to the µH Router just before your application terminates. This will remove the router from the dock if no other application is using the router. E.g.,
char request[1] = { QUITIFNOTINUSE } ;
write( routerWrite, request, 1 ) ;