jsax25
    Preparing search index...

    jsax25

    JSAX25

    AX.25 amateur packet radio for Node, made simple.

    GitHub TypeDoc npm version Coverage Codefactor

    Use your favorite JS package manager to add the package to your project:

    npm install jsax25      # npm

    yarn add jsax25 # yarn

    bun add jsax25 # bun

    This library assumes that you already have some understanding of the AX.25 standard.

    The latest revision of the AX.25 standard can be found here.

    import { createConnection } from "net";
    import { IncomingFrame, TESTFrame } from "jsax25";

    const test = new TESTFrame({
    destinationCallsign: 'N0CALL',
    destinationSsid: 10,
    sourceCallsign: 'N0CALL',
    sourceSsid: 7,
    payload: 'hello world'
    })

    createConnection({
    host: 'localhost',
    port: 8001
    })
    .on('data', (chunk) => console.log(new IncomingFrame(chunk)))
    .write(test.encode())
    import { createConnection, Socket } from "net";
    import { IncomingFrame, UIFrame } from "jsax25";

    const MY_CALL: string = 'N0CALL'
    const MY_SSID: number = 7

    // create a KISS connection to your running software modem or hardware TNC
    const kissConnection: Socket = createConnection({
    host: 'localhost',
    port: 8001
    })

    // setup an event listener
    kissConnection.on('data', (chunk) => {
    // create an IncomingFrame object from the chunk to decode it
    const incoming: IncomingFrame = new IncomingFrame(chunk)

    // do something with the data
    console.log(incoming)
    if (incoming.destinationCallsign === MY_CALL && incoming.destinationSsid === MY_SSID && incoming.subtype === 'UI') {
    const reply: UIFrame = incoming.replyWith('UI', `${MY_CALL}-${MY_SSID} has received your frame. Greetings!`)
    kissConnection.write(reply.encode())
    }
    })
    import { DISCFrame, IncomingFrame, SABMFrame } from "jsax25";
    import { SerialPort } from "serialport"

    const MY_CALL: string = 'N0CALL'
    const MY_SSID: number = 7

    const THEIR_CALL: string = 'N0CALL'
    const THEIR_SSID: number = 10

    async function connect(destinationCallsign: string, destinationSsid: number, sourceCallsign: string, sourceSsid: number, kissConnection: SerialPort): Promise<void> {
    return new Promise((resolve, reject) => {
    const sabm: SABMFrame = new SABMFrame({ destinationCallsign, destinationSsid, sourceCallsign, sourceSsid }) // create an SABM or SABME frame

    const handleConnection = (data: Buffer) => { // create a handler function for the on('data') event
    const response: IncomingFrame = new IncomingFrame(data)
    if (response.destinationCallsign === MY_CALL && response.destinationSsid === MY_SSID) { // if it's addressed to us
    if (response.subtype === 'UA') { // success
    resolve()
    }
    else if (response.subtype === 'DM') { // failure
    reject(new Error('The remote station is not ready for connection.'))
    }
    else { // unknown response
    reject(new Error(`The remote station responded with a ${response.subtype} frame. Expected a UA or DM frame.`))
    }
    kissConnection.removeListener('data', handleConnection) // don't forget to remove the handler
    }
    }
    kissConnection.on('data', handleConnection) // set the handler
    kissConnection.write(sabm.encode()) // send the frame
    })
    }

    async function disconnect(destinationCallsign: string, destinationSsid: number, sourceCallsign: string, sourceSsid: number, kissConnection: SerialPort, retries: number = 3): Promise<void> {
    return new Promise((resolve, reject) => {
    // keep track of how many attempts to disconnect have been made
    let attempts: number = 0

    // create a disconnect frame and encode it
    const disc: Uint8Array = new DISCFrame({ destinationCallsign, destinationSsid, sourceCallsign, sourceSsid }).encode()

    // create a handler function for the on('data') event
    const checkUA = async (data: Buffer) => {
    const response: IncomingFrame = new IncomingFrame(data)
    if (response.destinationCallsign === MY_CALL && response.destinationSsid === MY_SSID) { // if it's addressed to us
    if (response.subtype === 'UA') { // success
    resolve()
    kissConnection.removeListener('data', checkUA) // don't forget to remove the handler
    }
    else { // maybe failure
    if (attempts <= retries) { // try again if we haven't reached our retry limit
    kissConnection.write(disc)
    }
    else { // otherwise failure
    reject(new Error(`Unable to disconnect properly from ${destinationCallsign}-${destinationSsid}`))
    kissConnection.removeListener('data', checkUA) // don't forget to remove the handler
    }
    }
    }
    }
    kissConnection.on('data', checkUA) // set the handler
    kissConnection.write(disc) // send the frame
    attempts++ // increment the amount of times we've attempted to disconnect
    })
    }

    const kissConnection: SerialPort = new SerialPort({
    path: '/dev/rfcomm0',
    baudRate: 9600
    })
    kissConnection.on('data', (chunk) => console.log(new IncomingFrame(chunk)))

    try {
    await connect(THEIR_CALL, THEIR_SSID, MY_CALL, MY_SSID, kissConnection)
    console.log(`Connected to ${THEIR_CALL}-${THEIR_SSID}`)
    // continue with your program here...
    // All link state management must be done manually (I frames, retries, incrementing receievedSequence/sendSequence, acknowledgement, etc.). A future version of JSAX25 may automate this.
    await disconnect(THEIR_CALL, THEIR_SSID, MY_CALL, MY_SSID, kissConnection)
    console.log(`Disconnected from ${THEIR_CALL}-${THEIR_SSID}`)
    }
    catch (error) {
    console.log(error)
    kissConnection.destroy()
    }