Skip to content

Instantly share code, notes, and snippets.

@Webbanditten
Last active June 30, 2026 09:54
Show Gist options
  • Select an option

  • Save Webbanditten/30196d2d6d324030671895e19218535a to your computer and use it in GitHub Desktop.

Select an option

Save Webbanditten/30196d2d6d324030671895e19218535a to your computer and use it in GitHub Desktop.
Dinec Wiegand encoding
/**
* Dinec Wiegand encoding: EM4100 RFID -> "Chip system" badge id (Badge1).
*
* Use to enroll chips from a web dashboard instead of Dinec's software.
* Take the decimal RFID a scanner reads, produce the hex value Dinec stores
* in [DBM6000].[dbo].[Users].[Badge1] (CardType1 = EM4100). Write that, done.
*
* NOT encryption. Reversible encoding = bit-reversal of RFID's low 24 bits,
* framed as 26-bit Wiegand with inverted parity (lead odd / trail even).
* Reverse-engineered from 25 known pairs, 100% match. RFID bits above bit23
* (the 371.../361... manufacturer prefix) are discarded.
*/
function parity(x) {
let p = 0;
while (x) { p ^= x & 1; x >>>= 1; }
return p;
}
function rev24(x) {
let r = 0;
for (let i = 0; i < 24; i++) r = (r << 1) | ((x >>> i) & 1);
return r >>> 0;
}
/**
* Encode a scanned EM4100 RFID into the Dinec chip id (numeric).
* @param {number} rfid - decimal RFID from the scanner (e.g. 371045138)
* @returns {number} 26-bit chip id (e.g. 0x2919B71)
*/
function encode(rfid) {
const payload = rev24(rfid & 0xFFFFFF); // chip bits 24..1
const pLead = parity((payload >>> 12) & 0xFFF) ^ 1; // leading = odd parity
const pTrail = parity(payload & 0xFFF); // trailing = even parity
return ((pLead << 25) | (payload << 1) | pTrail) >>> 0;
}
/**
* Encode to the uppercase hex string stored in Badge1 (no leading zeros),
* e.g. encodeBadge(371045138) === "2919B71".
* @param {number|string} rfid - decimal RFID from the scanner
* @returns {string}
*/
function encodeBadge(rfid) {
return encode(Number(rfid)).toString(16).toUpperCase();
}
// ---- self-test: node dinec_wiegand.js ----
if (typeof require !== 'undefined' && require.main === module) {
const data = [
[371045138, '2919B71'], [371044110, 'E1EB70'], [371050725, '34E2770'], [371050850, '8D2770'],
[371035623, '1CF6371'], [371035018, '2A3A371'], [371043008, '206AB70'], [371039417, '13A7370'],
[371044608, '11B71'], [361929066, '2AD3293'], [371050019, '188C770'], [371049170, '968770'],
[371043089, '111AB70'], [371042545, '31E2B70'], [371041147, '3BD8B70'], [371052071, '1C8E770'],
[371048660, '2560770'], [371047717, '3497B71'], [371044847, '3EF1B71'], [371051508, '25FA770'],
[371034902, '2D1A371'], [371045289, '32B9B71'], [371051072, '4A770'], [371036201, '1281370'],
[2973668, '24FF568'],
];
const ok = data.every(([r, b]) => encodeBadge(r) === b);
console.log(ok ? `ALL ${data.length} MATCH` : 'FAIL');
}
if (typeof module !== 'undefined' && module.exports) {
module.exports = { encode, encodeBadge };
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment