Arcaze-USB SDK (C#)

Introduction

The Arcaze USB-Interface can not only be used as a HID device emulator, but also as a freely programmable USB I/O-Interface. You can set read and write all inputs and outputs, set port directions, access extension modules and more.

There are multiple ways to control the Arcaze USB-Interface by software. The available protocol layers are shown here:

The Feature Report Protocol is the communication protocol on the USB physical layer. If it’s desired to use this low level, the description of this protocol can be found here.

However, there are more convenient ways with a higher level of abstraction and standardization, which are being made available by the Arcaze-USB SDK, currently in it’s implementation as a C# DLL.

Note that there is also a command line executable available, that allows you to remote control the Arcaze either manually from the shell or automated from batch files or frontend applications without any programming at all. This application however is not covered in this document. It is described here.

Versions

Currently the recent version is V5.5 of the DLL. This version is still a pure C# DLL. This is the version documented here!

Starting from V6.0 of the DLL all functionality is being moved from the C# DLL into a C/C++ DLL and the C# DLL is only a wrapper around it. Therefore, starting from V6.0 it is possible to use the DLL from other environments like C,C++ and Delphi as well. To migrate from V5.x to V6.x some simple changes are required in your application, documented here.

Prerequisites and Compatibility

All SDK code is currently contained in a single C# DLL. This enables simple access from C# applications. Native applications written in other languages are able to use the DLL starting from V6.0 (not documented here yet).

Currently at least .Net V3.5 and Windows XP is required for operation of the DLL. Vista and 7 is compatible as well.

It is a 32 Bit DLL. It is no problem to use it on 64 Bit systems like Windows 7 / 64Bit, but your implementing application has to be a 32 Bit application!

General Informations

Port numbers and other IDs

The naming scheme of ports, pins and connectors has been changed a bit for better consistency in V5.5: In all new functions this hierarchy is followed:

  • Arcaze Serial Number
  • (Extension) Module Number
  • Connector Number
  • Port Number

So, „port“ can mean „pin“ or „output“ or „digit“ now (depending on style of module), but it does not mean „connector“ anymore. That was an ambiguousity before.

All functions access only single Arcaze modules, which are being addressed by their serial number. Therefore for the functions the top level hierarchy is the module number, which does NOT refer to the Arcaze serial number, but to the number of the extension module connected to one distinct Arcaze module! Module number 0 is always the Arcaze module itself and therefore accesses the internal ports, the first extension module has module number 1.

Some modules like the Display Module can have their addresses set by a switch, others like the LED Driver Module just increment with each module added to the chain.

In older functions, only the connector numbers were being allowed for addressing, this is depricated now. In newer functions module number, connector number and port number can be specified and it is allowed to „wrap around“ (for example start with module 1, connector 0, port 18 and send 10 ports, which will wrap to module 1 connector 1 if this is a LED Driver2 ). This is more flexible and does not have disadvantages compared to the old addressing, therefore it’s recommended to use the new universal functions for all new applications.

An example numbering of the connectors for a system consisting of one Arcaze module and 2 LED Driver modules is shown here:

Connector NumberModule NumberConnector Name printed on PCBDescription
00ALeft 40 pin I/O header on Arcaze-USB „A“
10BRight 40 pin I/O header on Arcaze-USB named „B“
21CLeft 40 pin I/O header on LED-Driver2 extension module named „C“
31DRight 40 pin I/O header on LED-Driver2 extension module named „D“
41ETop 16 pin I/O header on LED-Driver2 extension module named „E“
52CLeft 40 pin I/O header on LED-Driver2 extension module named „C“
62DRight 40 pin I/O header on LED-Driver2 extension module named „D“
72ETop 16 pin I/O header on LED-Driver2 extension module named „E“

Port Directions

The port direction is a value that defines which pins of a port are inputs and which ones are outputs. Each bit of the I/O port is represented by a bit in the direction value: Bit 0 of the value defines direction of bit 0 (=pin 0) of the port, bit 1 defines pin 1 and so on.

The most important thing to know is that

  • 0 means Input
  • 1 means Output

Port Values

Before version 5.5 of the DLL only 0 and 1 were allowed as input / output values:

  • When writing to an output port, a „1“ written to the output register means that a logic „high“ level is being output (on the interface typically 3.3V), a „0“ written to the output register means that a logic „low“ levelis being output (typically 0V).
  • When reading from an input port, a „1“ means that there is a logic „high“ present on the pin, a read „0“ means that there is a logic „low“ (0V) present on the pin.
  • It’s also possible to read from an output. Note that you do not read what you might have written to the port earlier, but rather read the actual voltage on the port! So, if it’s short circuited for example, you will read a „0“ even if the output register contains a „1“.

This is still true in most cases, since most ports used are digital. But with V5.5 an output port can also have other values than 0 and 1 to support PWM outputs, analog outputs and stepper motors for example. Therefore in the new functions the data type of output values is UInt32 instead of bool. If a higher value is written to a port than the port supports, the DLL limits it to the maximum valid value.

Pitfall – Inverted Logic

It’s important to note that when it comes to the connected hardware like LEDs or buttons a „0“ does not necessarily mean „inactive“ (unpressed button, dark LED), and a „1“ does not necessarily mean „active“ (pressed button, lit LED). Quite often the opposite is the case due to inverted logic:

The Arcaze inputs A and B do normally have their default (recessive) state set by pull-up resistors, which define the incative state to a logic high (here 3.3V) and a connected button short-circuits this resistor down to ground (0V, logic low), which is the dominant state. Therefore a pressed button would be read as a „0“ and a released button would read as „1“! This is called inverted logic.

The same may apply for LEDs:

  • If a LED is connected with the anode to the output and the cathode to ground, you get standard logic: It’s lit at output = „high“ and off when output=“low“
  • If the LED is connected with the cathode to the output and the anode to supply voltage, you get inverted logic: It’s off at output = „high“ and on when output=“high“.

And last but not least be aware, that in the Arcaze Config Tool (Windows GUI) the „high“ and „low“ definitions are more loosely used, to make it easier for users that are not used to inverted logic. There a „1“ does indeed always mean active (pressed button, lit LED) and a „0“ means inactive (released button, unlit LED).

New Commands

The following commands have been added publicly in V5.5:

  • void CmdInitExtensionPort(ExtModuleType extModuleType, int numModules, int bitsPerPort, int globalBrightness )
  • int GetOutputCacheAddr(int moduleNum, int connectorNum, int portNum)
  • void WriteOutputPort(int portCacheAddr, UInt32 data, OutputOperators outputOperator, bool triggerTransfer)
  • void WriteOutputPort(int moduleNum, int connectorNum, int portNum, UInt32 data, UInt32 mask, OutputOperators outputOperator, bool triggerTransfer)
  • bool WriteOutputPorts(int moduleNum, int connectorNum, int portNum, List<UInt32>data, OutputOperators outputOperator, bool triggerTransfer)
  • void WriteOutputPortsBitMask(int moduleNum, int connectorNum, int portNum, UInt64 data, int numBits, OutputOperators outputOperator, bool triggerTransfer)
  • bool UpdateOutputPorts()
  • void CmdReadQuadratureEncodersAbsolute(ref Int16[] EncoderValues)
  • void CmdReadQuadratureEncodersRelative(ref Int16[] EncoderValues)
  • int CmdReadPortsWithHistory(ref byte InputData)

Function Reference

All user relevant functionality is contained in 2 classes inside the ArcazeHid.dll:

  • class ArcazeHid handles finding, connecting, event handling and base functionality for USB communication.
  • class ArcazeCommand contains all available commands for remote controlling the Arcaze USB Interface.

You’ll only need these two classes for any communication with the Arcaze, this document gives you a brief overview.

Class „ArcazeHid“

The following figure shows all members of the ArcazeHid class, you will only require a fraction of these:

The detailed reference documentation of these functions is not complete yet. Instead, please refer to the „How to create a New Project“ Tutorial which shows the usage of all relevant functions in a very simple way.

Class „ArcazeCommand“

The following figure shows all members of the ArcazeCommand class.

The instance that you’ll normally be using, is already instantiated inside ArcazeHid. Therefore you’ll generally be calling the functions

arcazeHid.Command.CmdFunction()

Command Descriptions

CmdInitExtensionPort

The Init command has to be sent once before using any of the new Output commands. Most importantly it tells the system which and how many extension modules are connected to the Arcaze module.

Using this command is mandatory to support any extension module. Otherwise the output commands will return with an error.

Syntax
void CmdInitExtensionPort(ExtModuleType extModuleType, int numModules, int bitsPerPort, int globalBrightness )
Parameters
ParameterTypeDescription
extModuleTypeExtModuleType Selects which type of extension module is connected to the Arcaze. Currently these calues exist: 0 = InternalIo 1 = DisplayDriver 2 = LedDriver2 3 = LedDriver3
 
numModulesint Selects the number of extension modules connected to the Arcaze.
bitsPerPortint Defines the „resolution“ of each port. Earlier this was always one bit (per port), because only digital ports existed, so the only values were 0 and 1. But now this can be any number between 1 and 32 bits. The Arcaze LED-Module 3 for example has PWM outputs with 10 Bit resolution to set the brightness of each output pin individually. This value sets the number of bits used per pin.
globalBrightnessint In most cases an extension module allows a global setting of brightness. Display Driver and both LED modules do all have this. The number of bits differs internally but is normalized to 8 bits always. Therefore: maximum brightness is always 0xFF minimum brightness is always 0x00.
GetOutputCacheAddr

All outputs are being cached now to reduce the number of USB accesses and avoid bus contention. The cache is an array of UInt32 values, one value for each port, all ports of all modules in the same array in the order of the module addresses. For example the internal Arcaze ports are in the first 40 values, if the extension modules are LED Driver 2 modules, then there is an additional 48 values for each connected LED Driver module.

But the user does not need to care about this addressing inside the cache. It is calculated with GetOutputCacheAddr() correctly depending on the extension module type having been set up by CmdInitExtensionPort() before.

Syntax
int GetOutputCacheAddr(int moduleNum, int connectorNum, int portNum)
Parameters
ParameterTypeDescription
moduleNumintnumber of the extension module
connectorNumintnumber of the connector of the extension module
portNumint number of the port (pin) of this connector (In case of the Display Module this is the digit)

These paramters can be set to 0 if not required. If you just need the address of the first address of the first connector of the first extension module, then the parameters would be moduleNum=1, connectorNum=0, portNum=0.

WriteOutputPort (Addressed by cache address)

This writes a new output value into the output cache. It is not being sent to the module to allow for efficient caching and sending all changes together in as little USB packets as possible.

This version of WriteOutputPort() uses the cache address for addressing a port. Normally the end user will prefer addressing by module number, connector number and port number (see next command).

Syntax
void WriteOutputPort(int portCacheAddr, UInt32 data, OutputOperators outputOperator, bool triggerTransfer)
Parameters
ParameterTypeDescription
portCacheAddrintCache address of the desired output port as described in the GetOutputCacheAddr() description
dataUInt32 Data to be written. On digital outputs that will just be 0 or 1. Values above the allowed range are correctly set to the maximum value.
outputOperatorOutputOperatorsThe following output operators are available: 0 = PlainWrite 1 = And 2 = Or 3 = Xor
triggerTransferboolIf this is true, then an UpdateOutputPorts() is being issued automatically afterwards. If you have more changes to set, do not use this, but complete your changes first and send and UpdateOutputPorts() yourself afterwards. That will lower the USB load.
WriteOutputPort (Addressed by Module Num, Connector Num and port Num)

This WriteOutputPort() has the same functionality like the other WriteOutputPort() command. The only difference is the addressing of the port to be written to: You can adress it by specifying number of the extension module, connector number and port number, instead of the cache address. All other functionality is identical.

Syntax
void WriteOutputPort(int moduleNum, int connectorNum, int portNum, UInt32 data, UInt32 mask, OutputOperators outputOperator, bool triggerTransfer)
Parameters
ParameterTypeDescription
moduleNumintnumber of the extension module to be addressed
connectorNumintnumber of the connector of the extension module
portNumintnumber of the port (pin) of this connector

(In case of the Display Module this is the digit)
dataUInt32 Data to be written. On digital outputs that will just be 0 or 1. Values above the allowed range are correctly set to the maximum value.
outputOperatorOutputOperatorsThe following output operators are available: 0 = PlainWrite 1 = And 2 = Or 3 = Xor
triggerTransferboolIf this is true, then an UpdateOutputPorts() is being issued automatically afterwards. If you have more changes to set, do not use this, but complete your changes first and send and UpdateOutputPorts() yourself afterwards. That will lower the USB load.
WriteOutputPorts (writes multiple port values at once)

This has the same functionality as the WriteOutputPort() functions, the only difference is that a List of output values an be specified instead of only a single value.

It is allowed to wrap over connector boundaries or module boundaries. If you specify more values than fitting on the specified module or connector, it just continues writing on the next module/connector. Therefore it is for example possible to write a complete module or a complete chain of all modules with one single command execution.

Syntax
bool WriteOutputPorts(int moduleNum, int connectorNum, int portNum, List<UInt32>data, OutputOperators outputOperator, bool triggerTransfer)
WriteOutputPortsBitMask (writes multiple port values at once, all values in one Int)

This is the closest equivalent to the old CmdSetPort() command. It allows setting a complete port or module by specifying a UInt value. A UInt64 value is being used for this to allow setting a complete module as well instead of only a connector.

Syntax
void WriteOutputPortsBitMask(int moduleNum, int connectorNum, int portNum, UInt64 data, int numBits, OutputOperators outputOperator, bool triggerTransfer)
UpdateOutputPorts

This command is required to actually send the data written with WriteOutputPorts() to the Arcaze Modules and its extension modules. The caching has been introduced to avoid unnecessary USB communication. Only changes will be sent and all data will be put together to the lowest possible number of USB transactions automatically.

Therefore the recommended coding style is to write all your required output changes and issue an UpdateOutputPorts() afterwards. This will result in best efficiency on the USB.

Syntax
bool UpdateOutputPorts()
CmdReadQuadratureEncodersAbsolute

This command reads values for all 20 incremental encoder inputs of an Arcaze USB-Interface. It does always return values for all encoder inputs, no matter which of these are actually connected to an encoder and which are not. Each encoder is represented by a 16 bit signed integer.

The values are absolute, they increment infintely and are never reset. They overflow from 32767 to -32786 and vice versa.

Syntax
void CmdReadQuadratureEncodersAbsolute(ref Int16[] EncoderValues)
CmdReadQuadratureEncodersRelative

This command reads values for all 20 incremental encoder inputs of an Arcaze USB-Interface. It does always return values for all encoder inputs, no matter which of these are actually connected to an encoder and which are not. Each encoder is represented by a 16 bit signed integer.

The values are the relative change since the last call of this function. On the first call always 0 is returned for all encoders, because there is no reference value yet.

Syntax
void CmdReadQuadratureEncodersRelative(ref Int16[] EncoderValues)
CmdReadPortsWithHistory

This command is an extended version of CmdReadPorts. It does not only read the current state of the input ports, but also the state of the previous sample and the number of changes that have happened since the last execution of CmdReadPortsWithHistory().

InputData is an array of 40 bytes, one byte for each input port. It has the following format:

  • Bit 0 = Current State
  • Bit 1 = Last State
  • Bits 7…2 = Number of changes since last CMD_READ_PORTS_WITH_HISTORY. This value is reset after this command has been issued

Currently there no input extension modules exist. Therefore, there is no Module number.

Syntax
int CmdReadPortsWithHistory(ref byte[] InputData)
CmdSetPortDirection
Description

The command CmdSetPortDirection is used to set the port direction (input/output) of all pins of one port of the Arcaze USB Interface. Each pin can have a different direction, all pins of one port are set simultaneously.

Syntax
public void CmdSetPortDirection(int port, int direction)
Parameters
ParameterTypeDescription
portintSelects the port on which directions will be set. A list of available port numbers is available here
directionintEach pin of the port is represented by a bit in this value:
  0 = input
  1 = output
Example
private void buttonSetDir_Click(object sender, EventArgs e)
{
  int direction = 0x000F0;   // set pins 5 .. 8 as output
  int port = 1;              // 0: Port A, 1: Port B

  arcazeHid.Command.CmdSetPortDirection(port, direction);
}
CmdSetPort
Description

The command CmdSetPort is used to set output values of the pins of one port of the Arcaze USB Interface.

Syntax
public void CmdSetPort(int port, int value, PortAction action)
Parameters
ParameterTypeDescription
portint Selects the port on which directions will be set. A list of available port numbers is available here
valueint 
actionPortAction Here you select how the port values are being set. The following values are available: PortAction.Set: The absolute value is written directly into the port. 1’s become output high, 0’s become output low. PortAction.On: 1-bits are set, 0-bits are not touched. (this is an OR mask) PortAction.Off: 1-bits are cleared, 0-bits are not touched. (this is an AND mask)
Example
private void PortSet()
{
    arcazeHid.Command.CmdSetPort(0, 0x001, ArcazeCommand.PortAction.Set);    // Port A = 0x001
    arcazeHid.Command.CmdSetPort(0, 0xAAA, ArcazeCommand.PortAction.On);     // Port A = 0xAAB
    arcazeHid.Command.CmdSetPort(0, 0x002, ArcazeCommand.PortAction.Off);    // Port A = 0xAA9
    arcazeHid.Command.CmdSetPort(0, 0x555, ArcazeCommand.PortAction.Set);    // Port A = 0x555
}
CmdReadPort
Description

CmdReadPort is used to read all onboard digital inputs from the Arcaze USB Interface.

Syntax
public int CmdReadPort(int port)
Parameters
ParameterTypeDescription
 port int Selects the port to be read. A list of available port numbers is available here
 return value int The results are returned in a 32 bit integer value. If the read port is narrower than 32 bits, then the port bits are right aligned with the LSB.
Example
private void buttonPortRead_Click(object sender, EventArgs e)
{
  int PortValue = 0;

  PortValue = arcazeHid.Command.CmdReadPort(comboBoxPortReadPort.SelectedIndex);

  // Write value to a TextBox
  textBoxReadPort.Text = PortValue.ToString("X5");

}
CmdSetConfig
Description

The command CmdSetConfig is used to select and activate one of the configurations programmed into the Arcaze USB Interface.

The most common usage example is a MAME frontend GUI that launches multiple emulators, which in turn could expect different keyboard layouts. That would require reprogramming the Arcaze USB each time Interface before launching one of the emulators (inconvenient). Instead, all configurations can be preprogrammed into the Arcaze USB Interface and later be activated alternatively. This command selects and activates one of these configurations.

The Arcaze USB Interface will re-enumerate with the new configuration.

Note that after execution of this command the connection to the Arcaze USB Interface is lost, because of re-enumeration. Therefore you mandatoryly need to reconnect before sending another command!

Syntax
public void CmdSetConfig(int numConfig)
Parameters
ParameterTypeDescription
 numConfigintConfiguration number
Configs are numbered upwards starting from 0.
In the Arcaze Config Tool, the leftmost config is 0, incrementing to the right.
Example
// Select second configuration 
arcazeHid.Command.CmdSetConfig(1);
CmdReadADC
Description

The command CmdReadADC is used to read the most recent values from all ADC (Analog to Digital Converter) channels. There are 6 analog inputs on the module. CmdReadADC returns an array of 6 integers reflecting the 6 analog channels.

Syntax
public int[] CmdReadADC()
Parameters

No parameters required.

Example
private void buttonReadAdc_Click(object sender, EventArgs e)
{
    int[] inputs = arcazeHid.Command.CmdReadADC();

    trackBarAdc0.Value = inputs[0];
    trackBarAdc1.Value = inputs[1];
   
    // ...
   
    trackBarAdc5.Value = inputs[5];
}

„How to create a New Project“ Tutorial

  • Create new Project
  • Copy ArcazeHid.dll to your project
  • Add „ArcazeHid“ to your Project
  • Create an instance of the class ArcazeHid in your main class of your project
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Globalization;
using SimpleSolutions.Usb;        // <- add this using ...

namespace Demo
{
    public partial class Demo : Form
    {
        // Create the instance of ArcazeHid
        private ArcazeHid arcazeHid = new ArcazeHid();    

        // used to save present Arcazes (used later)        
        private List<DeviceInfo> presentArcaze = new List<DeviceInfo>(5);   

        public Demo()
        {
            InitializeComponent();
        }
 ...

    }
}

Add a method to find Arcaze-Devices which are plugged to your PC

private void toolStripButtonFind_Click(object sender, EventArgs e)
{
    List<DeviceInfo> allArcaze = new List<DeviceInfo>(5);

    arcazeHid.Find(allArcaze);

    // Remove all items with the same serialnumber, because 
    // every Arcaze can simulate more then one device. 
    // We only want one item for each "hardware"
   
    presentArcaze = arcazeHid.RemoveSameSerialDevices(allArcaze);  

    // for example, you can write your found Arcazes to a 
    // combobox for user selection

    toolStripComboBoxArcaze.Items.Clear();
    toolStripComboBoxArcaze.Text = "";
    for (int num = 0; num < presentArcaze.Count; num++)
    {
        toolStripComboBoxArcaze.Items.Add
        (
          presentArcaze[num].DeviceName + " (" + 
          presentArcaze[num].Serial + ")"
        );
    }

    // if there is at least one Arcaze you can connect to 
    // the first one
    
    if (presentArcaze.Count > 0)
    {
        toolStripComboBoxArcaze.SelectedIndex = 0;
        arcazeHid.Connect(presentArcaze[0].Path);

        // activate all disabled controls, which only will
        // work if there is a connection to an Arcaze

        updateControls();       
    }
}

Add a method to connect to a selected Arcaze

private void ArcazeChanged(object sender, EventArgs e)
{
    if (arcazeHid.Info.Connected)
        arcazeHid.Disconnect();
 
    arcazeHid.Connect(presentArcaze[toolStripComboBoxArcaze.SelectedIndex].Path);
    updateControls();
}

Add methods to communicate with your Arcaze

// Example: Setting the direction (input / output) of some pins of your Arcaze 
private void buttonSetDir_Click(object sender, EventArgs e)
{
    int value;

    value = int.Parse(textBoxDir.Text, NumberStyles.HexNumber);
    arcazeHid.Command.CmdSetPortDirection(comboBoxPortDir.SelectedIndex, value);
}
  • Add a method to close the connection to your Arcaze. It should be called when you close your program
private void OnClosed(object sender, FormClosedEventArgs e)
{ 
    if (arcazeHid.Info.Connected)
        arcazeHid.Disconnect();
}

Example Code

A simple example application demonstrating most functions of the Arcaze-USB SDK is available with source code for your reference.

It demonstrates:

  • Setting and reading complete ports (topmost row)
  • Setting and reading single pins (second row)
  • Reading Analog input (third row)
  • Resetting USB Interface (Reset button)

Usage

  1. Connect Arcaze USB Interface
  2. Click „Find“ Button
  3. Dropdown Box should be filled with all connected Arcaze USB Interfaces
  4. Select one of the interfaces
  5. Click Read Analog Input multiple times
  6. You see that the measured value varies a little, because it’s not terminated. If you touch the analog input pin on the analog connector it will vary a lot.
  7. Set port direction
    1. Select desired port A or B
    2. Enter value fffff to make all pins outputs
    3. Click „Set Port Direction“
    4. All LEDs should turn off
  8. Set port value
    1. Select desired port A or B (same as above)
    2. Select Action „Set“
    3. Enter value aaaaa to set and clear every second output bit
    4. Click Set
    5. If you have LEDs connected to your outputs you should see every second LED lit
  9. Read Port
    1. Click Port Read Button
    2. Values should appear in Port A/B textboxes
    3. Note that this reflects actual port pin voltages and can for outputs can differ from what you wrote into the output registers
  10. Do the same for single pins, this works similar to complete ports
  11. Click reset
    1. The Arcaze USB-Interface will re-enumerate and therefore disconnect from USB and reconnect a second later
    2. Now you may not send any more commands, because the connection is lost.
    3. You must select the device from the Devices drop-down menu once again, to trigger reconnection
    4. The device is reconnected
    5. Now you can send more commands again.
    6. If you ignore this, the application will crash, because the Exception from communication with a non-connected device is not caught in this simple demo.
    7. Later you can react on USB connect/disconnect events, which are also supported by this SDK. Documentation for the event features willl be added soon.

Building Instructions

The application is written in C# has been created with Microsoft Visual Studio Standard 2005. It can be opened and compiled with any Express version as well. .Net 2.0 or higher is required on the development machine as well as on the target.