Skip to content

Instantly share code, notes, and snippets.

@M0LTE
Last active February 19, 2026 23:11
Show Gist options
  • Select an option

  • Save M0LTE/37c02b17c61b9c3d517e022879c36101 to your computer and use it in GitHub Desktop.

Select an option

Save M0LTE/37c02b17c61b9c3d517e022879c36101 to your computer and use it in GitHub Desktop.
bpq32.cfg for GB7RDG
SIMPLE ; sets a lot of defaults
NODECALL=GB7RDG ; Node callsign
NODEALIAS=THURST ; Node alias (6 characters max)
LOCATOR=IO91lk ; Maidenhead locator
MAPCOMMENT=40m, 6m, 2m, 70cm
PASSWORD=xxxxxxxx
AUTOSAVE=1
EnableEvents=1
EnableM0LTEMap=1
NODESINTERVAL=60
MINQUAL=10
IDINTERVAL=15
IDMSG:
GB7RDG - Reading - 40m, 6m, 2m, 70cm
***
INFOMSG:
See https://ukpacketradio.network/nodes:gb7rdg
***
CTEXT:
Welcome to GB7RDG. Sysop M0LTE.
Type ? for Help
***
BTINTERVAL=5
BTEXT:
GB7RDG node IO91lk67SI - https://packet.oarc.uk
***
; https://www.cantab.net/users/john.wiseman/Documents/BPQCFGFile.html##Port
PORT
PORTNUM=1
ID=VHF
TYPE=ASYNC
PROTOCOL=KISS
KISSOPTIONS=ACKMODE
COMPORT=/dev/serial/by-path/platform-3f980000.usb-usb-0:1.2:1.0
SPEED=57600
TXDELAY=100
FRACK=4000
PACLEN=150
DIGIFLAG=1
QUALITY=191
MINQUAL=100
ENDPORT
PORT
PORTNUM=2
ID=UHF
TYPE=ASYNC
PROTOCOL=KISS
KISSOPTIONS=ACKMODE
COMPORT=/dev/serial/by-path/platform-3f980000.usb-usb-0:1.3:1.0
SPEED=57600
QUALITY=192
MINQUAL=20
DIGIFLAG=1
TXDELAY=30
PACLEN=236
FRACK=1000
MAXFRAME=4
RESPTIME=200
ENDPORT
PORT
PORTNUM=3
ID=7052.75kHz IL2P+CRC QPSK600
TYPE=ASYNC
PROTOCOL=KISS
KISSOPTIONS=ACKMODE
COMPORT=/dev/serial/by-path/platform-3f980000.usb-usb-0:1.5:1.0
SPEED=57600
FRACK=7000
PACLEN=80
NODESPACLEN=60
DIGIFLAG=0
MAXFRAME=1
RETRIES=10
QUALITY=190
MINQUAL=193
TXDELAY=100
TXTAIL=300
RESPTIME=100
T3=300
ENDPORT
PORT
PORTNUM=4
ID=50.650MHz IL2P+CRC BPSK300
TYPE=ASYNC
PROTOCOL=KISS
KISSOPTIONS=ACKMODE
COMPORT=/dev/serial/by-path/platform-3f980000.usb-usb-0:1.4:1.0
SPEED=57600
FRACK=7000
PACLEN=80
NODESPACLEN=60
DIGIFLAG=0
MAXFRAME=1
RETRIES=10
QUALITY=190
MINQUAL=193
TXDELAY=100
TXTAIL=300
RESPTIME=100
T3=300
ENDPORT
PORT
PORTNUM=7
ID=Telnet
DRIVER=Telnet
CONFIG
SECURETELNET=1 ; otherwise there's a gaping security hole
LOGGING=1
CMS=1
DisconnectOnClose=0 ; 1 = closes window when you bye
TCPPORT=8010
FBBPORT=8011
HTTPPORT=8008
LOGINPROMPT=user:
PASSWORDPROMPT=password:
MAXSESSIONS=10
RELAYAPPL=BBS ; RMS
CTEXT=Welcome to GB7RDG Telnet Server\n Enter ? for list of commands\n\n
USER=tf,xxxxxxxx,m0lte,,SYSOP
USER=2e1epq,xxxxxxxx,2e1epq
; https://www.cantab.net/users/john.wiseman/Documents/LinBPQ%20Applications%20Interface.html
CMDPORT 63000 63987
ENDPORT
; https://www.cantab.net/users/john.wiseman/Documents/BPQAXIP.htm
PORT
PORTNUM=8
ID=OARC-AXIP
DRIVER=BPQAXIP
QUALITY=50
MINQUAL=20
MAXFRAME=7
FRACK=7000
RESPTIME=1000
RETRIES=10
UNPROTO=ID
CONFIG
UDP 10094
MHEARD ON
BROADCAST NODES
NODESINTERVAL 1
BROADCAST ID
MAP GB7OUK 10.66.66.3 UDP 10094 B
MAP MB7NPW 10.66.66.17 UDP 10094 B
MAP MB7NGP 10.66.66.12 UDP 10094 B
ENDPORT
PORT
PORTNUM=9
ID=GB7CIP-AXIP
DRIVER=BPQAXIP ; Uses BPQAXIP.DLL
QUALITY=9 ; Quality factor applied to node broadcasts heard on
; this port, unless overridden by a locked route
; entry. Setting to 0 stops node broadcasts. Was 200
MINQUAL=0 ; Entries in the nodes table with qualities greater or
; equal to MINQUAL will be sent on this port. A value
; of 0 sends everything.
MAXFRAME=7 ; Max outstanding frames (1 thru 7)
FRACK=7000 ; Level 2 timout in milliseconds
RESPTIME=1000 ; Level 2 delayed ack timer in milliseconds
RETRIES=10 ; Level 2 maximum retry value
UNPROTO=ID ; BTEXT broadcast addrs format: DEST[,digi1[,digi2]]
;BCALL=NOCALL ; BTEXT call. unstated defaults to APPL1CALL
CONFIG
UDP 10093 ;Listens for UDP packets on this UDP port number
MHEARD ON
; AUTOADDMAP
BROADCAST NODES
BROADCAST ID
; MAP CALL IP-ADDRESS UDP 10093 B
MAP GB7CIP-5 82.70.39.222 UDP 10093 B
MAP GB7NXT 79.77.173.61 UDP 10093 B
ENDPORT
LINMAIL
LINCHAT
;APPLICATION n,CMD,New Command,Call,Alias,Quality,L2Alias
;
;n Application Number. You can define up to 32.
;CMD The command the user types
;New Command (optional) The Node command to be run
;Call (optional) The call which directly invokes CMD
;Alias,Quality (optional) If specified, causes an entry for Call and Alias to be added to your NODES table with the specified Quality
;L2Alias (optional)is an additional Alias for direct (L2) connects to the Node.
APPLICATION 1,BBS,,GB7RDG-2,RDGBBS,255
APPLICATION 2,CHAT,,GB7RDG-1,RDGCHT,255
; https://www.cantab.net/users/john.wiseman/Documents/LinBPQ%20Applications%20Interface.html
; APPLICATION 3,LINUX,C 2 HOST 0 S
;APPLICATION 3,TEST,C 2 HOST 0 S
APPLICATION 3,TELSTAR,ATTACH 7 GLASSTTY.COM 6502,GB7RDG-5,TSTARV,0
APPLICATION 4,WALL,C 7 HOST 1 K S
@914tom
Copy link

914tom commented Jan 9, 2025

Can anyone help me out here please?
I hav a Linbpq up and running and try to add ZORK to the Application via a Startscript.
This won't work, and I dont know where is my Problem?
APPLICATION 3,ZORK,/usr/local/bin/ZORK.Start.sh
It doesent matter what I do in the Script, bbq won't even try to start it.
ZORK appears in the manue of possible Application if i connect via Telnet.
If I try to start ZORK, I get the Error
Invalid command - Enter ? for command list
have anyone a clue where is my problem and how to solve it please?
73
Tom

@M0LTE
Copy link
Author

M0LTE commented Jan 9, 2025 via email

@gregoryfenton
Copy link

gregoryfenton commented Feb 19, 2026

Can anyone help me out here please? I hav a Linbpq up and running and try to add ZORK to the Application via a Startscript. This won't work, and I dont know where is my Problem? APPLICATION 3,ZORK,/usr/local/bin/ZORK.Start.sh It doesent matter what I do in the Script, bbq won't even try to start it. ZORK appears in the manue of possible Application if i connect via Telnet. If I try to start ZORK, I get the Error Invalid command - Enter ? for command list have anyone a clue where is my problem and how to solve it please? 73 Tom

It's actually simple once you understand how to do it.
in bpq32.cfg, look at your telnet server section (here's mine):

    PORT
    PORTNUM=1
    ID=Telnet Server
    DRIVER=Telnet
    CONFIG
        LOGGING=1
        CMS=1
        HTTPPORT=8080
        TCPPORT=23
        FBBPORT=8010
        USER=M0ODZ,PASSWORD,M0ODZ,,SYSOP
        DISCONNECTONCLOSE=1
        CMDPORT 63000 63001
    CTEXT=Welcome to GB7ODZ Telnet Server\n Enter  ?  for list of commands\n\n
ENDPORT

In the APPLICATIONS section:

APPLICATION 4,HELLO,C 1 HOST 0 K S,YOURNODECALL-4,YOURAPPSHORTNAME,255

replacing YOURNODECALL with your node call eg. M0QRZ and YOURAPPSHORTNAME with a snappier version of the appname a remote user can connect directly to eg. QRZHLO, so a remote user can connect directly to QRZHLO and they will be served with the app. For Example:

APPLICATION 4,HELLO,C 1 HOST 0 K S,M0QRZ-4,QRZHLO,255

Do you see the CMDPORT line? That is where the magic happens. The number that follows is a port number on the BPQ32 computer that an app listens to.

install node and npm:

sudo apt install nodejs npm

write a .js file to create a server on one of the ports listed in CMDLINE eg 63000.

/**
 * Simple Packet Radio "Hello World" Server
 * Runs on Port 63000
 */

const net = require('net');
const SERVERADDRESS='127.0.0.1';
const PORT = 63000;
const MESSAGE = "Hello packet radio world!\r\n";

const server = net.createServer((socket) => {
    // Log connection to the screen
    console.log(`Connection received from: ${socket.remoteAddress}`);

    // Send the message
    socket.write(MESSAGE);

    // Give it a tiny delay to ensure the packet is buffered 
    // through the LinBPQ stack before closing
    setTimeout(() => {
        socket.end();
    }, 500);
});

server.on('error', (err) => {
    console.error(`Server error: ${err.message}`);
});

server.listen(PORT, SERVERADDRESS, () => {
    console.log(`Packet App listening on ${SERVERADDRESS}:${PORT}`);
});

Save this as sampleserver.js
run

node sampleserver.js

in a new window, type

telnet SERVERADDRESS PORT

eg.

telnet 127.0.0.1 63000

And you should see Hello packet radio world! displayed on your screen.

For your real world situation (playing Zork, much respect!):

PORT
    PORTNUM=1
    ID=Telnet Server
    DRIVER=Telnet
    CONFIG
        LOGGING=1
        CMS=1
        HTTPPORT=8080
        TCPPORT=23
        FBBPORT=8010
        USER=M0ODZ,PASSWORD,M0ODZ,,SYSOP
        DISCONNECTONCLOSE=1
        CMDPORT 63000 63001
    CTEXT=Welcome to GB7ODZ Telnet Server\n Enter  ?  for list of commands\n\n
ENDPORT

APPLICATION 4,ZORK1,C 1 HOST 0 K S,M0QRZ-4,QRZZ1,255 ; CMDPORT first number (starting counting at 0) 63000
APPLICATION 5,ZORK2,C 1 HOST 1 K S,M0QRZ-4,QRZZ2,255 ; CMDPORT second number 63001

If you look carefully you will notice C 1 HOST 0 K S and C 1 HOST 1 K S. 0 is the first number in the CMDPORT list, 1 is the second and so on.

Here is a real world script to run Zork:

/**
 * Zork1.z5 Packet Radio Wrapper
 * * This script:
 * 1. Listens on Port 63000
 * 2. Asks for a Callsign (if not automatically provided by BPQ)
 * 3. Creates a unique directory for that Callsign
 * 4. Launches 'frotz' inside that directory
 */

const net = require('net');
const { spawn } = require('child_process');
const fs = require('fs');
const path = require('path');

const PORT = 63000;
const GAMES_DIR = path.join(__dirname, 'player_data');

// Ensure the base games directory exists
if (!fs.existsSync(GAMES_DIR)) {
    fs.mkdirSync(GAMES_DIR);
}

const server = net.createServer((socket) => {
    socket.write("--- Welcome to the Zork 1 Packet Portal ---\r\n");
    socket.write("Please enter your Callsign: ");

    let callsign = "";

    socket.on('data', (data) => {
        // If we haven't got the callsign yet, treat the first input as the ID
        if (!callsign) {
            callsign = data.toString().trim().toUpperCase().replace(/[^A-Z0-9-]/g, '');
            
            if (callsign.length < 3) {
                socket.write("\r\nInvalid Callsign. Please try again: ");
                callsign = "";
                return;
            }
            socket.write(`\r\nWelcome ${callsign}\r\n\r\n`);

            setupGame(socket, callsign);
        }
    });
});

function setupGame(socket, userCall) {
    const userDir = path.join(GAMES_DIR, `SAVEDIR_${userCall}`);

    // Create user-specific directory for save files
    if (fs.existsSync(userDir)) {
        socket.write(`\r\nWelcome back, ${userCall}!\r\n`);
    }
    else
    {
        fs.mkdirSync(userDir);
        socket.write(`\r\nWelcome, ${userCall}!\r\n\r\n`);
    }
    socket.write("Loading Zork 1...\r\n\r\n");

    // Start frotz
    // -p: plain text mode (better for packet/telnet)
    const frotz = spawn('frotz', ['-p', '/usr/share/games/zork1.z5'], {
        cwd: userDir
    });

    // Pipe socket to frotz stdin
    socket.pipe(frotz.stdin);

    // Pipe frotz stdout back to socket
    frotz.stdout.on('data', (data) => {
        // Replace Unix newlines with Telnet/Packet friendly CRLF
        socket.write(data.toString().replace(/\n/g, '\r\n'));
    });

    frotz.stderr.on('data', (data) => {
        console.error(`Frotz Error: ${data}`);
    });

    frotz.on('close', (code) => {
        socket.write("\r\n[Zork Session Closed - I blame the Grue!]\r\n");
        socket.end();
    });

    socket.on('close', () => {
        frotz.kill();
    });
}

server.listen(PORT, '0.0.0.0', () => {
    console.log(`Zork 1 Server listening on port ${PORT}`);
});

Save this as zork1server.js
run
node zork1server.js

To make it permanent:

sudo npm install -g pm2

# Start your script
pm2 start zork1server.js --name "zork-node"

# Make it start on boot
pm2 startup
pm2 save

Pretty soon, you'll outgrow 1 app, 1 port and make a menu script so all the apps run on the same port.

de M0ODZ Greg

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment