Skip to content

Add UDP Protocol #1433

Open
auzcoder wants to merge 5 commits intoqzind:masterfrom
auzcoder:master
Open

Add UDP Protocol #1433
auzcoder wants to merge 5 commits intoqzind:masterfrom
auzcoder:master

Conversation

@auzcoder
Copy link
Copy Markdown

@auzcoder auzcoder commented Apr 8, 2026

No description provided.

@tresf
Copy link
Copy Markdown
Contributor

tresf commented Apr 8, 2026

@auzcoder thanks, I approved the workflow to run so that you can download the binaries. They'll appear in the summary section of GitHub actions once ready.... e.g. https://github.com/qzind/tray/actions/runs/24137180724#artifacts

Thanks for this code contribution. I didn't expect this to require JNA... can you explain a bit about the addition of COM calls to this per (e.g. ShtrihUtilities). We generally try to avoid these platform-specific-type calls so this will be scrutinized heavily before merging.

Also note that all API calls are subject to change, so please feel free to integrate with the one-off builds provided by our build system, but if we add this feature for 2.3.x the API may change. We provide a https://qz.io/api/qz.api#.isVersionGreater call that you can potentially use in the future to support both versions.

@auzcoder
Copy link
Copy Markdown
Author

auzcoder commented Apr 9, 2026

QZ Tray UDP Integration Guide (Shtrih-M Scales)

This guide provides technical details for integrating industrial hardware (specifically Shtrikh-M scales) with your web application using the custom UDP-enabled QZ Tray.

1. Quick Start Connection

To establish a connection with the scale over UDP, use the following JavaScript configuration:

var config = {
    host: "192.168.85.208", // Scale IP
    port: 1213,             // Scale listening port (default for Shtrih-M Ethernet)
    options: {
        protocol: "udp",
        localPort: 2000,    // PC source port (Scale expects 2000)
        responseFormat: "HEX" // Critical for binary protocols
    }
};

qz.socket.open(config.host, config.port, config.options)
.then(() => console.log("UDP Socket Connected"))
.catch(err => console.error(err));

<clipboard-copy aria-label="Copy" class="ClipboardButton btn btn-invisible js-clipboard-copy m-2 p-0 d-flex flex-justify-center flex-items-center" data-copy-feedback="Copied!" data-tooltip-direction="w" value="var config = {
host: "192.168.85.208", // Scale IP
port: 1213, // Scale listening port (default for Shtrih-M Ethernet)
options: {
protocol: "udp",
localPort: 2000, // PC source port (Scale expects 2000)
responseFormat: "HEX" // Critical for binary protocols
}
};

qz.socket.open(config.host, config.port, config.options)
.then(() => console.log("UDP Socket Connected"))
.catch(err => console.error(err));" tabindex="0" role="button" style="box-sizing: border-box; padding: 0px !important; font-size: 14px; font-weight: 500; white-space: nowrap; vertical-align: middle; cursor: pointer; user-select: none; appearance: none; border: 0px; border-radius: 6px; line-height: 20px; display: flex !important; position: relative; color: rgb(68, 147, 248); background-color: rgba(0, 0, 0, 0); box-shadow: none; transition: color 80ms cubic-bezier(0.33, 1, 0.68, 1), background-color 80ms cubic-bezier(0.33, 1, 0.68, 1), box-shadow 80ms cubic-bezier(0.33, 1, 0.68, 1), border-color 80ms cubic-bezier(0.33, 1, 0.68, 1); justify-content: center !important; align-items: center !important; margin: 8px !important; width: 28px; height: 28px;">

2. Handling Responses

Since hardware protocols are binary, QZ Tray converts incoming bytes into HEX strings. You must set up an event listener to capture these.

qz.socket.setEventListener(function(event) {
if (event.type === 'RECEIVE') {
processScaleResponse(event.response);
}
});

