BlackBox - A control unit for Haunted Houses
Minimize 

BlackBox

This control unit is a result of several years expirience in building electronic devices for Haunted Houses and amusement parks. The idea behind this device was to have a very small and robust control unit, that has 3 power outlets and a hi-quality stereo sound output. The sound should be easily uploaded into the device using a Computer. I decided to use a PIC 18F2550 from Microchip because it has an onboard USB port and enough IO pins to control everything. After a few weeks of planning and development I had the prototype ready and started programming the PIC Firmware and the PC Software. Unfortunately there a re only C++ samplecodes out there - but I'm a C# programmer and want to programm the Windows Application using .Net and C#. I took the sample sources from Microchip and ported it to C#. As a result now there is a base for creating HID devices using PIC Microcontrollers and the Microsoft .Net languages on the Windows side. It's easy now to talk directly to HID devices. No driver installation for the Hardware and no third-party library or C++ Software is required. This is a huge step foreward.

BlackBox front view

The front view of the BlackBox. You can see the test button, input connector, volume control, duration control, frequency control, switch, 3 led's, audio stereo out, usb connector. A complete device is available for 400€ + V.A.T. per unit. In Haunted Houses the device is usually mounted to the puppets and the light is placed on the opposit side. This is the reason for the standard power outlet connector - the light can easily disconnected and taken away.

BlackBox rear view

On the rear side you can see the power line, two industrial connectors and their fuseholders. On the top sits the 3rd power outlet with a standard(german) power connection and the fuse. You can independently control 3 power outlets with up to 230V~/2.5A per channel.  

BlackBox 3D

A 3D view of the PCB design created using Target V15 Smart.

BlackBox Control Software

A screenshot of the control software - written entirely in C#. It talks directly to the usb device using P/Invoke and Interop. A soon as the hardware is connected, it reads the status and you can upload an audio file to the device. 

I costs me some nights to get the Interop stuff and the PIC-USB Firmware working correctly.
I hope that my results may help other peoples - so I will publish the result as Open Source here for download.
Get the sample code below and feel free to use it for your projects.  

  
C# Sourcecode for USB - HID Device
Minimize 

 

/********************************************************************
 FileName:        USBLib.cs
 Date:              11/8/2009
 Author:           Helmut Obertanner (flash [at] x4u [dot] de)
 Company:       X4U electronix [http://www.x4u.de]
 
 Software License Agreement:
 
 The software supplied herewith by X4U
 (the “Author”) is free for personal use only. Distribution of sources
 is not allowed withut permisson of the Author. The software is owned
 by the Author, and is protected under applicable copyright laws.
 All rights are reserved.
 Any use in violation of the foregoing restrictions may subject the
 user to criminal sanctions under applicable laws, as well as to
 civil liability for the breach of the terms and conditions of this
 license.
 
 THIS SOFTWARE IS PROVIDED IN AN “AS IS” CONDITION. NO WARRANTIES,
 WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED
 TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE AUTHOR SHALL NOT,
 IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
 CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 
********************************************************************/
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32.SafeHandles;
using System.Diagnostics;
 
namespace X4U.BlackBox {
 
    ///<summary>
    /// The BoxState Class is used to parse the result returned from
    /// USB device and provides a human readable form of the data
    ///</summary>
    internal class BoxState {
 
        public int State { get; private set; }
        public int Volume { get; private set; }
        public int VolumePercent { get; private set; }
        public int Duration { get; private set; }
        public int DurationPercent { get; private set; }
        public int Frequency { get; private set; }
        public int FrequencyPercent { get; private set; }
        public bool Inputdetected { get; private set; }
        public int Mode { get; private set; }
        public bool Led1Enabled { get; private set; }
        public bool Led2Enabled { get; private set; }
        public bool Led3Enabled { get; private set; }
 
 
        ///<summary>
        /// .ctor Parses the buffer returned fomr USB and calculate the values for the
        /// controls.
        ///</summary>
        ///<param name="buffer"></param>
        public BoxState(byte[] buffer) {
 
            switch (buffer[0]) {
                case USBLib.BOX_GET_STATE:
                    State = (int)buffer[1];
 
                    Volume = (int)(buffer[3] << 8) + buffer[2];
                    VolumePercent = (int)(LogToLin(Volume, 1023d) / 10.23d); //(Max AD Value is 1023)
 
                    Duration = (int)(buffer[5] << 8) + buffer[4];
                    DurationPercent = (int)(LogToLin(Duration, 1023d) / 10.23d);
 
                    Frequency = (int)(buffer[7] << 8) + buffer[6];
                    FrequencyPercent = (int)(LogToLin(Frequency, 1023d) / 10.23d);
 
                    Inputdetected = (buffer[8] == 1);
                    Mode = buffer[9];
 
                    Led1Enabled = (buffer[10] == 1);
                    Led2Enabled = (buffer[11] == 1);
                    Led3Enabled = (buffer[12] == 1);
 
                    //... and so on...
 
                    break;
            }
        }
 
        ///<summary>
        /// Required Conversion from Logarithmic Potentiometers to Linear scale
        ///</summary>
        ///<param name="value">The current value</param>
        ///<param name="maxValue">The maximum value</param>
        ///<returns>Returns the corrected value for the controls (percent)</returns>
        private static double LogToLin(double value, double maxValue){
            return (Math.Sqrt(value) / Math.Sqrt(maxValue)) * maxValue;
        }
    }
 
 
    ///<summary>
    /// Class is used to communicate with the USB-HID Device
    ///</summary>
    ///<example>
    /// // define a class wide variable:
    /// USBLib _usb = new USBLib("Vid_04d8&Pid_XXXX&rev_XXXX");
    ///
    ///
    /// // In a Timer.Tick event or a Backgroundthread do the following:
    /// private void timer1_Tick(object sender, EventArgs e) {
    ///     if (_usb.IsDeviceConnected()) {
    ///         SetConnectedState(true);                     // Led
    ///         BoxState currentState = _usb.GetState();     // Reads the Data from USB
    ///         SetVolume(currentState.VolumePercent);       // Knob
    ///         SetDuration(currentState.DurationPercent);   // Knob
    ///         SetFrequency(currentState.FrequencyPercent); // Knob
    ///         SetImpulseState(currentState.Inputdetected); // Led
    ///         SetSwitchState(currentState.Mode == 1);      // Led
    ///         SetLed1State(currentState.Led1Enabled);      // Led
    ///         SetLed2State(currentState.Led2Enabled);      // Led
    ///         SetLed3State(currentState.Led3Enabled);      // Led
    ///     } else {
    ///         SetConnectedState(false);                    // Led
    ///     }
    /// }
    /// 
    /// // Issues a direct command
    /// private void button1_Click(object sender, EventArgs e) {
    ///     if (_usb.IsDeviceConnected()) {
    ///         _usb.ClearMemory();
    ///     }
    /// }
    ///</example>
    internal class USBLib {
 
        ///<summary>
        /// .ctor takes the device ID as parameter
        ///</summary>
        ///<param name="deviceId">Your deviceID: "Vid_04d8&Pid_XXXX&rev_XXXX"</param>
        public USBLib(string deviceId) {
            _deviceId = deviceId.ToLowerInvariant();
        }
 
        ///<summary>
        /// The Control Commands for the device
        ///</summary>
 
        public const byte BOX_GET_STATE = 0x80;
        public const byte BOX_GET_MEMORY_STATE = 0x81;
        public const byte BOX_CLEAR_MEMORY = 0x82;
 
        public const byte BOX_GET_VOLUME = 0x83;
        public const byte BOX_SET_VOLUME = 0x84;
 
        public const byte BOX_GET_OUT1_STATE = 0x85;
        public const byte BOX_GET_OUT2_STATE = 0x86;
        public const byte BOX_GET_OUT3_STATE = 0x87;
 
        public const byte BOX_SET_OUT1_STATE = 0x88;
        public const byte BOX_SET_OUT2_STATE = 0x89;
        public const byte BOX_SET_OUT3_STATE = 0x90;
 
        public const byte BOX_GET_LED1_STATE = 0x91;
        public const byte BOX_GET_LED2_STATE = 0x92;
        public const byte BOX_GET_LED3_STATE = 0x93;
 
        public const byte BOX_SET_LED1_STATE = 0x94;
        public const byte BOX_SET_LED2_STATE = 0x95;
        public const byte BOX_SET_LED3_STATE = 0x96;
 
        #region API Stuff
 
        const int DIGCF_PRESENT = 0x00000002;
        const int DIGCF_DEVICEINTERFACE = 0x00000010;
        const int DIGCF_INTERFACEDEVICE = 0x00000010;
        const uint GENERIC_READ = 0x80000000;
        const uint GENERIC_WRITE = 0x40000000;
        const uint FILE_SHARE_READ = 0x00000001;
        const uint FILE_SHARE_WRITE = 0x00000002;
        const uint OPEN_EXISTING = 3;
        const int ERROR_NO_MORE_ITEMS = 259;
 
 
        enum SPDRP {
            SPDRP_DEVICEDESC = 0x00000000,
            SPDRP_HARDWAREID = 0x00000001,
            SPDRP_COMPATIBLEIDS = 0x00000002,
            SPDRP_NTDEVICEPATHS = 0x00000003,
            SPDRP_SERVICE = 0x00000004,
            SPDRP_CONFIGURATION = 0x00000005,
            SPDRP_CONFIGURATIONVECTOR = 0x00000006,
            SPDRP_CLASS = 0x00000007,
            SPDRP_CLASSGUID = 0x00000008,
            SPDRP_DRIVER = 0x00000009,
            SPDRP_CONFIGFLAGS = 0x0000000A,
            SPDRP_MFG = 0x0000000B,
            SPDRP_FRIENDLYNAME = 0x0000000C,
            SPDRP_LOCATION_INFORMATION = 0x0000000D,
            SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = 0x0000000E,
            SPDRP_CAPABILITIES = 0x0000000F,
            SPDRP_UI_NUMBER = 0x00000010,
            SPDRP_UPPERFILTERS = 0x00000011,
            SPDRP_LOWERFILTERS = 0x00000012,
            SPDRP_MAXIMUM_PROPERTY = 0x00000013,
        }
 
        [StructLayout(LayoutKind.Sequential)]
        struct SP_DEVINFO_DATA {
            public Int32 cbSize;
            public Guid ClassGuid;
            public uint DevInst;
            public IntPtr Reserved;
        }
 
        [StructLayout(LayoutKind.Sequential)]
        struct SP_DEVICE_INTERFACE_DATA {
            public Int32 cbSize;
            public Guid interfaceClassGuid;
            public Int32 flags;
            private UIntPtr reserved;
        }
 
        // Device interface detail data
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public struct SP_DEVICE_INTERFACE_DETAIL_DATA {
            public Int32 cbSize;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
            public string DevicePath;
        }
 
 
 
        //Returns a HDEVINFO type for a device information set (USB HID devices in
        //our case). We will need the HDEVINFO as in input parameter for calling many of
        //the other SetupDixxx() functions.
        [DllImport("setupapi.dll", CharSet = CharSet.Ansi, EntryPoint = "SetupDiGetClassDevs", SetLastError = true)]
        static extern IntPtr SetupDiGetClassDevs(
            ref Guid ClassGuid,                            //Input: Supply the class GUID here.
            [MarshalAs(UnmanagedType.LPTStr)] string Enumerator,                        //Input: Use NULL here, not important for our purposes
            IntPtr hwndParent,                             //Input: Use NULL here, not important for our purposes
            int Flags);                              //Input: Flags describing what kind of filtering to use.
 
        //Gives us "PSP_DEVICE_INTERFACE_DATA" which contains the Interface specific GUID (different
        //from class GUID). We need the interface GUID to get the device path.
        [DllImport("setupapi.dll", CharSet = CharSet.Ansi, EntryPoint = "SetupDiEnumDeviceInterfaces", SetLastError = true)]
        static extern bool SetupDiEnumDeviceInterfaces(
            IntPtr DeviceInfoSet,                  //Input: Give it the HDEVINFO we got from SetupDiGetClassDevs()
            IntPtr DeviceInfoData,             //Input (optional)
            ref Guid InterfaceClassGuid,       //Input
            int MemberIndex,                         //Input: "Index" of the device you are interested in getting the path for.
            ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData);        //Output: This function fills in an "SP_DEVICE_INTERFACE_DATA" structure.
 
        //SetupDiDestroyDeviceInfoList() frees up memory by destroying a DeviceInfoList
        [DllImport("setupapi.dll", CharSet = CharSet.Ansi, EntryPoint = "SetupDiDestroyDeviceInfoList", SetLastError = true)]
        static extern bool SetupDiDestroyDeviceInfoList(
            IntPtr DeviceInfoSet);             //Input: Give it a handle to a device info list to deallocate from RAM.
 
        //SetupDiEnumDeviceInfo() fills in an "SP_DEVINFO_DATA" structure, which we need for SetupDiGetDeviceRegistryProperty()
        [DllImport("setupapi.dll", CharSet = CharSet.Ansi, EntryPoint = "SetupDiEnumDeviceInfo", SetLastError = true)]
        static extern bool SetupDiEnumDeviceInfo(
            IntPtr DeviceInfoSet,
            int MemberIndex,
            ref SP_DEVINFO_DATA DeviceInfoData);
 
        //SetupDiGetDeviceRegistryProperty() gives us the hardware ID, which we use to check to see if it has matching VID/PID
        [DllImport("setupapi.dll", CharSet = CharSet.Ansi, EntryPoint = "SetupDiGetDeviceRegistryProperty", SetLastError = true)]
        static extern bool SetupDiGetDeviceRegistryProperty(
            IntPtr DeviceInfoSet,
            ref SP_DEVINFO_DATA DeviceInfoData,
            int Property,
            out int PropertyRegDataType,
            IntPtr PropertyBuffer,
            int PropertyBufferSize,
            out int RequiredSize);
 
        //SetupDiGetDeviceInterfaceDetail() gives us a device path, which is needed before CreateFile() can be used.
        [DllImport("setupapi.dll", CharSet = CharSet.Ansi, EntryPoint = "SetupDiGetDeviceInterfaceDetail", SetLastError = true)]
        static extern bool SetupDiGetDeviceInterfaceDetail(
            IntPtr DeviceInfoSet,                                                           //Input: Wants HDEVINFO which can be obtained from SetupDiGetClassDevs()
            ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData,                  //Input: Pointer to an structure which defines the device interface. 
            ref SP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData, //Output: Pointer to a strucutre, which will contain the device path.
            int DeviceInterfaceDetailDataSize,                                      //Input: Number of bytes to retrieve.
            out int RequiredSize,                                                           //Output (optional): The number of bytes needed to hold the entire struct
            int DeviceInfoData);                                                       //Output
 
        //SetupDiGetDeviceInterfaceDetail() gives us a device path, which is needed before CreateFile() can be used.
        [DllImport("setupapi.dll", CharSet = CharSet.Ansi, EntryPoint = "SetupDiGetDeviceInterfaceDetail", SetLastError = true)]
        static extern unsafe bool SetupDiGetDeviceInterfaceDetail(
            IntPtr DeviceInfoSet,                                                           //Input: Wants HDEVINFO which can be obtained from SetupDiGetClassDevs()
            ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData,                  //Input: Pointer to an structure which defines the device interface. 
            int DeviceInterfaceDetailData,                                     //Output: Pointer to a strucutre, which will contain the device path.
            int DeviceInterfaceDetailDataSize,                                      //Input: Number of bytes to retrieve.
            out int RequiredSize,                                                           //Output (optional): The number of bytes needed to hold the entire struct
            int DeviceInfoData);                                                       //Output
 
 
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
        static extern SafeFileHandle CreateFile(
           string lpFileName,
           uint dwDesiredAccess,
           uint dwShareMode,
           int lpSecurityAttributes,
           uint dwCreationDisposition,
           uint dwFlagsAndAttributes,
           int hTemplateFile);
 
        #endregion
 
 
        object _lockObject = new Object();
 
        ///<summary>
        /// The Device-ID in form of "Vid_04d8&Pid_XXXX&rev_XXXX"
        ///</summary>
        string _deviceId;
 
        ///<summary>
        /// The idea is to check for the device, and when it's present remember the devicepath
        /// So I don't need to leave a handle open the whole time.
        /// Instead I use the CreateFile Method for each transmission.
        ///</summary>
        string _usbDevicePath;
 
        ///<summary>
        /// The Interface GUID
        ///</summary>
        Guid _interfaceClassGuid = new Guid(0x4d1e55b2, 0xf16f, 0x11cf, 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30);
 
        ///<summary>
        /// Checks if the Device is connected and gets the Device Path.
        ///</summary>
        ///<returns>True, when the device is connected to the PC</returns>
        public bool IsDeviceConnected() {
 
            bool isConnected = false;
            int errorStatus = 0;
            int interfaceIndex = 0;
            string enummerator = null;
 
            // First populate a list of plugged in devices (by specifying "DIGCF_PRESENT"), which are of the specified class GUID.
            IntPtr deviceInfoTable = SetupDiGetClassDevs(ref _interfaceClassGuid, enummerator, IntPtr.Zero, (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));
 
            // Now look through the list we just populated. We are trying to see if any of them match our device.
            while (true) {
 
                SP_DEVICE_INTERFACE_DATA interfaceData = new SP_DEVICE_INTERFACE_DATA();
                interfaceData.cbSize = Marshal.SizeOf(interfaceData);
 
                if (SetupDiEnumDeviceInterfaces(deviceInfoTable, IntPtr.Zero, ref _interfaceClassGuid, interfaceIndex, ref interfaceData)) {
                    errorStatus = Marshal.GetLastWin32Error();
                    if (errorStatus == ERROR_NO_MORE_ITEMS)      //Did we reach the end of the list of matching devices in the DeviceInfoTable?
                            { //Cound not find the device. Must not have been attached.
                        break;
                    }
                } else {     //Else some other kind of unknown error ocurred...
                    errorStatus = Marshal.GetLastWin32Error();
                    break;
                }
                // No Error
 
                //Now retrieve the hardware ID from the registry. The hardware ID contains the VID and PID, which we will then
                //check to see if it is the correct device or not.
 
                //Initialize an appropriate SP_DEVINFO_DATA structure. We need this structure for SetupDiGetDeviceRegistryProperty().
 
                SP_DEVINFO_DATA devInfo = new SP_DEVINFO_DATA();
                devInfo.cbSize = Marshal.SizeOf(devInfo);
 
                SetupDiEnumDeviceInfo(deviceInfoTable, interfaceIndex, ref devInfo);
 
                IntPtr propertyBuffer = IntPtr.Zero;
                int propertyRegDataType;
                int requiredSize;
 
                //First query for the size of the hardware ID, so we can know how big a buffer to allocate for the data.
                SetupDiGetDeviceRegistryProperty(deviceInfoTable, ref devInfo, (int)SPDRP.SPDRP_HARDWAREID, out propertyRegDataType, propertyBuffer, 0, out requiredSize);
 
                //Allocate a buffer for the hardware ID.
                propertyBuffer = Marshal.AllocHGlobal((int)requiredSize + 4);
 
                if (propertyBuffer == IntPtr.Zero) { //if null, error, couldn't allocate enough memory
                    //Can't really recover from this situation, just exit instead.
                    break;
                }
                int dummySize = 0;
 
                //Retrieve the hardware IDs for the current device we are looking at. PropertyValueBuffer gets filled with a
                //REG_MULTI_SZ (array of null terminated strings). To find a device, we only care about the very first string in the
                //buffer, which will be the "device ID". The device ID is a string which contains the VID and PID, in the example
                //format "Vid_04d8&Pid_003f".
                SetupDiGetDeviceRegistryProperty(deviceInfoTable, ref devInfo, (int)SPDRP.SPDRP_HARDWAREID, out propertyRegDataType, propertyBuffer, requiredSize, out dummySize);
 
                //Now check if the first string in the hardware ID matches the device ID of my USB device.
                String deviceIDFromRegistry = Marshal.PtrToStringAnsi(propertyBuffer).ToLowerInvariant();
                Marshal.FreeHGlobal(propertyBuffer);
 
                if (deviceIDFromRegistry.Contains(_deviceId)) {
                    //Device must have been found. Open read and write handles. In order to do this, we will need the actual device path first.
                    //We can get the path by calling SetupDiGetDeviceInterfaceDetail(), however, we have to call this function twice: The first
                    //time to get the size of the required structure/buffer to hold the detailed interface data, then a second time to actually
                    //get the structure (after we have allocated enough memory for the structure.)
                    requiredSize = 0;
                    dummySize = 0;
 
                    //First call populates "StructureSize" with the correct value
                    SetupDiGetDeviceInterfaceDetail(deviceInfoTable, ref interfaceData, 0, 0, out requiredSize, 0);
 
                    int size = Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DETAIL_DATA));
                    IntPtr ptr = Marshal.AllocHGlobal(requiredSize);
                    SP_DEVICE_INTERFACE_DETAIL_DATA detailData = (SP_DEVICE_INTERFACE_DETAIL_DATA)Marshal.PtrToStructure(ptr, typeof(SP_DEVICE_INTERFACE_DETAIL_DATA));
                    detailData.cbSize = requiredSize;
 
                    //Now call SetupDiGetDeviceInterfaceDetail() a second time to receive the goods. 
                    SetupDiGetDeviceInterfaceDetail(deviceInfoTable, ref interfaceData, ref detailData, requiredSize, out requiredSize, 0);
 
                    if (!string.IsNullOrEmpty(detailData.DevicePath)) {
                        string path = "\\\\?\\" + detailData.DevicePath;
                        if (path != _usbDevicePath) {
                            SafeFileHandle handle = CreateFile(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
                            if (!handle.IsInvalid) {
                                _usbDevicePath = path;
                                isConnected = true;
                            } else {
                                _usbDevicePath = string.Empty;
                            }
                            handle.Close();
                        } else {
                            isConnected = true;
                        }
                    }
                    Marshal.FreeHGlobal(ptr);
 
                    break;
                }
                interfaceIndex++;
            }
 
            // Cleanup
            SetupDiDestroyDeviceInfoList(deviceInfoTable);
            return isConnected;
        }
 
 
        //public void ToggleLeds() {
 
        //    byte[] buffer = new byte[65];
        //    buffer[0] = 0;
        //    buffer[1] = 0x80;
        //    SafeFileHandle hWrite = CreateFile(_usbDevicePath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
        //    FileStream writer = new FileStream(hWrite, FileAccess.Write, 65);
        //    writer.Write (buffer,0,buffer.Length);
        //    writer.Close();
 
        //}
 
        public void ClearMemory() {
            WriteCommand(BOX_CLEAR_MEMORY);
        }
 
        public void SetOutput(byte output, bool value) {
            WriteCommand((byte)(BOX_SET_OUT1_STATE + output), (byte)(value ? 1 : 0));
        }
 
        public BoxState GetState() {
            return new BoxState(ReadData(BOX_GET_STATE));
        }
 
        ///<summary>
        /// Sends just a command byte to the device.
        ///</summary>
        ///<param name="command">The command to send</param>
        private void WriteCommand(byte command) {
            WriteCommand(command, 0);
        }
 
        ///<summary>
        /// Sends a command byte to the device and a value
        ///</summary>
        ///<param name="command">The command to send</param>
        ///<param name="value">The value parameter for the command</param>
        private void WriteCommand(byte command, byte value) {
 
            lock (_lockObject) {
 
                // buffer is always data len +1
                byte[] buffer = new byte[65];
                buffer[0] = 0; // the first byte is parsed by the usb stack
 
                buffer[1] = command;
                buffer[2] = value;
                try {
                    SafeFileHandle hWrite = CreateFile(_usbDevicePath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
                    if (!hWrite.IsInvalid) {
                        FileStream writer = new FileStream(hWrite, FileAccess.Write, buffer.Length);
                        writer.Write(buffer, 0, buffer.Length);
                        writer.Close();
                    } else {
                        hWrite.Close();
                    }
                } catch (System.Exception ex) {
                    //TODO: need some Exception handling
                }
            }
        }
 
        ///<summary>
        /// Sends a command to the device and reads the values returnd from device
        ///</summary>
        ///<param name="command">The command to send</param>
        ///<returns>64 bytes of ddevice data</returns>
        private byte[] ReadData(byte command) {
 
            lock (_lockObject) {
 
                // buffer for receiving the data
                byte[] buffer = new byte[65];
                buffer[0] = 0;
 
                buffer[1] = command;
                try {
                    SafeFileHandle hWrite = CreateFile(_usbDevicePath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
                    if (hWrite.IsInvalid) {
                        hWrite.Close();
                    } else {
                        SafeFileHandle hRead = CreateFile(_usbDevicePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
                        FileStream writer = new FileStream(hWrite, FileAccess.Write, buffer.Length);
                       writer.Write(buffer, 0, buffer.Length);
 
                        FileStream reader = new FileStream(hRead, FileAccess.Read);
                        reader.ReadByte(); // remove the control byte
                        for (int index = 0; index < buffer.Length - 1; index++) {
                            buffer[index] = (byte)reader.ReadByte();
                        }
                        reader.Close();
                        writer.Close();
                    }
                } catch (System.Exception ex) {
                    //TODO: need some Exception handling
                }
 
                return buffer;          
            }
        }
    }
}