Skip to content

Instantly share code, notes, and snippets.

@ChrisMoney
Last active September 23, 2023 19:29
Show Gist options
  • Save ChrisMoney/56449136882104f917af9c8b8f9d6a8c to your computer and use it in GitHub Desktop.
Save ChrisMoney/56449136882104f917af9c8b8f9d6a8c to your computer and use it in GitHub Desktop.
C# Send low level hexadecimal, binary command to K720 debit card dispenser listening on a serial port.
#define USE_DEBUG
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.IO.Ports;
using Microsoft.Win32.SafeHandles;
using System.Reflection;
using System.Runtime.InteropServices;
using SCL011Reader;
using System.Management;
using Microsoft.Win32;
using System.Security.Permissions;
namespace Dispenser
{
static class Program
{
static void Main(string[] args)
{
}
}
public class K720Driver : IDispenser
{
public static readonly K720Driver StaticK720Driver = new K720Driver();
private string _dispenserPort;
private string _readerPort;
private SafeFileHandle _dispenserHandle;
private SafeFileHandle _readerHandle;
//private IntPtr DispenserHandle { get { return GetHandle(_dispenserPort, ref _dispenserHandle); } }
private IntPtr DispenserHandle { get { return _dispenserHandle.DangerousGetHandle(); } }
//private IntPtr ReaderHandle { get { return GetHandle(_readerPort, ref _readerHandle); } }
private IntPtr ReaderHandle { get { return _readerHandle.DangerousGetHandle(); } }
private const byte MacID = 1;
private readonly RFIDParser _rfidParser;
private readonly byte[] _recordInfo;
private readonly CardReader _reader;
public DState Flags { get; private set; }
private bool Has(DState flag) { return (Flags & flag) == flag; }
public bool IsOpen { get; private set; }
public bool Open(string dispenserPort, string readerPort)
{
if (IsOpen)
{
Close();
IsOpen = false;
return IsOpen;
}
_dispenserPort = dispenserPort;
var h = D1000_CommOpen(dispenserPort);
_dispenserHandle = new SafeFileHandle(h, true);
_readerPort = readerPort;
h = RF610_CommOpen(readerPort);
_readerHandle = new SafeFileHandle(h, true);
// check dispenser and rfid connections
if (_dispenserHandle.IsInvalid || _readerHandle.IsInvalid)
{
IsOpen = false;
}
else
{
IsOpen = true;
}
return IsOpen;
}
public void Close()
{
if(_dispenserHandle!=null)
_dispenserHandle.Close();
if(_readerHandle !=null)
_readerHandle.Close();
IsOpen = false;
}
private K720Driver()
{
_recordInfo = new byte[256];
_rfidParser = RFIDParser.Intercard_Default;
_reader = new CardReader(this);
}
//public DState CaptureCard()
//{
// Wait();
// D1000_SendCmd(DispenserHandle, MacID, "CP", 2);
// GetState();
// if (!Has(DState.CannotExecute))
// Wait();
// return Flags;
//}
public DState DispenseCard()
{
Wait();
D1000_SendCmd(DispenserHandle, MacID, "FC0", 3);
GetState();
if (!Has(DState.CannotExecute))
{
Wait();
D1000_SendCmd(DispenserHandle, MacID, "FC7", 3);
}
return Flags;
}
public DState Reset()
{
D1000_SendCmd(DispenserHandle, MacID, "RS", 2);
return GetState();
}
public StringBuilder GetDispenserVersion()
{
StringBuilder version = new StringBuilder(4096);
D1000_GetSysVersion(DispenserHandle, MacID, version);
return version;
}
public DState ReadyCard()
{
Wait();
if (Has(DState.Pos1_2)) //already in position
return Flags;
//If card is is behind the read position, move forward
if (!Has(DState.Pos1) && (Has(DState.Pos2) || Has(DState.Pos3)))
{
D1000_SendCmd(DispenserHandle, MacID, "FC7", 3); //Move to read position
Wait();
}
if (Has(DState.Pos1)) //Card is too far forward, need to dispense, I think...
{
bool stackEmpty = Has(DState.StackEmpty);
D1000_SendCmd(DispenserHandle, MacID, "FC0", 3); //Dispense
Wait();
if (!stackEmpty)
{
//Move the next card into read position so we don't have to wait to read.
D1000_SendCmd(DispenserHandle, MacID, "FC7", 3);
Wait();
}
}
return Flags;
}
public string ScanCard(out long corpID, out long acctNo)
{
corpID = 0;
acctNo = 0;
string status = GetEnumDescription(RF610_ULDetectCard(ReaderHandle, _recordInfo));
if (status != GetEnumDescription(ReaderCode.Ok))
return status;
if (!_rfidParser.Parse(_reader))
return GetEnumDescription(ReaderCode.ReadDataError);
corpID = _rfidParser.Corp ?? 0;
acctNo = _rfidParser.Acct ?? 0;
return GetEnumDescription(ReaderCode.Ok);
}
public ReaderCode DetectCard()
{
return RF610_ULDetectCard(ReaderHandle, _recordInfo);
}
public void SendCommand(string cmd)
{
D1000_SendCmd(DispenserHandle, MacID, cmd, cmd.Length);
}
public int Check()
{
string cmd = Command.Check;
return D1000_SendCmd(DispenserHandle, MacID, cmd, cmd.Length);
}
public DState GetState()
{
StringBuilder strState = new StringBuilder(3);
D1000_SensorQuery(DispenserHandle, MacID, strState);
Flags = (DState)Int32.Parse(strState.ToString(), System.Globalization.NumberStyles.HexNumber);
return Flags;
}
// Interface method
public void PositionCardToRead()
{
SendCommand(Command.CaptureCard);
}
// Interface Method
public void Read()
{
ReadyCard();
}
// Interface method
public void Dispense()
{
SendCommand(Command.DispenseSensor2);
}
public void DispenseReadCard()
{
SendCommand(Command.DispenseReadCard);
}
public void DispenseHoldCard()
{
SendCommand(Command.DispenseReadCard);
}
public void DispenseMouth()
{
SendCommand(Command.DispenseMouth);
}
private void Wait()
{
DState curState = Flags;
do
{
DState newState = GetState();
if (curState != newState)
Log("StateChange: {0:g} -> {1:g}", curState, newState);
curState = newState;
}
while ((Flags & (DState.CannotExecute | DState.PreparingCard | DState.DispensingCard | DState.CapturingCard)) != 0);
}
public void WaitDetectUL()
{
while (RF610_ULDetectCard(ReaderHandle, _recordInfo) == ReaderCode.SearchCardFailed) { }
Log("Card Detected");
}
private void Log(string format, params object[] arg)
{
Console.WriteLine(format, arg);
System.Diagnostics.Debug.WriteLine(format, arg);
}
public static Dictionary<string, string> ActivePorts
{
get
{
string[] portNames = SerialPort.GetPortNames();
Dictionary<string, string> ports = portNames.ToDictionary(i => i, i => i);
var searcher = new ManagementObjectSearcher(
new ManagementScope(
new ManagementPath(string.Format(@"\\{0}\root\CIMV2", Environment.MachineName)),
new ConnectionOptions
{
Impersonation = ImpersonationLevel.Impersonate,
Authentication = AuthenticationLevel.Default,
EnablePrivileges = true
}),
new ObjectQuery(@"Select * from Win32_PnPEntity where ClassGuid = ""{4d36e978-e325-11ce-bfc1-08002be10318}"" and ConfigManagerErrorCode = 0"));
using (searcher)
{
foreach (ManagementObject obj in searcher.Get())
{
if (obj["Caption"] != null && obj["DeviceID"] != null)
{
string friendlyName = (string)obj["Caption"];
string path = string.Format(@"SYSTEM\CurrentControlSet\Enum\{0}\Device Parameters", obj["DeviceID"]);
string port = null;
RegistryKey baseKey = null;
RegistryKey serialKey = null;
new RegistryPermission(RegistryPermissionAccess.Read, @"HKEY_LOCAL_MACHINE\" + path).Assert();
try
{
baseKey = Registry.LocalMachine;
serialKey = baseKey.OpenSubKey(path, false);
if (serialKey != null)
{
port = (string)serialKey.GetValue("PortName");
if (port != null && ports.ContainsKey(port))
ports[port] = friendlyName;
}
}
finally
{
if (baseKey != null)
baseKey.Close();
if (serialKey != null)
serialKey.Close();
RegistryPermission.RevertAssert();
}
}
}
}
return ports;
}
}
public class CardReader : ICardReader
{
private K720Driver _dispenser;
private byte[] _keyValue;
public CardReader(K720Driver dispenser)
{
_dispenser = dispenser;
}
public string CardName { get { return "Mifare Ultra Light"; } }
public string ERROR { get { return ""; } }
public string Standard { get { return "ISO 14443 A, part 3"; } }
public int SW { get { return 0; } }
public byte[] UID
{
get
{
var uid = new byte[7];
return RF610_ULGetCardID(_dispenser.ReaderHandle, uid, _dispenser._recordInfo) == ReaderCode.Ok ? uid : null;
}
}
public bool Authenticate(int address, int keyType, int keyNumber)
{
if (_keyValue == null)
return false;
byte keyChar = (byte)(keyType==0x60? '0' : '1');
ReaderCode status = RF610_S50LoadSecKey(_dispenser.ReaderHandle, (byte)address, keyChar, _keyValue, _dispenser._recordInfo);
return status == ReaderCode.Ok;
}
public string GetVersion()
{
StringBuilder ver = new StringBuilder(4096);
RF610_GetSysVersion(_dispenser.ReaderHandle, ver);
return ver.ToString();
}
public bool LoadKey(int keyStructure, int keyNumber, byte[] keyValue)
{
_keyValue = keyValue;
return true;
}
public byte[] Read(int blockNumber, int length)
{
byte[] block = new byte[16];
RF610_ULReadBlock(_dispenser.ReaderHandle, (byte)blockNumber, block, _dispenser._recordInfo);
if (block.Length != length)
Array.Resize(ref block, length);
return block;
}
public bool Write(int blockNumber, byte[] data)
{
return RF610_ULWriteBlock(_dispenser.ReaderHandle, (byte)blockNumber, data, _dispenser._recordInfo) == ReaderCode.Ok;
}
}
// Get enum full string description
public static string GetEnumDescription(Enum enumValue)
{
string enumValueAsString = enumValue.ToString();
var type = enumValue.GetType();
FieldInfo fieldInfo = type.GetField(enumValueAsString);
object[] attributes = fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Length > 0)
{
var attribute = (DescriptionAttribute)attributes[0];
return attribute.Description;
}
return enumValueAsString;
}
private const int VERSION_MAX_LEN = 32;
//[DllImport("RF610_DLL.dll")]
//private static extern void* RF610_CommOpen(byte* Port);
[DllImport("RF610_DLL.dll", CharSet = CharSet.Ansi)]
private static extern IntPtr RF610_CommOpen(string port);
//[DllImport("RF610_DLL.dll")]
//private static extern void* RF610_CommOpenWithBaud(byte* Port, uint _data);
//[DllImport("RF610_DLL.dll")]
//private static extern int RF610_CommClose(void* ComHandle);
[DllImport("RF610_DLL.dll", CharSet = CharSet.Ansi)]
private static extern ReaderCode RF610_GetSysVersion(IntPtr handle, [MarshalAs(UnmanagedType.LPStr, SizeConst = VERSION_MAX_LEN)] [Out] StringBuilder version);
[DllImport("RF610_DLL.dll")]
private static extern ReaderCode RF610_ULDetectCard(IntPtr handle, byte[] recordInfo);
[DllImport("RF610_DLL.dll")]
private static extern ReaderCode RF610_ULGetCardID(IntPtr handle, byte[] uid, byte[] recordInfo);
[DllImport("RF610_DLL.dll")]
private static extern ReaderCode RF610_ULReadBlock(IntPtr handle, byte SectorAddr, byte[] blockData, byte[] recordInfo);
[DllImport("RF610_DLL.dll")]
private static extern ReaderCode RF610_ULWriteBlock(IntPtr handle, byte SectorAddr, byte[] blockData, byte[] recordInfo);
[DllImport("RF610_DLL.dll")]
private static extern ReaderCode RF610_ULHalt(IntPtr handle, byte[] recordInfo);
//[DllImport("RF610_DLL.dll")]
//private static extern int RF610_SetCommBaud(IntPtr handle, uint _Baud, byte[] recordInfo);
[DllImport("RF610_DLL.dll", CharSet = CharSet.Ansi)]
private static extern ReaderCode RF610_Reset(IntPtr handle, [MarshalAs(UnmanagedType.LPStr, SizeConst = VERSION_MAX_LEN)] out string version, byte[] recordInfo);
[DllImport("RF610_DLL.dll")]
private static extern ReaderCode RF610_S50LoadSecKey(IntPtr handle, byte sectorAddr, byte keyType, byte[] key, byte[] recordInfo);
[DllImport("RF610_DLL.dll")]
private static extern ReaderCode RF610_S50ReadBlock(IntPtr handle, byte sectorAddr, byte blockAddr, byte[] blockData, byte[] recordInfo);
[DllImport("RF610_DLL.dll")]
private static extern ReaderCode RF610_S50WriteBlock(IntPtr handle, byte sectorAddr, byte blockAddr, byte[] blockData, byte[] recordInfo);
[DllImport("RF610_DLL.dll")]
private static extern ReaderCode RF610_S50InitValue(IntPtr handle, byte sectorAddr, byte blockAddr, byte[] blockData, byte[] recordInfo);
[DllImport("RF610_DLL.dll")]
private static extern ReaderCode RF610_S50Increment(IntPtr handle, byte sectorAddr, byte blockAddr, ref int value, byte[] recordInfo);
[DllImport("RF610_DLL.dll")]
private static extern ReaderCode RF610_S50Decrement(IntPtr handle, byte sectorAddr, byte blockAddr, ref int value, byte[] recordInfo);
[DllImport("RF610_DLL.dll")]
private static extern ReaderCode RF610_S50Halt(IntPtr handle, byte[] recordInfo);
// K720 Unmanaged Methods
[DllImport("D1000_DLL.dll", CharSet = CharSet.Ansi)]
private static extern IntPtr D1000_CommOpen(string port);
//[DllImport("D1000_DLL.dll")]
//private static extern void* D1000_CommOpen(byte* Port);
//[DllImport("D1000_DLL.dll")]
//private static extern void* D1000_CommOpenWithBaud(byte* Port, uint _data);
//[DllImport("D1000_DLL.dll")]
//private static extern IntPtr D1000_CommClose(string port);
[DllImport("D1000_DLL.dll")]
private static extern int D1000_GetDllVersion(IntPtr handle, [MarshalAs(UnmanagedType.LPStr, SizeConst = VERSION_MAX_LEN)] out string version);
[DllImport("D1000_DLL.dll", CharSet = CharSet.Ansi)]
private static extern int D1000_GetSysVersion(IntPtr handle, byte MacAddr, [Out] StringBuilder version);
[DllImport("D1000_DLL.dll", CharSet = CharSet.Ansi)]
private static extern int D1000_SendCmd(IntPtr handle, byte MacAddr, byte[] cmd, int cmdLen);
[DllImport("D1000_DLL.dll", CharSet = CharSet.Ansi)]
private static extern int D1000_SendCmd(IntPtr handle, byte MacAddr, string cmd, int cmdLen);
[DllImport("D1000_DLL.dll")]
private static extern int D1000_Query(IntPtr handle, byte MacAddr, byte[] stateInfo);
[DllImport("D1000_DLL.dll", EntryPoint="D1000_SensorQuery", CharSet = CharSet.Ansi)]
private static extern int D1000_SensorQuery(IntPtr handle, byte MacAddr, [Out] StringBuilder stateInfo);
}
[Flags]
public enum DState
{
[Description("Position 1")]
Pos1 = 0x0001,
[Description("Position 2")]
Pos2 = 0x0002,
[Description("Position 1 and 2")]
Pos1_2 = 0x0003,
[Description("Position 3")]
Pos3 = 0x0004,
[Description("Stack Empty")]
StackEmpty = 0x0008,
[Description("Stack Near Empty")]
StackNearEmpty = 0x0010,
[Description("Card Jammed")]
CardJammed = 0x0020,
[Description("Card Over Lapped")]
CardOverlapped = 0x0040,
[Description("No Card Captured")]
NoCapturedCard = 0x0080,
[Description("Card Capture Error")]
CaptureCardError = 0x0100,
[Description("Dispense Card Error")]
DispenseCardError = 0x0200,
[Description("Capturing Card")]
CapturingCard = 0x0400,
[Description("Dispensing Card")]
DispensingCard = 0x0800,
[Description("Preparing Card")]
PreparingCard = 0x1000,
[Description("Preparing Card Fail")]
PreparingCardFail = 0x2000,
[Description("Cannot Execute")]
CannotExecute = 0x4000,
[Description("Keep")]
Keep = 0x8000
}
public enum ReaderCode
{
[Description("Read successfully")]
Ok = 0,
[Description("Command parameter error")]
CommandParameterError = 0x01,
[Description("Command data error")]
CommandDataError = 0x02,
[Description("Cannot execute")]
CannotExecute = 0x03,
[Description("Execution failed")]
ExecuteFailed = 0x04,
[Description("Card search failed")]
SearchCardFailed = 0x41,
[Description("Failed to read serial number")]
ReadSerialNumberFailed = 0x42,
[Description("Verify PW Error")]
VerifyPWError = 0x43,
[Description("Card select error")]
SelectCardError = 0x44,
[Description("Data read error")]
ReadDataError = 0x45,
[Description("Data write error")]
WriteDataError = 0x46,
[Description("Increment failed")]
IncrementFailed = 0x47,
[Description("Devalue failed")]
DevalueFailed = 0x48,
[Description("Bad command read")]
BadCommRead = -103,
[Description("Bad command write")]
BadCommWrite = -104,
[Description("ACK timeout")]
AckTimeout = -105,
[Description("EOT timeout")]
EotTimeout = -106,
[Description("Packet timeout")]
PacketTimeout = -107,
[Description("Wrong packet head")]
WrongPacketHead = -108,
[Description("Wrong packet length")]
WrongPacketLen = -109,
[Description("Wrong BCC")]
WrongBCC = -110,
[Description("Bad parameter")]
BadParameter = -201,
[Description("Bad com port close")]
BadCommClose = -202
}
[StructLayout(LayoutKind.Sequential)]
struct SensorQueryState
{
[MarshalAs(UnmanagedType.LPStr, SizeConst = 4)]
private readonly string _byteChars;
public static implicit operator DState(SensorQueryState state)
{
return (DState)Int32.Parse(state._byteChars, System.Globalization.NumberStyles.HexNumber);
}
public override string ToString()
{
return ((DState)this).ToString("g");
}
}
public struct Command
{
public static string CaptureCard = "CP";
public static string Check = "AP";
public static string IssueCard = "DC";
public static string CollectCard = "CP";
public static string Reposition = "RS";
public static string DispenseSensor2 = "FC6";
public static string DispenseReadCard = "FC7";
public static string DispenseHoldCard = "FC4";
public static string DispenseMouth = "FC0";
public static string AllowBuzz = "BE";
public static string StopBuzz = "BD";
public static string BaudRate1200 = "CS0";
public static string BaudRate2400 = "CS1";
public static string BaudRate4800 = "CS2";
public static string BaudRate9600 = "CS3";
}
}
@rrrebusa
Copy link

RF610_DLL do you have these dll

@anaryk
Copy link

anaryk commented Sep 23, 2023

Hi @ChrisMoney , I have just purchased a K720 card dispenser and I am trying to get it to work using your snippet, but unfortunately I can't find the DLLs mentioned anywhere. Would it be possible to supply these DLLs. Also some imports such as
using SCL011Reader;
using System.Management;
are marking me as not valid. I am using the latest version of Visual Studio. Thank you very much in advance.

@ChrisMoney
Copy link
Author

ChrisMoney commented Sep 23, 2023 via email

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