pliceclogs-og/rfid-daemon/probe.js
type-two e5b8504bc6 Import pliceclogs as-is — the original Discogs seller extension
Snapshot of the working tree exactly as it stood, no edits. This is the predecessor
PRICEGOD was rewritten from ("kept intact, untouched" per pricegod/README.md), and it
is still the only place the DYMO scale is actually implemented —
rfid-daemon/index.js:1068-1265: HID discovery, parseScaleReport, one-shot read, and a
streaming /weight + /scale/start|stop session whose JSON shape PRICEGOD's daemon.js
already speaks.

Preserved verbatim on purpose (hence -og), including the known bug: DYMO_PIDS at
index.js:1082 is [0x8003, 0x8004], so it cannot see the bench M25 (0x8009). Fix that
in whatever daemon inherits the scale, not here.

node_modules stays ignored (22M of the 24M tree). No credentials in the import: the
two PEM markers in utils.js/sheets.js only strip headers off a key read from settings,
and mrpadmin / johnking are an SSH and a Postgres username, both key/trust auth.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 13:47:09 +10:00

155 lines
5.5 KiB
JavaScript

'use strict';
// Chafon H102 probe: scan baud rates, attempt GET_ALL_PARAM, attempt SET interface to CDC_COM
const { SerialPort } = require('serialport');
const PORT = process.env.RFID_PORT || '/dev/cu.usbserial-5130';
const BAUDS = [115200, 9600, 38400, 57600, 19200];
function crc16(buf) {
let crc = 0xFFFF;
for (const b of buf) {
crc ^= b;
for (let i = 0; i < 8; i++)
crc = (crc & 1) ? ((crc >> 1) ^ 0x8408) : (crc >> 1);
}
return crc;
}
function frame(cmd, data = Buffer.alloc(0)) {
const hdr = Buffer.from([0xCF, 0x00, cmd[0], cmd[1], data.length]);
const body = Buffer.concat([hdr, data]);
const chk = crc16(body);
const f = Buffer.concat([body, Buffer.from([(chk >> 8) & 0xFF, chk & 0xFF])]);
console.log(` TX: ${f.toString('hex').replace(/../g,'$& ').trim().toUpperCase()}`);
return f;
}
const CMD_INVENTORY = [0x00, 0x01];
const CMD_GET_PARAM = [0x00, 0x72];
const CMD_SET_PARAM = [0x00, 0x71];
// AllParamBean minimal structure to set interface to CDC_COM (0x04)
// Based on SDK doc: mInterface byte is first param byte.
// We send a minimal known-safe config: keep everything default, just flip interface.
// Structure (from SDK AllParamBean): interface(1) baud(1) addr(1) power(1) ...
// We'll use GET_PARAM first to read existing values then patch interface byte.
function tryPort(baud) {
return new Promise(resolve => {
console.log(`\n── Testing ${baud} baud ──`);
const port = new SerialPort({ path: PORT, baudRate: baud, autoOpen: false });
port.open(err => {
if (err) { console.log(` open error: ${err.message}`); return resolve(null); }
let buf = Buffer.alloc(0);
let resolved = false;
const done = (result) => {
if (resolved) return;
resolved = true;
port.close(() => resolve(result));
};
port.on('data', chunk => {
const hex = chunk.toString('hex').replace(/../g,'$& ').trim().toUpperCase();
const asc = chunk.toString().replace(/[^\x20-\x7E]/g, '.');
console.log(` RX: ${hex} "${asc}"`);
buf = Buffer.concat([buf, chunk]);
});
// Send inventory first (simplest command)
port.write(frame(CMD_INVENTORY));
setTimeout(() => {
if (buf.length > 0) {
console.log(` ✓ Got ${buf.length} bytes at ${baud} baud`);
done({ baud, buf });
} else {
console.log(` ✗ No response at ${baud} baud`);
done(null);
}
}, 2000);
});
});
}
async function getAndPatchConfig(baud) {
return new Promise(resolve => {
console.log(`\n── GET_ALL_PARAM at ${baud} baud ──`);
const port = new SerialPort({ path: PORT, baudRate: baud, autoOpen: false });
port.open(err => {
if (err) return resolve(false);
let buf = Buffer.alloc(0);
port.on('data', chunk => {
buf = Buffer.concat([buf, chunk]);
const hex = chunk.toString('hex').replace(/../g,'$& ').trim().toUpperCase();
console.log(` RX: ${hex}`);
});
port.write(frame(CMD_GET_PARAM));
setTimeout(() => {
if (buf.length < 7) {
console.log(' No config response received');
port.close(() => resolve(false));
return;
}
// Response: CF ADDR CMD_H CMD_L LEN STATUS DATA... CRC_H CRC_L
// DATA is the AllParamBean bytes — patch interface byte (index 0 of data = STATUS+0)
// status = buf[5], param data starts at buf[6]
const status = buf[5];
console.log(` GET_PARAM status: 0x${status.toString(16)}`);
if (status !== 0x00) {
port.close(() => resolve(false));
return;
}
const paramData = buf.slice(6, buf.length - 2); // strip CRC
console.log(` Param bytes: ${paramData.toString('hex').replace(/../g,'$& ').trim().toUpperCase()}`);
console.log(` Current interface byte: 0x${paramData[0]?.toString(16)}`);
// Patch interface byte to 0x04 (CDC_COM serial mode)
paramData[0] = 0x04;
console.log(` Setting interface to 0x04 (CDC_COM)...`);
port.write(frame(CMD_SET_PARAM, paramData));
setTimeout(() => {
const hex2 = buf.slice(paramData.length + 9).toString('hex').replace(/../g,'$& ').trim().toUpperCase();
console.log(` SET_PARAM response raw: ${hex2 || '(none yet)'}`);
port.close(() => resolve(true));
}, 2000);
}, 2000);
});
});
}
async function main() {
console.log(`Probing ${PORT}`);
console.log('Place a tag on the reader if you have one handy.\n');
let workingBaud = null;
for (const baud of BAUDS) {
const result = await tryPort(baud);
if (result) { workingBaud = result.baud; break; }
}
if (!workingBaud) {
console.log('\n━━━ No response at any baud rate ━━━');
console.log('Device is likely in USB-HID keyboard wedge mode.');
console.log('Serial port (CH340) is present but firmware is not routing data to it.');
console.log('\nFix: use Chafon CfTech Android app over BLE to change Interface setting');
console.log(' to "CDC_COM" (0x04) or "USB" (0x01), then reconnect USB.');
console.log('\nSafer option: use one of the shop Android tablets (not your Pixel).');
return;
}
console.log(`\n✓ Device communicating at ${workingBaud} baud`);
await getAndPatchConfig(workingBaud);
console.log('\nDone. If SET_PARAM succeeded, unplug and replug the H102 USB.');
console.log(`Then restart the daemon with: RFID_BAUD=${workingBaud} node index.js`);
console.log('(or update BAUD_RATE in index.js if it differs from 115200)');
}
main().catch(console.error);