function processScaleResponse(hex) {
console.log("Raw HEX from Scale:", hex);

<span class="pl-c" style="box-sizing: border-box; color: rgb(145, 152, 161);">// Example: Response 02021300 means Success (00)</span>
<span class="pl-k" style="box-sizing: border-box; color: rgb(255, 123, 114);">if</span> <span class="pl-kos" style="box-sizing: border-box;">(</span><span class="pl-s1" style="box-sizing: border-box;">hex</span><span class="pl-kos" style="box-sizing: border-box;">.</span><span class="pl-en" style="box-sizing: border-box; color: rgb(210, 168, 255);">startsWith</span><span class="pl-kos" style="box-sizing: border-box;">(</span><span class="pl-s" style="box-sizing: border-box; color: rgb(165, 214, 255);">"02"</span><span class="pl-kos" style="box-sizing: border-box;">)</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(121, 192, 255);">&amp;&amp;</span> <span class="pl-s1" style="box-sizing: border-box;">hex</span><span class="pl-kos" style="box-sizing: border-box;">.</span><span class="pl-en" style="box-sizing: border-box; color: rgb(210, 168, 255);">endsWith</span><span class="pl-kos" style="box-sizing: border-box;">(</span><span class="pl-s" style="box-sizing: border-box; color: rgb(165, 214, 255);">"00"</span><span class="pl-kos" style="box-sizing: border-box;">)</span><span class="pl-kos" style="box-sizing: border-box;">)</span> <span class="pl-kos" style="box-sizing: border-box;">{</span>
    <span class="pl-en" style="box-sizing: border-box; color: rgb(210, 168, 255);">alert</span><span class="pl-kos" style="box-sizing: border-box;">(</span><span class="pl-s" style="box-sizing: border-box; color: rgb(165, 214, 255);">"Action Successful!"</span><span class="pl-kos" style="box-sizing: border-box;">)</span><span class="pl-kos" style="box-sizing: border-box;">;</span>
<span class="pl-kos" style="box-sizing: border-box;">}</span>

}

<clipboard-copy aria-label="Copy" class="ClipboardButton btn btn-invisible js-clipboard-copy m-2 p-0 d-flex flex-justify-center flex-items-center" data-copy-feedback="Copied!" data-tooltip-direction="w" value="qz.socket.setEventListener(function(event) {
if (event.type === 'RECEIVE') {
processScaleResponse(event.response);
}
});

function processScaleResponse(hex) {
console.log("Raw HEX from Scale:", hex);

// Example: Response 02021300 means Success (00)
if (hex.startsWith(&quot;02&quot;) &amp;&amp; hex.endsWith(&quot;00&quot;)) {
    alert(&quot;Action Successful!&quot;);
}

}" tabindex="0" role="button" style="box-sizing: border-box; padding: 0px !important; font-size: 14px; font-weight: 500; white-space: nowrap; vertical-align: middle; cursor: pointer; user-select: none; appearance: none; border: 0px; border-radius: 6px; line-height: 20px; display: flex !important; position: relative; color: rgb(68, 147, 248); background-color: rgba(0, 0, 0, 0); box-shadow: none; transition: color 80ms cubic-bezier(0.33, 1, 0.68, 1), background-color 80ms cubic-bezier(0.33, 1, 0.68, 1), box-shadow 80ms cubic-bezier(0.33, 1, 0.68, 1), border-color 80ms cubic-bezier(0.33, 1, 0.68, 1); justify-content: center !important; align-items: center !important; margin: 8px !important; width: 28px; height: 28px;">

3. Shtrih-M Protocol Packets

All packets follow this structure: STX (02) | LEN | CMD | DATA | CRC (XOR).

Common Commands:

Command HEX Packet (Password: 30) Purpose
Gudok (Beep) 02011312 Chirps the scale to verify connection.
Status 0205111E0000000A Returns internal flags and state.
Get Weight 0205151E0000000E Returns current weight in grams.

4. CRC Calculation (JavaScript)

Use this helper to construct packets dynamically:

function sendShtrihPacket(cmd, dataBytes) {
var len = dataBytes.length + 1;
var packet = [len, cmd].concat(dataBytes);

<span class="pl-k" style="box-sizing: border-box; color: rgb(255, 123, 114);">var</span> <span class="pl-s1" style="box-sizing: border-box;">crc</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(121, 192, 255);">=</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(121, 192, 255);">0</span><span class="pl-kos" style="box-sizing: border-box;">;</span>
<span class="pl-k" style="box-sizing: border-box; color: rgb(255, 123, 114);">for</span> <span class="pl-kos" style="box-sizing: border-box;">(</span><span class="pl-k" style="box-sizing: border-box; color: rgb(255, 123, 114);">var</span> <span class="pl-s1" style="box-sizing: border-box;">i</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(121, 192, 255);">=</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(121, 192, 255);">0</span><span class="pl-kos" style="box-sizing: border-box;">;</span> <span class="pl-s1" style="box-sizing: border-box;">i</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(121, 192, 255);">&lt;</span> <span class="pl-s1" style="box-sizing: border-box;">packet</span><span class="pl-kos" style="box-sizing: border-box;">.</span><span class="pl-c1" style="box-sizing: border-box; color: rgb(121, 192, 255);">length</span><span class="pl-kos" style="box-sizing: border-box;">;</span> <span class="pl-s1" style="box-sizing: border-box;">i</span><span class="pl-c1" style="box-sizing: border-box; color: rgb(121, 192, 255);">++</span><span class="pl-kos" style="box-sizing: border-box;">)</span> <span class="pl-kos" style="box-sizing: border-box;">{</span>
    <span class="pl-s1" style="box-sizing: border-box;">crc</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(121, 192, 255);">^=</span> <span class="pl-s1" style="box-sizing: border-box;">packet</span><span class="pl-kos" style="box-sizing: border-box;">[</span><span class="pl-s1" style="box-sizing: border-box;">i</span><span class="pl-kos" style="box-sizing: border-box;">]</span><span class="pl-kos" style="box-sizing: border-box;">;</span>
<span class="pl-kos" style="box-sizing: border-box;">}</span>

<span class="pl-k" style="box-sizing: border-box; color: rgb(255, 123, 114);">var</span> <span class="pl-s1" style="box-sizing: border-box;">fullPacket</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(121, 192, 255);">=</span> <span class="pl-kos" style="box-sizing: border-box;">[</span><span class="pl-c1" style="box-sizing: border-box; color: rgb(121, 192, 255);">0x02</span><span class="pl-kos" style="box-sizing: border-box;">]</span><span class="pl-kos" style="box-sizing: border-box;">.</span><span class="pl-en" style="box-sizing: border-box; color: rgb(210, 168, 255);">concat</span><span class="pl-kos" style="box-sizing: border-box;">(</span><span class="pl-s1" style="box-sizing: border-box;">packet</span><span class="pl-kos" style="box-sizing: border-box;">)</span><span class="pl-kos" style="box-sizing: border-box;">.</span><span class="pl-en" style="box-sizing: border-box; color: rgb(210, 168, 255);">concat</span><span class="pl-kos" style="box-sizing: border-box;">(</span><span class="pl-kos" style="box-sizing: border-box;">[</span><span class="pl-s1" style="box-sizing: border-box;">crc</span><span class="pl-kos" style="box-sizing: border-box;">]</span><span class="pl-kos" style="box-sizing: border-box;">)</span><span class="pl-kos" style="box-sizing: border-box;">;</span>
<span class="pl-k" style="box-sizing: border-box; color: rgb(255, 123, 114);">var</span> <span class="pl-s1" style="box-sizing: border-box;">hexString</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(121, 192, 255);">=</span> <span class="pl-s1" style="box-sizing: border-box;">fullPacket</span><span class="pl-kos" style="box-sizing: border-box;">.</span><span class="pl-en" style="box-sizing: border-box; color: rgb(210, 168, 255);">map</span><span class="pl-kos" style="box-sizing: border-box;">(</span><span class="pl-s1" style="box-sizing: border-box;">b</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(121, 192, 255);">=&gt;</span> <span class="pl-kos" style="box-sizing: border-box;">(</span><span class="pl-s" style="box-sizing: border-box; color: rgb(165, 214, 255);">"0"</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(121, 192, 255);">+</span> <span class="pl-s1" style="box-sizing: border-box;">b</span><span class="pl-kos" style="box-sizing: border-box;">.</span><span class="pl-en" style="box-sizing: border-box; color: rgb(210, 168, 255);">toString</span><span class="pl-kos" style="box-sizing: border-box;">(</span><span class="pl-c1" style="box-sizing: border-box; color: rgb(121, 192, 255);">16</span><span class="pl-kos" style="box-sizing: border-box;">)</span><span class="pl-kos" style="box-sizing: border-box;">)</span><span class="pl-kos" style="box-sizing: border-box;">.</span><span class="pl-en" style="box-sizing: border-box; color: rgb(210, 168, 255);">slice</span><span class="pl-kos" style="box-sizing: border-box;">(</span><span class="pl-c1" style="box-sizing: border-box; color: rgb(121, 192, 255);">-</span><span class="pl-c1" style="box-sizing: border-box; color: rgb(121, 192, 255);">2</span><span class="pl-kos" style="box-sizing: border-box;">)</span><span class="pl-kos" style="box-sizing: border-box;">.</span><span class="pl-en" style="box-sizing: border-box; color: rgb(210, 168, 255);">toUpperCase</span><span class="pl-kos" style="box-sizing: border-box;">(</span><span class="pl-kos" style="box-sizing: border-box;">)</span><span class="pl-kos" style="box-sizing: border-box;">)</span><span class="pl-kos" style="box-sizing: border-box;">.</span><span class="pl-en" style="box-sizing: border-box; color: rgb(210, 168, 255);">join</span><span class="pl-kos" style="box-sizing: border-box;">(</span><span class="pl-s" style="box-sizing: border-box; color: rgb(165, 214, 255);">""</span><span class="pl-kos" style="box-sizing: border-box;">)</span><span class="pl-kos" style="box-sizing: border-box;">;</span>

<span class="pl-k" style="box-sizing: border-box; color: rgb(255, 123, 114);">return</span> <span class="pl-s1" style="box-sizing: border-box;">qz</span><span class="pl-kos" style="box-sizing: border-box;">.</span><span class="pl-c1" style="box-sizing: border-box; color: rgb(121, 192, 255);">socket</span><span class="pl-kos" style="box-sizing: border-box;">.</span><span class="pl-en" style="box-sizing: border-box; color: rgb(210, 168, 255);">sendData</span><span class="pl-kos" style="box-sizing: border-box;">(</span><span class="pl-s1" style="box-sizing: border-box;">host</span><span class="pl-kos" style="box-sizing: border-box;">,</span> <span class="pl-s1" style="box-sizing: border-box;">port</span><span class="pl-kos" style="box-sizing: border-box;">,</span> <span class="pl-kos" style="box-sizing: border-box;">{</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(121, 192, 255);">data</span>: <span class="pl-s1" style="box-sizing: border-box;">hexString</span><span class="pl-kos" style="box-sizing: border-box;">,</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(121, 192, 255);">type</span>: <span class="pl-s" style="box-sizing: border-box; color: rgb(165, 214, 255);">'HEX'</span> <span class="pl-kos" style="box-sizing: border-box;">}</span><span class="pl-kos" style="box-sizing: border-box;">)</span><span class="pl-kos" style="box-sizing: border-box;">;</span>

}

<clipboard-copy aria-label="Copy" class="ClipboardButton btn btn-invisible js-clipboard-copy m-2 p-0 d-flex flex-justify-center flex-items-center" data-copy-feedback="Copied!" data-tooltip-direction="w" value="function sendShtrihPacket(cmd, dataBytes) {
var len = dataBytes.length + 1;
var packet = [len, cmd].concat(dataBytes);

var crc = 0;
for (var i = 0; i &lt; packet.length; i++) {
    crc ^= packet[i];
}

var fullPacket = [0x02].concat(packet).concat([crc]);
var hexString = fullPacket.map(b =&gt; (&quot;0&quot; + b.toString(16)).slice(-2).toUpperCase()).join(&quot;&quot;);

return qz.socket.sendData(host, port, { data: hexString, type: 'HEX' });

}" tabindex="0" role="button" style="box-sizing: border-box; padding: 0px !important; font-size: 14px; font-weight: 500; white-space: nowrap; vertical-align: middle; cursor: pointer; user-select: none; appearance: none; border: 0px; border-radius: 6px; line-height: 20px; display: flex !important; position: relative; color: rgb(68, 147, 248); background-color: rgba(0, 0, 0, 0); box-shadow: none; transition: color 80ms cubic-bezier(0.33, 1, 0.68, 1), background-color 80ms cubic-bezier(0.33, 1, 0.68, 1), box-shadow 80ms cubic-bezier(0.33, 1, 0.68, 1), border-color 80ms cubic-bezier(0.33, 1, 0.68, 1); justify-content: center !important; align-items: center !important; margin: 8px !important; width: 28px; height: 28px;">

5. Troubleshooting

  • Address already in use: Ensure no other software (like the Shtrih-M Test Driver) is using Port 2000 or 1213.
  • No Response: Verify you are sending the correct 4-byte password. Default is 30 (Hex 1E).
  • Firewall: Ensure the Windows Firewall allows UDP traffic on the specified ports.

@tresf
Copy link
Copy Markdown
Contributor

tresf commented Apr 9, 2026

@auzcoder please cleanup the above post it's filled with formatting issues.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants