/*

Modified

*/



/***************************************************************************************/



//Author: LabJack
//June 25, 2009
//Example UE9 helper functions.  Function descriptions are in ue9.h.

#include "ue9.h"


ue9CalibrationInfo UE9_CALIBRATION_INFO_DEFAULT = {
    9,
    //Nominal Values
    {   0.000077503,
        -0.012,
        0.000038736,
        -0.012,
        0.000019353,
        -0.012,
        0.0000096764,
        -0.012,
        0.00015629,
        -5.176,
        842.59,
        0.0,
        842.259,
        0.0,
        0.012968,
        0.012968,
        298.15,
        2.43,
        0.0,
        1.215,
        0.00009272,
        0.000077503,
        -0.012,
        0.00015629,
        -5.176}
};


void normalChecksum(uint8 *b, int n)
{
    b[0] = normalChecksum8(b,n);
}


void extendedChecksum(uint8 *b, int n)
{
    uint16 a;

    a = extendedChecksum16(b,n);
    b[4] = (uint8)(a & 0xff);
    b[5] = (uint8)((a/256) & 0xff);
    b[0] = extendedChecksum8(b);
}


uint8 normalChecksum8(uint8 *b, int n)
{
    int i;
    uint16 a, bb;

    //Sums bytes 1 to n-1 unsigned to a 2 byte value. Sums quotient and
    //remainder of 256 division.  Again, sums quotient and remainder of
    //256 division.
    for (i = 1, a = 0; i < n; i++) 
        a += (uint16)b[i];

    bb = a/256;
    a = (a-256*bb)+bb;
    bb = a/256;

    return (uint8)((a-256*bb)+bb);
}


uint16 extendedChecksum16(uint8 *b, int n)
{
    int i, a = 0;

    //Sums bytes 6 to n-1 to a unsigned 2 byte value
    for (i = 6; i < n; i++)
        a += (uint16)b[i];

    return a;
}


uint8 extendedChecksum8(uint8 *b)
{
    int i, a, bb;

    //Sums bytes 1 to 5. Sums quotient and remainder of 256 division. Again, sums 
    //quotient and remainder of 256 division.
    for (i = 1, a = 0; i < 6; i++)
        a+=(uint16)b[i];

    bb=a/256;
    a=(a-256*bb)+bb;
    bb=a/256;

    return (uint8)((a-256*bb)+bb);
}


HANDLE openUSBConnection(int localID)
{
    BYTE buffer[38];
    uint16 checksumTotal = 0;
    HANDLE hDevice = 0;
    uint32 numDevices = 0;
    uint32 dev;
    int i;

    numDevices = LJUSB_GetDevCount(UE9_PRODUCT_ID);
    if (numDevices == 0) 
    {
        printf("Open error: No UE9 devices could be found\n");
        return NULL;
    }

    for (dev = 1;  dev <= numDevices; dev++) 
    {
        hDevice = LJUSB_OpenDevice(dev, 0, UE9_PRODUCT_ID);
        if (hDevice != NULL)
        {
            if (localID < 0)
            {
                return hDevice;
            }
            else
            {
                checksumTotal = 0;

                //setting up a CommConfig command
                buffer[1] = (BYTE)(0x78);
                buffer[2] = (BYTE)(0x10);
                buffer[3] = (BYTE)(0x01);

                for (i = 6; i < 38; i++)
                    buffer[i] = (BYTE)(0x00);

                extendedChecksum(buffer,38);

                if (LJUSB_BulkWrite(hDevice, UE9_PIPE_EP1_OUT, buffer, 38) != 38)
                    goto locid_error;

                for (i = 0; i < 38; i++)
                    buffer[i] = 0;

                if (LJUSB_BulkRead(hDevice, UE9_PIPE_EP1_IN, buffer, 38) != 38)
                    goto locid_error;

                checksumTotal = extendedChecksum16(buffer, 38);
                if ( (BYTE)((checksumTotal / 256) & 0xff) != buffer[5])
                    goto locid_error;

                if ( (BYTE)(checksumTotal & 0xff) != buffer[4])
                    goto locid_error;

                if ( extendedChecksum8(buffer) != buffer[0])
                    goto locid_error;

                if ( buffer[1] != (BYTE)(0x78) || buffer[2] != (BYTE)(0x10) || buffer[3] != (BYTE)(0x01) )
                    goto locid_error;

                if ( (int)buffer[8] == localID)
                    return hDevice;
                else
                    LJUSB_CloseDevice(hDevice);
            } //else localID >= 0 end
        }  //if hDevice != NULL end
    } //for end

    printf("Open error: could not find a UE9 with a local ID of %d\n", localID);
    return NULL;

locid_error:
    printf("Open error: problem when checking local ID\n");
    return NULL;
}


void closeUSBConnection(HANDLE hDevice) 
{
    LJUSB_CloseDevice(hDevice);
}


long getTickCount()
{
    struct timeval tv;

    gettimeofday(&tv, NULL);

    return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
}


long getCalibrationInfo(HANDLE hDevice, ue9CalibrationInfo *caliInfo)
{
    uint8 sendBuffer[8];
    uint8 recBuffer[136];
    int sentRec = 0, i = 0, j = 0, ccTotal = 0, count = 0;

    /* Setting up command */
    sendBuffer[1] = (uint8)(0xF8);  //command byte
    sendBuffer[2] = (uint8)(0x01);  //number of data words
    sendBuffer[3] = (uint8)(0x2A);  //extended command number
    sendBuffer[6] = (uint8)(0x00);

    for (i = 0; i < 5; i ++)
    {
        /* reading block 1 from memory */
        sendBuffer[7] = (uint8)i;    //Blocknum = i
        extendedChecksum(sendBuffer, 8);

        sentRec = LJUSB_BulkWrite(hDevice, UE9_PIPE_EP1_OUT, sendBuffer, 8);
        if (sentRec < 8)
        {
            if (sentRec == 0)
                printf("Error : getCalibrationInfo write failed\n");
            else
                printf("Error : getCalibrationInfo did not write all of the buffer\n");
            return -1;
        }

        sentRec = LJUSB_BulkRead(hDevice, UE9_PIPE_EP1_IN, recBuffer, 136);
        if (sentRec < 136)
        {
            if (sentRec == 0)
                printf("Error : getCalibrationInfo read failed\n");
            else  
                printf("Error : getCalibrationInfo did not read all of the buffer\n");
        }

        if (recBuffer[1] != (uint8)(0xF8) || recBuffer[2] != (uint8)(0x41) || recBuffer[3] != (uint8)(0x2A))
        {
            printf("Error : received buffer at byte 1, 2, or 3 are not 0xA3, 0x01, 0x2A \n");
            return -1;
        }

        //Reading out calbration constants
        if (i == 0)
            ccTotal = 8;
        if (i == 1)
            ccTotal = 2;
        if (i == 2)
            ccTotal = 13;
        if (i == 3)
            ccTotal = 2;
        if (i == 4)
            ccTotal = 2;

        for (j = 0; j < ccTotal; j++)
        {
            if (i != 2 || (i == 2 && j != 5 && j != 7) )
            {
                //block data starts on byte 8 of the buffer
                caliInfo->ccConstants[count] = FPuint8ArrayToFPDouble(recBuffer + 8, j*8);
                count++;
            }
        }
    }

    caliInfo->prodID = 9;

    return 0;
}

long getTdacCalibrationInfo(HANDLE hDevice, ue9TdacCalibrationInfo *caliInfo, uint8 DIOAPinNum)
{
    int err;
    uint8 options, speedAdjust, sdaPinNum, sclPinNum, address, numByteToSend, numBytesToReceive, errorcode;
    uint8 bytesCommand[1];
    uint8 bytesResponse[32];
    uint8 ackArray[4];

    err = 0;

    //Setting up I2C command for LJTDAC
    options = 0;               //I2COptions : 0
    speedAdjust = 0;           //SpeedAdjust : 0 (for max communication speed of about 130 kHz)
    sdaPinNum = DIOAPinNum+1;  //SDAPinNum : FIO channel connected to pin DIOB
    sclPinNum = DIOAPinNum;    //SCLPinNum : FIO channel connected to pin DIOA
    address = (uint8)(0xA0);   //Address : h0xA0 is the address for EEPROM
    numByteToSend = 1;         //NumI2CByteToSend : 1 byte for the EEPROM address
    numBytesToReceive = 32;    //NumI2CBytesToReceive : getting 32 bytes starting at EEPROM address specified in I2CByte0

    bytesCommand[0] = 64;       //I2CByte0 : Memory Address (starting at address 64 (DACA Slope)

    //Performing I2C low-level call
    err = I2C(hDevice, options, speedAdjust, sdaPinNum, sclPinNum, address, numByteToSend, numBytesToReceive, bytesCommand, &errorcode, ackArray, bytesResponse);

    if (errorcode != 0)
    {
        printf("Getting LJTDAC calibration info error : received errorcode %d in response\n", errorcode);
        err = -1;
    }

    if (err == -1)
        return err;

    caliInfo->ccConstants[0] = FPuint8ArrayToFPDouble(bytesResponse, 0);
    caliInfo->ccConstants[1] = FPuint8ArrayToFPDouble(bytesResponse, 8);
    caliInfo->ccConstants[2] = FPuint8ArrayToFPDouble(bytesResponse, 16);
    caliInfo->ccConstants[3] = FPuint8ArrayToFPDouble(bytesResponse, 24);
    caliInfo->prodID = 9;

    return err;
}


double FPuint8ArrayToFPDouble(uint8 *buffer, int startIndex) 
{ 
    uint32 resultDec = 0;
    uint32 resultWh = 0;
    int i;

    for (i = 0; i < 4; i++)
    {
        resultDec += (uint32)buffer[startIndex + i] * pow(2, (i*8));
        resultWh += (uint32)buffer[startIndex + i + 4] * pow(2, (i*8));
    }

    return ( (double)((int)resultWh) + (double)(resultDec)/4294967296.0 );
}


long isCalibrationInfoValid(ue9CalibrationInfo *caliInfo)
{
    if (caliInfo == NULL)
        goto invalid;
    if (caliInfo->prodID != 9)
        goto invalid;
    return 1;
invalid:
  printf("Error: Invalid calibration info.\n");
  return -1;
}


long isTdacCalibrationInfoValid(ue9TdacCalibrationInfo *caliInfo)
{
    if (caliInfo == NULL)
        goto invalid;
    if (caliInfo->prodID != 9)
        goto invalid;
    return 1;
invalid:
    printf("Error: Invalid LJTDAC calibration info.\n");
    return -1;
}


long getAinVoltCalibrated(ue9CalibrationInfo *caliInfo, uint8 gainBip, uint8 resolution, uint16 bytesVolt, double *analogVolt)
{
    if (isCalibrationInfoValid(caliInfo) == -1)
        return -1;

    if (resolution < 18)
    {
        if ((gainBip >= 0 && gainBip <= 3) || gainBip == 8)
        {
            if (gainBip == 8)
                gainBip = 4;  //setting this for index purposes
            *analogVolt = (caliInfo->ccConstants[gainBip*2]*bytesVolt) + caliInfo->ccConstants[gainBip*2 + 1];
            return 0;
        }
        else
            goto invalidGainBip;
    }
    else  //UE9 Pro high res
    {
        if (gainBip == 0 || gainBip == 8)
        {
            if (gainBip == 8)
                gainBip = 1;  //setting this for index purposes
            *analogVolt = (caliInfo->ccConstants[gainBip*2 + 21]*bytesVolt) + caliInfo->ccConstants[gainBip*2 + 22];
            return 0;
        }
        else
            goto invalidGainBip;
    }

invalidGainBip:
    printf("getAinVoltCalibrated error: invalid GainBip.\n");
    return -1;
}


long getDacBinVoltCalibrated(ue9CalibrationInfo *caliInfo, int dacNumber, double analogVolt, uint16 *bytesVolt)
{
    double tBytesVolt;

    if (isCalibrationInfoValid(caliInfo) == -1)
        return -1;

    if (dacNumber < 0 || dacNumber > 2)
    {
        printf("getDacBinVoltCalibrated error: invalid channelNumber.\n");
        return -1;
    }

    tBytesVolt = analogVolt*caliInfo->ccConstants[10 + dacNumber*2] +   caliInfo->ccConstants[11 + dacNumber*2];

    //Checking to make sure bytesVoltage will be a value between 0 and 4095, or 
    //that a uint16 overflow does not occur.  A too high analogVoltage (above 5 
    //volts) or too low analogVoltage (below 0 volts) will cause a value not 
    //between 0 and 4095.
    if (tBytesVolt < 0)
        tBytesVolt = 0;
    if (tBytesVolt > 4095)  
        tBytesVolt = 4095;

    *bytesVolt = (uint16)tBytesVolt; 

    return 0;
}


long getTdacBinVoltCalibrated(ue9TdacCalibrationInfo *caliInfo, int dacNumber, double analogVolt, uint16 *bytesVolt)
{
    uint32 tBytesVolt;

    if (isTdacCalibrationInfoValid(caliInfo) == -1)
        return -1;

    if (dacNumber < 0 || dacNumber > 2)
    {
        printf("getTdacBinVoltCalibrated error: invalid channelNumber.\n");
        return -1;
    }

    tBytesVolt = analogVolt*caliInfo->ccConstants[dacNumber*2] + caliInfo->ccConstants[dacNumber*2 + 1];

    //Checking to make sure bytesVolt will be a value between 0 and 65535.
    if (tBytesVolt < 0)
        tBytesVolt = 0;
    if (tBytesVolt > 65535)
        tBytesVolt = 65535;

    *bytesVolt = (uint16)tBytesVolt;

    return 0;  
}


long getTempKCalibrated(ue9CalibrationInfo *caliInfo, int powerLevel, uint16 bytesTemp, double *kelvinTemp)
{
    if (isCalibrationInfoValid(caliInfo) == -1)
        return -1;

    if (powerLevel == 0 || powerLevel == 1)
    {
        *kelvinTemp = caliInfo->ccConstants[14 + powerLevel]*bytesTemp;
        return 0;
    }
    else
    {
        printf("getTempKCalibrated error: invalid powerLevel.\n");
        return -1;
    }
}


long getAinVoltUncalibrated(uint8 gainBip, uint8 resolution, uint16 bytesVolt, double *analogVolt)
{
    return getAinVoltCalibrated(&UE9_CALIBRATION_INFO_DEFAULT, gainBip, resolution, bytesVolt, analogVolt);
}

long getDacBinVoltUncalibrated(int dacNumber, double analogVolt, uint16 *bytesVolt)
{
    return getDacBinVoltCalibrated(&UE9_CALIBRATION_INFO_DEFAULT, dacNumber, analogVolt, bytesVolt);
}

long getTempKUncalibrated(int powerLevel, uint16 bytesTemp, double *kelvinTemp)
{
    return getTempKCalibrated(&UE9_CALIBRATION_INFO_DEFAULT, powerLevel, bytesTemp, kelvinTemp);
}


long I2C(HANDLE hDevice, uint8 I2COptions, uint8 SpeedAdjust, uint8 SDAPinNum, uint8 SCLPinNum, uint8 Address, uint8 NumI2CBytesToSend, uint8 NumI2CBytesToReceive, uint8 *I2CBytesCommand, uint8 *Errorcode, uint8 *AckArray, uint8 *I2CBytesResponse)
{
    uint8 *sendBuff, *recBuff;
    int sendChars, recChars, sendSize, recSize, i, ret;
    uint16 checksumTotal = 0;
    uint32 ackArrayTotal, expectedAckArray;

    *Errorcode = 0;
    ret = 0;
    sendSize = 6 + 8 + ((NumI2CBytesToSend%2 != 0)?(NumI2CBytesToSend + 1):(NumI2CBytesToSend));
    recSize = 6 + 6 + ((NumI2CBytesToReceive%2 != 0)?(NumI2CBytesToReceive + 1):(NumI2CBytesToReceive));

    sendBuff = (uint8*)malloc(sizeof(uint8)*sendSize);
    recBuff = (uint8*)malloc(sizeof(uint8)*recSize);

    sendBuff[sendSize - 1] = 0;

    //I2C command
    sendBuff[1] = (uint8)(0xF8);          //command byte
    sendBuff[2] = (sendSize - 6)/2;       //number of data words = 4 + NumI2CBytesToSend
    sendBuff[3] = (uint8)(0x3B);          //extended command number

    sendBuff[6] = I2COptions;             //I2COptions
    sendBuff[7] = SpeedAdjust;            //SpeedAdjust
    sendBuff[8] = SDAPinNum;              //SDAPinNum
    sendBuff[9] = SCLPinNum;              //SCLPinNum
    sendBuff[10] = Address;               //Address
    sendBuff[11] = 0;                     //Reserved
    sendBuff[12] = NumI2CBytesToSend;     //NumI2CByteToSend
    sendBuff[13] = NumI2CBytesToReceive;  //NumI2CBytesToReceive

    for (i = 0; i < NumI2CBytesToSend; i++)
        sendBuff[14 + i] = I2CBytesCommand[i];  //I2CByte

    extendedChecksum(sendBuff, sendSize);

    //Sending command to UE9
    sendChars = LJUSB_BulkWrite(hDevice, UE9_PIPE_EP1_OUT, sendBuff, sendSize);
    if (sendChars < sendSize)
    {
        if (sendChars == 0)
            printf("I2C Error : write failed\n");
        else
            printf("I2C Error : did not write all of the buffer\n");
        ret = -1;
        goto cleanmem;
    }

    //Reading response from UE9
    recChars = LJUSB_BulkRead(hDevice, UE9_PIPE_EP1_IN, recBuff, recSize);
    if (recChars < recSize)
    {
        if (recChars == 0)
            printf("I2C Error : read failed\n");
        else
        {
            printf("I2C Error : did not read all of the buffer\n");
            if (recChars >= 12)
                *Errorcode = recBuff[6];
        }
        ret = -1;
        goto cleanmem;
    }

    *Errorcode = recBuff[6];

    AckArray[0] = recBuff[8];
    AckArray[1] = recBuff[9];
    AckArray[2] = recBuff[10];
    AckArray[3] = recBuff[11];

    for (i = 0; i < NumI2CBytesToReceive; i++)
        I2CBytesResponse[i] = recBuff[12 + i];

    if ((uint8)(extendedChecksum8(recBuff)) != recBuff[0])
    {
        printf("I2C Error : read buffer has bad checksum (%d)\n", recBuff[0]);
        ret = -1;
    }

    if (recBuff[1] != (uint8)(0xF8))
    {
        printf("I2C Error : read buffer has incorrect command byte (%d)\n", recBuff[1]);
        ret = -1;
    }

    if (recBuff[2] != (uint8)((recSize - 6)/2))
    {
        printf("I2C Error : read buffer has incorrect number of data words (%d)\n", recBuff[2]);
        ret = -1;
    }

    if (recBuff[3] != (uint8)(0x3B))
    {
        printf("I2C Error : read buffer has incorrect extended command number (%d)\n", recBuff[3]);
        ret = -1;
    }

    checksumTotal = extendedChecksum16(recBuff, recSize);
    if ( (uint8)((checksumTotal / 256) & 0xff) != recBuff[5] || (uint8)(checksumTotal & 255) != recBuff[4])
    {
        printf("I2C error : read buffer has bad checksum16 (%d)\n", checksumTotal);
        ret = -1;
    }

    //ackArray should ack the Address byte in the first ack bit, but did not until control firmware 1.84
    ackArrayTotal = AckArray[0] + AckArray[1]*256 + AckArray[2]*65536 + AckArray[3]*16777216;
    expectedAckArray = pow(2.0,  NumI2CBytesToSend+1)-1;
    if (ackArrayTotal != expectedAckArray)
    printf("I2C error : expected an ack of %u, but received %u\n", expectedAckArray, ackArrayTotal);

cleanmem:
    free(sendBuff);
    free(recBuff);
    sendBuff = NULL;
    recBuff = NULL;

    return ret;
}


long eAIN(HANDLE Handle, ue9CalibrationInfo *CalibrationInfo, long ChannelP, long ChannelN, double *Voltage, long Range, long Resolution, long Settling, long Binary, long Reserved1, long Reserved2)
{
    uint8 IOType, Channel, AINM, AINH, ainGain;
    uint16 bytesVT;

    if (isCalibrationInfoValid(CalibrationInfo) != 1)
    {
        printf("eAIN error: calibration information is required");
        return -1;
    }

    if (Range == LJ_rgBIP5V)
        ainGain = 8;
    else if (Range == LJ_rgUNI5V)
        ainGain = 0;
    else if (Range == LJ_rgUNI2P5V)
        ainGain = 1;
    else if (Range == LJ_rgUNI1P25V)
        ainGain = 2;
    else if (Range == LJ_rgUNIP625V)
        ainGain = 3;
    else
    {
        printf("eAIN error: Invalid Range\n");
        return -1;
    }

    if (ehSingleIO(Handle, 4, (uint8)ChannelP, ainGain, (uint8)Resolution, (uint8)Settling, &IOType, &Channel, NULL, &AINM, &AINH) < 0)
        return -1;

    bytesVT = AINM + AINH*256;

    if (Binary != 0)
    {
        *Voltage = (double)bytesVT;
    }
    else
    {
        if (ChannelP == 133 || ChannelP == 141)
        {
            if (getTempKCalibrated(CalibrationInfo, 0, bytesVT, Voltage) < 0)
                return -1;
        }
        else
        {
            if (getAinVoltCalibrated(CalibrationInfo, ainGain, (uint8)Resolution, bytesVT, Voltage) < 0)
                return -1;
        }
    }

    return 0;
}


long eDAC(HANDLE Handle, ue9CalibrationInfo *CalibrationInfo, long Channel, double Voltage, long Binary, long Reserved1, long Reserved2)
{
    uint8 IOType, channel;
    uint16 bytesVoltage;

    if (isCalibrationInfoValid(CalibrationInfo) != 1)
    {
        printf("eDAC error: calibration information is required");
        return -1;
    }

    if (getDacBinVoltCalibrated(CalibrationInfo, (uint8)Channel, Voltage, &bytesVoltage) < 0) 
        return -1;

    return ehSingleIO(Handle, 5, (uint8)Channel, (uint8)( bytesVoltage & (0x00FF) ), (uint8)(( bytesVoltage /256 ) + 192), 0, &IOType, &channel, NULL, NULL, NULL);
}


long eDI(HANDLE Handle, long Channel, long *State)
{
    uint8 state;

    if (Channel > 22)
    {
        printf("eDI error: Invalid Channel");
        return -1;
    }

    if (ehDIO_Feedback(Handle, (uint8)Channel, 0, &state) < 0)
        return -1;
    *State = state;

    return 0;
}


long eDO(HANDLE Handle, long Channel, long State)
{
    uint8 state;

    state = (uint8)State;
    if (Channel > 22)
    {
        printf("Error: Invalid Channel");
        return -1;
    }

    return ehDIO_Feedback(Handle, (uint8)Channel, 1, &state);
}


long eTCConfig(HANDLE Handle, long *aEnableTimers, long *aEnableCounters, long TCPinOffset, long TimerClockBaseIndex, long TimerClockDivisor, long *aTimerModes, double *aTimerValues, long Reserved1, long Reserved2)
{
    uint8 enableMask;
    uint8 timerMode[6], counterMode[2];
    uint16 timerValue[6];
    int numTimers, numTimersStop, i;

    //Setting EnableMask
    enableMask = 128;  //Bit 7: UpdateConfig

    if (aEnableCounters[1] != 0)
    enableMask += 16; //Bit 4: Enable Counter1

    if (aEnableCounters[0] |= 0)
    enableMask += 8;  //Bit 3: Enable Counter0

    numTimers = 0;
    numTimersStop = 0;

    for (i = 0; i < 6; i++)
    {
        if (aEnableTimers[i] != 0 && numTimersStop == 0)
        {
            numTimers++;
            timerMode[i] = (uint8)aTimerModes[i];     //TimerMode
            timerValue[i] = (uint16)aTimerValues[i];  //TimerValue
        }
        else
        {
            numTimersStop = 1;
            timerMode[i] = 0;
            timerValue[i] = 0;
        }
    }
    enableMask += numTimers;  //Bits 2-0: Number of Timers

    counterMode[0] = 0;  //Counter0Mode
    counterMode[1] = 0;  //Counter1Mode

    return ehTimerCounter(Handle, (uint8)TimerClockDivisor, enableMask, (uint8)TimerClockBaseIndex, 0, timerMode, timerValue, counterMode, NULL, NULL);
}


long eTCValues(HANDLE Handle, long *aReadTimers, long *aUpdateResetTimers, long *aReadCounters, long *aResetCounters, double *aTimerValues, double *aCounterValues, long Reserved1, long Reserved2)
{
    uint8 updateReset, timerMode[6], counterMode[2];
    uint16 timerValue[6];
    uint32 timer[6], counter[2];
    int i;
    long errorcode;

    //UpdateReset
    updateReset = 0;
    for (i = 0; i < 6; i++)
    {
        updateReset += ((aUpdateResetTimers[i] != 0) ? pow(2, i) : 0);
        timerMode[i] = 0;
        timerValue[i] = 0;
    }
    for (i = 0; i < 2; i++)
    {
        updateReset += ((aResetCounters[i] != 0) ? pow(2, 6 + i) : 0);
        counterMode[i] = 0;
    }

    if ( (errorcode = ehTimerCounter(Handle, 0, 0, 0, updateReset, timerMode, timerValue, counterMode, timer, counter)) != 0)
        return errorcode;

    for (i = 0; i < 6; i++)
        aTimerValues[i] = timer[i];

    for (i = 0; i < 2; i++)
        aCounterValues[i] = counter[i];

    return 0;
}


long ehSingleIO(HANDLE hDevice, uint8 inIOType, uint8 inChannel, uint8 inDirBipGainDACL, uint8 inStateResDACH, uint8 inSettlingTime, uint8 *outIOType, uint8 *outChannel, uint8 *outDirAINL, uint8 *outStateAINM, uint8 *outAINH)
{
    uint8 sendBuff[8], recBuff[8];
    int sendChars, recChars;

    sendBuff[1] = (uint8)(0xA3);    //command byte
    sendBuff[2] = inIOType;         //IOType
    sendBuff[3] = inChannel;        //Channel
    sendBuff[4] = inDirBipGainDACL; //Dir/BipGain/DACL
    sendBuff[5] = inStateResDACH;   //State/Resolution/DACH
    sendBuff[6] = inSettlingTime;  //Settling time
    sendBuff[7] = 0;                //Reserved
    sendBuff[0] = normalChecksum8(sendBuff, 8);

    //Sending command to UE9
    sendChars = LJUSB_BulkWrite(hDevice, UE9_PIPE_EP1_OUT, sendBuff, 8);
    if (sendChars < 8)
    {
        if (sendChars == 0)
            printf("SingleIO error : write failed\n");
        else
            printf("SingleIO error : did not write all of the buffer\n");
        return -1;
    }

    //Reading response from UE9
    recChars = LJUSB_BulkRead(hDevice, UE9_PIPE_EP1_IN, recBuff, 8);
    if (recChars < 8)
    {
        if (recChars == 0)
            printf("SingleIO error : read failed\n");
        else
            printf("SingleIO error : did not read all of the buffer\n");
        return -1;
    }

    if ((uint8)(normalChecksum8(recBuff, 8)) != recBuff[0])
    {
        printf("SingleIO error : read buffer has bad checksum\n");
        return -1;
    }

    if (recBuff[1] != (uint8)(0xA3))
    {
        printf("SingleIO error : read buffer has wrong command byte\n");
        return -1;
    }

    if (outIOType != NULL)
        *outIOType = recBuff[2];
    if (outChannel != NULL)
        *outChannel = recBuff[3];
    if (outDirAINL != NULL)
        *outDirAINL = recBuff[4];
    if (outStateAINM != NULL)
        *outStateAINM = recBuff[5];
    if (outAINH != NULL)
        *outAINH = recBuff[6];

    return 0;
}


long ehDIO_Feedback(HANDLE hDevice, uint8 channel, uint8 direction, uint8 *state)
{
    uint8 sendBuff[34], recBuff[64];
    int sendChars, recChars;
    int i;
    uint8 tempDir, tempState, tempByte;
    uint16 checksumTotal;

    sendBuff[1] = (uint8)(0xF8);  //command byte
    sendBuff[2] = (uint8)(0x0E);  //number of data words
    sendBuff[3] = (uint8)(0x00);  //extended command number

    for (i = 6; i < 34; i++)
    sendBuff[i] = 0;

    tempDir = ((direction < 1) ? 0 : 1);
    tempState = ((*state < 1) ? 0 : 1);

    if (channel <=  7)
    {
        tempByte = pow(2, channel);
        sendBuff[6] = tempByte;
        if (tempDir)
            sendBuff[7] = tempByte;
        if (tempState)
            sendBuff[8] = tempByte;
    }
    else if (channel <= 15)
    {
        tempByte = pow(2, (channel - 8));
        sendBuff[9] = tempByte;
        if (tempDir)
            sendBuff[10] = tempByte;
        if (tempState)
            sendBuff[11] = tempByte;
    }
    else if (channel <= 19)
    {
        tempByte = pow(2, (channel - 16));
        sendBuff[12] = tempByte;
        if (tempDir)
            sendBuff[13] = tempByte*16;
        if (tempState)
            sendBuff[13] += tempByte;
    }
    else if (channel <= 22)
    {
        tempByte = pow(2, (channel - 20));
        sendBuff[14] = tempByte;
        if (tempDir)
            sendBuff[15] = tempByte*16;
        if (tempState)
            sendBuff[15] += tempByte;
    }
    else
    {
        printf("DIO Feedback error: Invalid Channel\n");
        return -1;
    }

    extendedChecksum(sendBuff, 34);

    //Sending command to UE9
    sendChars = LJUSB_BulkWrite(hDevice, UE9_PIPE_EP1_OUT, sendBuff, 34);
    if (sendChars < 34)
    {
        if (sendChars == 0)
            printf("DIO Feedback error : write failed\n");
        else  
            printf("DIO Feedback error : did not write all of the buffer\n");
        return -1;
    }

    //Reading response from UE9
    recChars = LJUSB_BulkRead(hDevice, UE9_PIPE_EP1_IN, recBuff, 64);
    if (recChars < 64)
    {
        if (recChars == 0)
            printf("DIO Feedback error : read failed\n");
        else  
            printf("DIO Feedback error : did not read all of the buffer\n");
        return -1;
    }

    checksumTotal = extendedChecksum16(recBuff, 64);
    if ( (uint8)((checksumTotal / 256) & 0xff) != recBuff[5])
    {
        printf("DIO Feedback error : read buffer has bad checksum16(MSB)\n");
        return -1;
    }

    if ( (uint8)(checksumTotal & 0xff) != recBuff[4])
    {
        printf("DIO Feedback error : read buffer has bad checksum16(LBS)\n");
        return -1;
    }

    if ( extendedChecksum8(recBuff) != recBuff[0])
    {
        printf("DIO Feedback error : read buffer has bad checksum8\n");
        return -1;
    }

    if (recBuff[1] != (uint8)(0xF8) || recBuff[2] != (uint8)(0x1D) || recBuff[3] != (uint8)(0x00))
    {
        printf("DIO Feedback error : read buffer has wrong command bytes \n");
        return -1;
    }

    if (channel <=  7)
        *state = ((recBuff[7]&tempByte) ? 1 : 0);
    else if (channel <= 15)
        *state = ((recBuff[9]&tempByte) ? 1 : 0);
    else if (channel <= 19)
        *state = ((recBuff[10]&tempByte) ? 1 : 0);
    else if (channel <= 22)
        *state = ((recBuff[11]&tempByte) ? 1 : 0);

    return 0;
}


long ehTimerCounter(HANDLE hDevice, uint8 inTimerClockDivisor, uint8 inEnableMask, uint8 inTimerClockBase, uint8 inUpdateReset, uint8 *inTimerMode, uint16 *inTimerValue, uint8 *inCounterMode, uint32 *outTimer, uint32 *outCounter)
{
    uint8 sendBuff[30], recBuff[40];
    int sendChars, recChars, i, j;
    uint16 checksumTotal;

    sendBuff[1] = (uint8)(0xF8);  //command byte
    sendBuff[2] = (uint8)(0x0C);  //number of data words
    sendBuff[3] = (uint8)(0x18);  //extended command number

    sendBuff[6] = inTimerClockDivisor;  //TimerClockDivisor
    sendBuff[7] = inEnableMask;  //EnableMask
    sendBuff[8] = inTimerClockBase;  //TimerClockBase

    sendBuff[9] = inUpdateReset;  //UpdateReset

    for (i = 0; i < 6; i++)
    {
        sendBuff[10 + i*3] = inTimerMode[i];  //TimerMode
        sendBuff[11 + i*3] = (uint8)(inTimerValue[i]&0x00FF);  //TimerValue (low byte)
        sendBuff[12 + i*3] = (uint8)((inTimerValue[i]&0xFF00)/256);  //TimerValue (high byte)
    }

    for (i = 0; i < 2; i++)
        sendBuff[28 + i] = inCounterMode[i];  //CounterMode

    extendedChecksum(sendBuff, 30);

    //Sending command to UE9
    sendChars = LJUSB_BulkWrite(hDevice, UE9_PIPE_EP1_OUT, sendBuff, 30);
    if (sendChars < 30)
    {
        if (sendChars == 0)
            printf("ehTimerCounter error : write failed\n");
        else
            printf("ehTimerCounter error : did not write all of the buffer\n");
        return -1;
    }

    //Reading response from UE9
    recChars = LJUSB_BulkRead(hDevice, UE9_PIPE_EP1_IN, recBuff, 40);
    if (recChars < 40)
    {
        if (recChars == 0)
            printf("ehTimerCounter error : read failed\n");
        else
            printf("ehTimerCounter error : did not read all of the buffer\n");
        return -1;
    }

    checksumTotal = extendedChecksum16(recBuff, 40);
    if ( (uint8)((checksumTotal / 256) & 0xff) != recBuff[5])
    {
        printf("ehTimerCounter error : read buffer has bad checksum16(MSB)\n");
        return -1;
    }

    if ( (uint8)(checksumTotal & 0xff) != recBuff[4])
    {
        printf("ehTimerCounter error : read buffer has bad checksum16(LBS)\n");
        return -1;
    }

    if ( extendedChecksum8(recBuff) != recBuff[0])
    {
        printf("ehTimerCounter error : read buffer has bad checksum8\n");
        return -1;
    }

    if (recBuff[1] != (uint8)(0xF8) || recBuff[2] != (uint8)(0x11) || recBuff[3] != (uint8)(0x18))
    {
        printf("ehTimerCounter error : read buffer has wrong command bytes for TimerCounter\n");
        return -1;
    }

    if (outTimer != NULL)
    {
        for (i = 0; i < 6; i++)
        {
            outTimer[i] = 0;
            for (j = 0; j < 4; j++)
                outTimer[i] += recBuff[8 + j + i*4]*pow(2, 8*j);
        }
    }

    if (outCounter != NULL)
    {
        for (i = 0; i < 2; i++)
        {
            outCounter[i] = 0;
            for (j = 0; j < 4; j++)
                outCounter[i] += recBuff[32 + j + i*4]*pow(2, 8*j);
        }
    }

    return recBuff[6];
}
