Resurrecting a PK-232 TNC for Winlink email

Over the past several weeks, I’ve had a project to resurrect an old PK-232MBX AX.25 packet radio Terminal Node Controller (TNC, or basically what we’d today call a MODEM) so I can send email over radio.

The ultimate objective is to use WinLink Express with our local Raynet group. In particular, we’re involved in a number of marathons, cycle rides and other events where the main UHF voice net gets very busy with people passing routine messages about which participants have reached their location. If some of this mundane, routine traffic could be moved onto an email network, the voice net would then be clearer for any less routine message exchange, and also should decrease the probability of mistakes caused by mis-hearing numbers. It should also help the Control station with information management. WinLink provides a mechanism for email without any internet requirement. This is useful because, whilst we could set up wide area WiFi, long-distance WiFi tends to be based on point-to-point links rather than an omnidirectional service to cover an area.Thus, we can have email access over several miles of course, independent of any cellular coverage. While WinLink’s protocols can ‘piggyback’ on many different modes with a node-based infrastructure, we’d at least initially just use AX.25 Peer-to-Peer connections to carry the data.

Which is where the AEA/Timewave PK-232 comes in. This was manufactured through the second-half of the 1980s into the 1990s and in its day was the TNC to have. It supported many modes, including AX.25, and cost a fairly substantial chunk of money too. These days, now that soundcards can often perform the role of a TNC, and with the AX.25 packet radio network largely supplanted by broadband internet services, PK-232s are widely obtainable for really not very much. I picked mine up a few years ago at a continental radio rally for just €10, I believe. It has been sitting in my garage ever since awaiting a project.

Image of the TNC with LEDs lit

My now-working PK-232

Getting mine working proved to be a slightly complex exercise, having to piece together bits of information scattered around the internet and a lot of head-scratching, so this slightly long, technical, post explains what I did…

Tip: Skip to the Conclusion to see the main findings, but I’d strongly encourage anyone having similar problems to follow through the thinking, which is why I’ve typed it up.

The fuse

The unit was dead on initial power-on, which I soon traced to the glass 1A fuse on the circuit board. This was nice and easy to sort, thanks to a quick order from RS Components (part number 783-3343).

Basic TNC-to-computer functionality works

Once the fuse was replaced, I connected the TNC to my computer (the TNC has an old-school DB25 25-pin serial port, so you’ll probably need an adaptor), it powered on OK. I fired up PuTTy and set the basic default serial port connection parameters, typed ‘*’ and soon saw a command prompt.

The ‘default serial port connection parameters’, which are entirely separate from the RF output speeds, used by the PK-232 are unusual by today’s standards: 1200 baud, 7 bit word length, 1 stop bit, even parity. PuTTy will default to 9600 baud, 8 bit words and no parity, so you need to be careful to set the right thing or you may not see anything on your screen, or you may see gibberish.

The prompt I received showed that the firmware in the TNC dated from October 1989, about halfway through the PK-232’s production run, and certainly not the latest. Never mind, though, we have a start.

Wiring the TNC to my IC-706Mk2G

The next step was to get a connection from the TNC to my IC-706Mk2G radio. It felt as though the 6-pin mini-DIN ‘data port’ on the 706’s rear panel was the right way to do this, which meant creating an adapter lead from mini-DIN to the 5-pin molex-like plug used by the PK-232. Note that the 706’s mini-DIN annoyingly has different pinouts for 1200 baud and 9600 baud packet, which means we’ll be hardwiring to a certain speed by doing this.

I built the lead, tuned the radio to 144.800 MHz (the European APRS frequency) so there would be some APRS traffic to decode and… nothing. Absolutely nothing was received by the TNC, judging by the lights on the front. Was it my soldering? Another cable fault? I couldn’t see any obvious errors. At this point I became suspicious as all the other writeups I could find online showed people using the 13-pin full-size DIN accessory output from the 706, rather than the 6-pin mini-DIN data output.

I didn’t have any 13-pin DINs to hand to try, though, so instead I decided to go to the RJ-45 microphone connector on the front panel of the 706. It would be easy enough to wire that to the PK-232, and I didn’t even have to mess around with crimping an RJ-45 as I just needed to chop an old Cat 5 ethernet patch cable in half. The pinout for this is as follows:

RJ-45 pin    Ethernet cable colour    Role         PK-232 'Radio 1' pin
-            -                        RX audio     1    [Not connected]
2            Green                    TX audio     2
-            -                        Squelch      3    [Not connected]
7            White/Brown              Ground       4
4            Blue                     PTT          5

You’ll see that I left the RX audio line disconnected. I can’t find the source for this now, but one source on the internet noted that the IC-706’s output level from the microphone connector isn’t high enough to give reliable decodes on TNCs. That would make sense: this connection will only be designed for a small speaker/mic, so instead we use a 3.5mm patch cable to connect the 706’s rear-panel external speaker socket to the PK-232’s separate RX-IN audio input. The PK-232 doesn’t actually mind where the audio comes in, as long as the levels are OK. Having the 706’s volume knob at the 9 o’clock position seems adequate.

We don’t need to connect the squelch line as it’s optional and really only for channels that have shared voice and data traffic, which won’t be our use case.

Getting it to work with APRSIS32

Once the leads were made up, I could tune to 144.800 and APRS packets were decoded at the prompt in PuTTy. (The APRS system is built on top of AX.25, which is why this works.) We have progress! Now to find out whether we can transmit APRS (since I know there’s traffic there) before we try Winlink…

At this point I went down a long rabbit-hole trying to figure out the correct way to make the PK-232 work with APRSIS32. That isn’t the objective of this post, so I won’t dwell on it, but basically APRSIS32 doesn’t natively support the PK-232, so we need to put the PK-232 into KISS mode and then treat it as a KISS modem in APRSIS32.

Before starting APRSIS32, issue the following commands at the PK-232’s prompt in PuTTy:

MYCALL <your callsign here>
AWLEN 8
PARITY 0
RESTART

This will allow you to connect to the PK-232 using the more normal 8 bit word length and no parity for the serial connection. You do not need to set expert mode on, nor set 8bitconv on, nor set rawHDLC on, all of which caused me to waste a load of time.

By the way, there is a difference between RESTART (which just warm-restarts the box) and RESET (which should do a factory reset). I say should because I found some cases where RESET appeared to reset the box but didn’t actually reset everything, leaving me in a confused state about its configuration. The only way to guarantee a reset of the PK-232 is to remove jumper J1 behind the battery on the circuit board for a minute or so, to ensure the memory is cleared. This again caused wasted time. (In fact, you’ll be removing this jumper so often that I’d recommend leaving the screws off the case until you know the PK-232 is working.)

Having restarted (not reset) the PK-232, we can connect to the PK-232 from APRSIS32. You can create a new KISS port in the user interface, or alternatively you can edit the APRSIS32 settings XML file to add the port in a more controlled way. The port configuration you need is:

<RFPort Name="PK232KISS">
  <Protocol>KISS</Protocol>
  <Device>COM8:1200,N,8,1</Device>
  <RfBaud>1200</RfBaud>
  <OpenCmd>^M~</OpenCmd>
  <OpenCmd>^M~</OpenCmd>
  <OpenCmd>XFLOW OFF</OpenCmd>
  <OpenCmd>FULLDUP OFF</OpenCmd>
  <OpenCmd>CONMODE TRANS</OpenCmd>
  <OpenCmd>KISS ON</OpenCmd>
  <CloseCmd>^192^255^192~!!0</CloseCmd>
  <CloseCmd>^C^C^C~!!0</CloseCmd>
  <CloseCmd>TC 1!TS 1</CloseCmd>
  <CloseCmd>TN 2,0!TN 2,0</CloseCmd>
  <QuietTime>0</QuietTime>
  <Enabled>1</Enabled>
  <XmitEnabled>1</XmitEnabled>
  <ProvidesNMEA>0</ProvidesNMEA>
  <RFtoISEnabled>1</RFtoISEnabled>
  <IStoRFEnabled>0</IStoRFEnabled>
  <MyCallNot3rd>0</MyCallNot3rd>
  <BeaconingEnabled>1</BeaconingEnabled>
  <BeaconPath></BeaconPath>
  <BulletinObjectEnabled>0</BulletinObjectEnabled>
  <DXEnabled>0</DXEnabled>
  <DXPath></DXPath>
  <MessagesEnabled>0</MessagesEnabled>
  <MessagePath></MessagePath>
  <TelemetryEnabled>0</TelemetryEnabled>
  <TelemetryPath></TelemetryPath>
  <!--DigiXform-->
</RFPort>

I could now transmit and receive in APRSIS32. I noted, however, that the APRSIS32 log file included the following line:

Port(PK232KISS):2018-04-12T06:49:58.155 Missed Expected Response(cmd:) From Command(KISS ON)

It still worked, but that line would come back to haunt me when I tried to use the TNC from Winlink Express.

Firmware upgrade time

By this point, I knew the PK-232 to work with APRSIS32, both transmit and receive, but I couldn’t get it to work with Winlink Express 1.15.11.

Unlike APRSIS32, Winlink does natively have the option to support the PK-232, so it should just work without messing around with KISS mode. But no matter what settings I tried, I always got the same message from the Packet P2P window: Winlink would correctly initialise the PK-232, but would then say ‘Unable to connect to port COM8’. I knew no other program was holding the port open (otherwise it wouldn’t have initialised) so something else must be wrong. (Incidentally, it could connect to the COM port correctly in Packet Node mode, but that isn’t useful for me as I don’t have any local nodes).

After a bit of analysis, including monitoring the serial port traffic, I realised that the ‘native’ support for the PK-232 actually does put the PK-232 into KISS mode and, moreover, it was hanging immediately after issuing the KISS ON command. That looked familiar. It was waiting for the missing ‘cmd:’ prompt that APRSIS32 had complained about.

But I didn’t think KISS ON should return a prompt. After all, the whole point of KISS is to give a controlling computer control of the TNC, and so a user-facing prompt wouldn’t be necessary. I was mystified and yet it seemed unlikely that both APRSIS32 and Winlink would have a bug around this (even if APRSIS32 handled it more gracefully than Winlink Express). I decided that the next course of action had to be a firmware upgrade for the PK-232, to see whether ‘KISS ON’ gave a different response in later versions.

Of course today a firmware upgrade normally means a free download off a website, put a USB cable into some equipment, and press some magic button to upload the new firmware to an EEPROM (where the first two Es stand for Electronically Erasable). Not so in the 1980s. The PK-232’s firmware was stored on one (or two, depending on the version) one-time programmable EPROM (with no erasing, other than flooding it with UV light). An upgrade meant buying a new chip and installing it onto the circuit board.

So, being very careful with my phrasing here, since I couldn’t possibly condone or encourage copyright infringement, I got hold of a couple 27C512 ICs and obtained the final v7.2 firmware. Be aware that you can only use the latest firmware on a PK-232 with a serial number over about 45,000, or an earlier model with the mailbox daughter board (ie. the PK-232MBX upgrade). Mine has a serial number of 23,400-ish but with the daughter board installed, so I was OK.

Replacing the chips was straightforward enough, although the pins almost inevitably bend as you pull them out of the board, In all likelihood, you won’t be able to replace the original chip once it’s out. A strong word of caution here, since I got this wrong: the photo linked from the firmware source shows a chip marked ‘LO’ in socket U3 on the board. This is incorrect and, if you follow it, you’ll likely have trouble getting the IC back out again as explained above. You want to put the v7.2 LO chip in socket U1 and v7.2 HI in socket U3.

Pull out jumper J1 (just behind the battery), wait a minute and then replace it to ensure the memory is clear.

Phew, it boots!

KISS mode, control flow, and the question of timing

As I had expected, issuing the KISS ON command in the v7.2 firmware behaves no differently from my earlier version. It still doesn’t produce a command prompt, so the Winlink problem was still there. It couldn’t connect to the COM port in Packet P2P mode.

What I did know from the earlier COM port sniffing, however, was that Winlink was actually trying to put the PK-232 into KISS mode. So what if I put it into KISS mode manually, and just connected to it from Winlink as a straight-forward KISS modem?

Using PuTTy, I issued the commands that APRSIS32 had used: XFLOW OFF / FULLDUP OFF / CONMODE TRANS / KISS ON, then closed PuTTy and started Winlink Express and told it to connect to a KISS modem.

