// chafcheck — probe the Chafon UHF reader via the native UHFPrimeReader.dll C API (P/Invoke). // Validates connection + read + write before wiring a Chafon /write-tag into the daemon. // Needs UHFPrimeReader.dll + hidapi.dll (x64) beside the exe. // // chafcheck info -> list USB-HID readers (no tag needed) // chafcheck read -> read the EPC of the tag in the field // chafcheck write <24-hex-EPC> -> write EPC to the tag in the field, then read back // // Build (x64, .NET Framework): // csc /platform:x64 /out:chafcheck.exe chafcheck.cs using System; using System.Runtime.InteropServices; using System.Text; class ChafCheck { const string DLL = "UHFPrimeReader.dll"; [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] static extern int CFHid_GetUsbCount(); [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] static extern int CFHid_GetUsbInfo(ushort index, byte[] pucDeviceInfo); [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] static extern int OpenHidConnection(ref long hComm, ushort index); [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] static extern int OpenDevice(ref long hComm, string pcCom, int iBaudRate); [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] static extern int CloseDevice(long hComm); [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] static extern int GetTagUii(long hComm, ref TagInfo tag, ushort timeout); [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)] static extern int WriteTag(long hComm, byte option, byte[] accPwd, byte memBank, ushort wordPtr, byte wordCount, byte[] writeData); [StructLayout(LayoutKind.Sequential)] struct TagInfo { public ushort NO; public short rssi; public byte antenna; public byte channel; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public byte[] crc; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public byte[] pc; public byte codeLen; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)] public byte[] code; } static TagInfo NewTag() { return new TagInfo { crc = new byte[2], pc = new byte[2], code = new byte[255] }; } static long Connect(string com) { long h = 0; int cnt = CFHid_GetUsbCount(); Console.WriteLine("CFHid_GetUsbCount = " + cnt); if (cnt > 0) { int r = OpenHidConnection(ref h, 0); Console.WriteLine("OpenHidConnection(0) = 0x" + r.ToString("X") + (r == 0 ? " (ok)" : " (FAIL)")); if (r == 0) return h; } // COM4/COM5 here are the Urovo's Bluetooth links — skip them. The H-102 is the CH340 port. string[] ports = com != null ? new[] { com } : new[] { "COM3", "COM6", "COM7", "COM8" }; foreach (var c in ports) { long hh = 0; int r = OpenDevice(ref hh, c, 115200); Console.WriteLine("OpenDevice(" + c + ",115200) = 0x" + r.ToString("X")); if (r == 0) return hh; } return 0; } static string Hex(byte[] b, int n) { var sb = new StringBuilder(); for (int i = 0; i < n && i < b.Length; i++) sb.Append(b[i].ToString("X2")); return sb.ToString(); } static byte[] FromHex(string h) { byte[] b = new byte[h.Length / 2]; for (int i = 0; i < b.Length; i++) b[i] = Convert.ToByte(h.Substring(2 * i, 2), 16); return b; } static string CodeName(int c) { switch ((uint)c) { case 0x00000000: return "(OK)"; case 0xFFFFFF08: return "(TAG no-resp — reader ANSWERS, just no tag in field ← good baud!)"; case 0xFFFFFF12: return "(comm timeout — reader not answering at this baud)"; case 0xFFFFFF13: return "(serial write failed)"; case 0xFFFFFF14: return "(serial read failed)"; default: return ""; } } static void Main(string[] args) { string action = args.Length > 0 ? args[0] : "info"; try { if (action == "info") { int cnt = CFHid_GetUsbCount(); Console.WriteLine("USB-HID Chafon reader count = " + cnt); for (ushort i = 0; i < cnt; i++) { var buf = new byte[512]; CFHid_GetUsbInfo(i, buf); Console.WriteLine(" [" + i + "] " + Encoding.ASCII.GetString(buf).Trim('\0')); } if (cnt == 0) Console.WriteLine("(none on USB-HID; reader may be on a COM port — try 'read')"); return; } string com = null; foreach (var a in args) if (System.Text.RegularExpressions.Regex.IsMatch(a, "^(?i)COM\\d+$")) com = a.ToUpper(); if (action == "scan") { int[] bauds = { 115200, 57600, 38400, 19200, 9600, 230400, 460800 }; foreach (int b in bauds) { long hh = 0; int r = OpenDevice(ref hh, com ?? "COM3", b); if (r != 0) { Console.WriteLine("baud " + b + ": open failed 0x" + r.ToString("X")); continue; } var t = NewTag(); int rr = GetTagUii(hh, ref t, 1200); Console.WriteLine("baud " + b + ": GetTagUii = 0x" + rr.ToString("X") + " " + CodeName(rr) + (rr == 0 ? (" EPC=" + Hex(t.code, 12)) : "")); CloseDevice(hh); } return; } long h = Connect(com); if (h == 0) { Console.WriteLine("RESULT: could not connect to the Chafon reader"); return; } try { if (action == "read") { var tag = NewTag(); int r = GetTagUii(h, ref tag, 3000); Console.WriteLine("GetTagUii = 0x" + r.ToString("X")); if (r == 0) Console.WriteLine("EPC = " + Hex(tag.code, 12) + " len=" + tag.codeLen + " rssi=" + (tag.rssi / 10) + " ant=" + tag.antenna); else Console.WriteLine("(no tag in field / error)"); } else if (action == "write") { string epc = args.Length > 1 ? args[1] : "1234567890ABCDEF12345678"; byte[] data = FromHex(epc); // 12 bytes byte[] pwd = new byte[4]; // access pwd 00000000 int r = WriteTag(h, 0, pwd, 1, 2, (byte)(data.Length / 2), data); // memBank 1 = EPC/UII, wordPtr 2, 6 words Console.WriteLine("WriteTag(EPC=" + epc + ") = 0x" + r.ToString("X") + (r == 0 ? " (ok)" : " (FAIL)")); var tag = NewTag(); if (GetTagUii(h, ref tag, 2000) == 0) Console.WriteLine("read-back EPC = " + Hex(tag.code, 12)); else Console.WriteLine("read-back: no tag / error"); } } finally { CloseDevice(h); Console.WriteLine("closed."); } } catch (Exception ex) { Console.WriteLine("EXCEPTION: " + ex.GetType().Name + ": " + ex.Message); if (ex.InnerException != null) Console.WriteLine(" inner: " + ex.InnerException.Message); } } }