Success at first… I could call a station using Winlink Express and, when the call happened, the radio went into transmit mode and transmitted something. But nothing was ever received. I even tried using a laptop + Kenwood D72 as another Winlink station positioned literally right next to the PK-232. The D72 heard the request from the PK-232 and transmitted a reply, but the PK-232 couldn’t decode the reply, so the handshake never succeeded.

I again turned to sniffing the COM port traffic and noticed that there was never even an attempt by host computer to read from the serial line. This also matched what I could see if I connected to the PK-232 with PuTTy immediately after Winlink: the read buffer would immediately be printed out onscreen as soon as PuTTy connected, so the replies were just being stored on the PK-232 and not being forwarded to the computer.

I started double-checking everything I knew and was eventually drawn to that mysterious command ‘XFLOW OFF’. What exactly did it do? It was the only thing I had no knowledge of at all.

It turns out that old-style serial communications could only be one directional at any given time. To implement a send/receive scenario, therefore, you had control flow logic to decide whether the host computer could send or receive at any moment. This control flow could either be implemented in software by sending control characters as part of the data, or in hardware through the RTS/CTS pins of the serial connector. More about RS-232 flow control here. XFLOW OFF was setting the software flow control off, and so implicitly asserting that hardware flow control was to be used. I started to suspect that this switching between transmit and receive was at the root of the problem. I could transmit OK but if the D72 was starting its reply before the PK-232 had switched over to receive, that would explain why the replies couldn’t be decoded. That seemed entirely plausible with a modern fast computer replying to a connection request from old 1980s hardware. Control flow would also be consistent with the computer not even attempting to read the decoded information. It would also explain why APRS worked: APRS is a single broadcast beacon message, with no handshaking and probably several seconds between your signal and another person’s signal – plenty of time for changeovers to complete.

The breakthrough

My first step was to increase the serial baud rate for the connection between the computer and the PK-232 to 9600 baud, from 1200 but that didn’t help.

I should mention that the PK-232 technical manual says that hardware control flow is generally implemented only on the RTS pin. If you want to use full hardware flow control over the CTS pin as well, you can do that by setting all of XON, XOFF, START and STOP to null, ie. $00. That didn’t help either.

While considering other options, I also played around with trying other settings in Winlink for alternative KISS-type TNCs, but I couldn’t get any of them to reliably work. I also tried the Extended KISS mode available in the later PK-232 firmware (command KISS $03 to enable it), without success.

Somewhat in frustration, I then tried setting Winlink to treat my PK-232 as a PK-96 under native (not KISS) mode. Suddenly everything sprang into life and I could both send and receive.

Conclusion

In summary, therefore:

  • Don’t trust the RESET command on the PK-232. Use the jumper on the circuit board as not everything will always be reset otherwise.
  • Winlink seems to have a bug in its Packet P2P connection mode (but not in Node mode), where it hangs waiting on the ‘cmd:’ prompt after issuing the KISS ON command, which it will never receive by the very design of KISS ON.
    APRSIS32 also has this bug but handles it gracefully, merely logging the error and then connecting anyway.
    In the case of Winlink, connecting to the PK-232 as a PK-96 works around this issue, with no adverse effects that I have found so far.
  • Putting my PK-232 into KISS mode manually and then trying to use Winlink to connect to it as a generic KISS modem doesn’t work. I strongly suspect a control-flow / timing problem but I found the PK-96 workaround above before I completed this investigation.

Wow! After many more hours than should have been necessary, I now have a PK-232 driving my IC-706Mk2G and sending/receiving email over RF, which we can use in the control station at Raynet events, while we use laptop/D72 combinations in the field.

Postscript: The working configuration

And finally, just because I wanted this information so badly while working on it myself, here’s the complete known-working configuration (output from DISPLAY Z) of the PK-232 when used with Winlink, pretending to be a PK-96:

Connect Link state is: DISCONNECTED
UPlink
Unproto CQ
3Rdparty OFF
8Bitconv ON
AAb
ABaud 110
ACKprior OFF
ACRDisp 0
ACRPack OFF
ACRRtty 71
ADelay 4 (40 msec.)
AFilter OFF
ALFDisp ON
ALFPack OFF
ALFRtty ON
ALTModem 0
ARQTmo 60 (60 sec.)
ARQTOL 3
ARXTor OFF
ASPect 2 (576)
ATxrtty 0 (000 msec.)
AUdelay 2 (20 msec.)
AUTOBaud OFF
AWlen 8
Ax25l2v2 ON
AXDelay 0 (00 msec.)
AXHang 0 (000 msec.)
BBSmsgs OFF
Beacon EVERY 0 (00 sec.)
BItinv $00
BKondel ON
BText
CANline $18 (CTRL-X)
CANPac $19 (CTRL-Y)
CASedisp 0 (as is)
CBell OFF
CFrom all
CHCall OFF
CHDouble OFF
CHeck 0 (00 sec.)
CHSwitch $00
CMdtime 10 (1000 msec.)
CMSg OFF
CODe 0 (International)
COMmand $03 (CTRL-C)
CONMode TRANS
CONPerm OFF
CONStamp OFF
CPactime OFF
CRAdd OFF
CText
CUstom $0A15
CWid $06 (CTRL-F)
DAYStamp OFF
DCdconn OFF
DELete OFF
DFrom none
DIDdle ON
DWait 16 (160 msec.)
EAS OFF
Echo OFF
ERrchar $5F (_)
EScape OFF
FAXNeg OFF
Flow OFF
FRack 2 (2 sec.)
FREe 18536
FRIck 0 (00 msec.)
FSpeed 2 (120)
FUlldup OFF
GENDchar $0D (CTRL-M)
GINIText
GLOCtx 0 (00 sec.)
GNMEA1 $GPGLL
GNMEA2
GPOLLcal none
GPSAuto OFF
GPSMode 0
GREMprog OFF
GSYMchar $00
GUNstart OFF
GRaphics 1 (960 dots)
GUsers 0
HBaud 1200
HEAderln ON
HEReis $02 (CTRL-B)
HId OFF
HOMebbs none
ILfpack OFF
KILONFWD ON
LAstmsg 0
LEftrite ON
LIte OFF
MAildrop OFF
MARsdisp OFF
MAXframe 2
MBEll OFF
MBx none
MCon 0 (none)
MDigi OFF
MDMon OFF
MDPrompt Subject:/Enter message, ^Z (CTRL-Z) or /EX to end
MFIlter $80
MFrom none
MId 0 (00 sec.)
MMsg OFF
Monitor 0 (none)
MOPtt ON
MProto OFF
MRpt ON
MSPeed 20
MStamp OFF
MTExt Welcome to my Timewave PK-232M maildrop.
Type H for help.
MTo none
MWeight 10
MXmit OFF
MYAlias none
MYALTcal none
MYcall M0BLF
MYGate none
MYIdent none
MYMail none
MYPTcall none
MYSelcal none
NAVMsg all
NAVStn all
NEwmode OFF
NOmode ON
NUCr OFF
NULf OFF
NULLs 0
PACLen 128
PACTime AFTER 10 (1000 msec.)
PARity 0 (none)
PASs $16 (CTRL-V)
PASSAll OFF
PErsist 160
PPersist ON
PRCon OFF
PRFax ON
PROut OFF
PRType 2
PT200 ON
PTHuff 0
PTOver $1A (CTRL-Z)
PTRound OFF
RBaud 45
RECeive $04 (CTRL-D)
REDispla $12 (CTRL-R)
RELink OFF
RESptime 0 (000 msec.)
REtry 5
RFec ON
RFRame OFF
RXRev OFF
SEndpac $0D (CTRL-M)
SLottime 60 (600 msec.)
SQuelch OFF
SRXall OFF
STArt $11 (CTRL-Q)
STOp $13 (CTRL-S)
TBaud 9600
TDBaud 96
TDChan 0
TIme $14 (CTRL-T)
TMail OFF
TMPrompt GA subj/GA msg, '/EX' to end.
TRACe OFF
TRFlow OFF
TRIes 0
TXdelay 60 (600 msec.)
TXFlow OFF
TXRev OFF
USers 1
USOs OFF
Vhf ON
WHYnot OFF
WIdeshft OFF
WOrdout OFF
WRu OFF
XBaud 0
XFlow OFF
XMITOk ON
XOff $13 (CTRL-S)
XON $11 (CTRL-Q)